템플릿 언더바는 View단에 백엔드 로직을 두는 것을 막아주고, 데이터를 간단한 문법으로 View단에 표출할 수 있도록 도와주는 템플릿 엔진 입니다.
안녕하세요, 라이언 입니다!
픽스팀의 기술블로그는 처음으로 작성하는데요.
PHP에 같이 사용가능한 템플릿엔진인 “템플릿 언더바”에서 자주사용되는 문법을 정리하려고 합니다.
템플릿 언더바 (Template Underbar)
사실 PHP는 그 자체의 언어로도 템플릿엔진이 이미 존재합니다.
하지만, 개발자가 실수할 수 있는 부분이 있는데요, PHP는 백엔드단도 같이 작성가능한 언어이므로, 실수로 View단에서도 비즈니스 로직을 포함시킬 가능성이 있다는 부분입니다.
또한, 프론트와 백엔드의 역할을 확실히 분담하기 위해서도, View만 관여하는 템플릿엔진을 사용하는 것이 좋습니다.
템플릿 언더바는 아주 심플한 방법으로 백엔드 단에서 제공한 데이터를 View단으로 가공할 수 있는 간결한 문법들을 제공합니다.
단순한 출력
<h2>{info.item}</h2>
HTML
복사
•
템플릿 언더바는 기본적으로 중괄호 {} 안에 표출할 내용을 작성하게 됩니다.
•
단순히 백엔드 단에서 준 데이터를 표출하고자 하면, 위와 같이 작성하면 됩니다.
조건문
<table>
<thead>
...
</thead>
<tbody>
{? info.item.name == 'good'}
<tr>
<td>{info.item.name}</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
가장 간단한 조건문의 형태입니다.
•
? 다음에 조건문을 작성하고, 하단에 렌더링 할 DOM 객체를 작성하고, 마지막으로 {/} 로 닫아 마무리 합니다.
<table>
<thead>
...
</thead>
<tbody>
{? info.item.name == 'good'}
<tr>
<td>{info.item.name}</td>
</tr>
{:}
<tr>
<td>음.. 좋지 않습니다..</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
{:} 을 중간에 사용하여, else문의 역할을 수행합니다.
<table>
<thead>
...
</thead>
<tbody>
{? info.item.name == 'good'}
<tr>
<td>{info.item.name}</td>
</tr>
{: info.item.name == 'banana'}
<tr>
<td>맛있는 바나나</td>
</tr>
{:}
<tr>
<td>음.. 좋지 않습니다..</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
{:} 만 존재하면 기본적으로 else문의 역할을 하지만, {: 다른 조건} 형태로 작성하게 되면, else if 문의 역할을 수행합니다.
반복문 기본
•
데이터
info => list => [
[0] => {
"name": "이름1",
"title": "제목1"
},
[1] => ...
]
HTML
복사
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.name}</td>
<td>{.title}</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
반복문은 반복시킬 데이터 앞에 @을 붙여서 반복문의 시작을 알립니다.
◦
반복문 안에서 변수를 작성할 때는, 바로 속성 key를 접근해서 사용할 수 있습니다.
반복문 중첩
•
데이터
info => list => [
[0] => {
"name": "이름1",
"title": "제목1"
"roles" => [
[0] => {
"role_name": "ADMIN",
"role_code": "ROLE_01"
},
[1] => ...
]
},
[1] => ...
]
HTML
복사
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.name}</td>
<td>{.title}</td>
<td>
<ul>
{@ .roles}
<li>{..role_name}</li>
<li>{..role_code}</li>
{/}
</ul>
</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
중첩이 되었다고, 크게 달라지는 부분은 없습니다.
•
단지 중첩이 되었다면, 중첩이 된 속성 값을 가져올 때는, 중첩이 된 갯수만큼 “.”을 추가로 작성해야 합니다.
•
role_name 같은 경우를 보면, 2중첩이 되었기 때문에, 속성을 가져올 때, “.” 기호를 두 번 사용하는 것을 알 수 있습니다.
•
굳이 이렇게 구분하는 이유는, 하위 반복문에서도 상위 데이터의 값에 접근하는 경우가 있기 때문입니다.
•
만약 이걸 구분하지 않는다면, 하위 반복문에서 상위 반복문 데이터를 가져오지 못하여 난감한 상황이 발생하고 말 것 입니다.
반복문 key, value로 구성된 array 순회하기.
•
PHP는 조금은 특수한 문법이 있는데요 배열을 다룰 때, 단순히 인덱스를 참조해서 다루는 것이 아닌 key값을 통해 배열을 지정하는 문법이 존재합니다.
•
인덱스가 0~N의 형태로 올라가는 것이 아닌, [’abc’], [’bbb’] 와 같이 임의의 문자열 키로도 배열의 인덱스를 지정할 수 있습니다.
info => list => [
['little_prince'] => {
"title": "어린왕자",
"description": "보아뱀이 있었죠 아마?"
},
['red_head_an'] => {
"title": "빨강 머리 앤",
"description": "성냥을 팔았었죠"
}
]
HTML
복사
•
그럼 배열이지만, 이런 이상한 형태를 사용하는 경우도 발생하게 됩니다. 숫자로 지정하는 순차 인덱스가 아닌, 문자열 key값으로 지정하는 인덱스 형태가 됩니다.
•
그리고, 우리의 목표는 이를 View단에서 ‘little_prince’ 와 같은 단어를 ‘하드코딩’ 하지 않도록, 반복문을 돌려서 내용을 표출하는 것입니다.
•
왜냐하면, 나중에 내용이 추가되면, View단을 수정하고 싶지 않거든요..!
•
유지보수가 어려워지는 예시 (하드코딩 방식)
<table>
<thead>
...
</thead>
<tbody>
<tr>
<td>{info.list.little_prince.title}</td>
<td>{info.list.little_prince.description}</td>
</tr>
<tr>
<td>{info.list.red_head_an.title}</td>
<td>{info.list.red_head_an.description}</td>
</tr>
</tbody>
</table>
HTML
복사
•
전부 key형태로 되어있어, 반복문을 어떻게 돌려야 할지 감이 오지 않아 이렇게 작성하는 방향이 발생할 수 있습니다. (네 실제로 제가 그랬구요…
)
•
반복문으로 돌리는 방향 (좋은 예시)
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.value_.title}</td>
<td>{.value_.description}</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
value_ 는 특수한 의미를 나타냅니다. 현재의 반복중인 객체의 값을 나타냅니다. 이를 통해 굳이 key를 몰라도 바로 값에 접근하여, key로 이루어진 배열을 바로 순회할 수 있습니다.
•
key_ 를 사용하면, 현재 순회중인 객체의 key값도 확인할 수 있습니다.
반복할 데이터가 없는 경우
•
생각해보면, 우리는 데이터가 없다면, 친절하게 사용자에게 “데이터가 없습니다” 와 같은 안내문구를 띄워주어야 하는 경우가 많이 생깁니다.
•
일반적으로 이를 구현하고자 한다면, 최초에 배열의 크기를 측정하고, 있으면 반복문을 돌리고, 없으면 else문으로 돌아가서 데이터가 존재하지 않는다는 문구를 띄워줄 것 입니다.
•
템플릿 언더바는 이런 비효율을 없애기 위해서, 반복문하고 if문을 합친 것과 같은 효과를 내는 문법을 제공하고 있습니다.
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.value_.title}</td>
<td>{.value_.description}</td>
</tr>
{:}
<tr>
<td>데이터가 존재하지 않습니다.</td>
<tr>
{/}
</tbody>
</table>
HTML
복사
•
중간에 {:} 를 추가해서, 데이터가 없으면, 렌더링 할 DOM객체를 작성하면 됩니다.
현재 반복자의 전체 크기와, 인덱스 확인하기.
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.size_}개 중 {.index_}번 째</td>
<td>{.value_.title}</td>
<td>{.value_.description}</td>
</tr>
{:}
<tr>
<td>데이터가 존재하지 않습니다.</td>
<tr>
{/}
</tbody>
</table>
HTML
복사
•
size_ 를 사용하면, 현재 반복되고 있는 목록의 전체 갯수를 가져오게 됩니다.
•
index_ 를 사용하면, 현재 반복이 몇 번째 인지 가져오게 됩니다.
•
주의 할 점으로는, 해당 예약어 또한, 중첩이 N번 되면, 앞에 . 기호를 N번 작성해야 합니다.
<table>
<thead>
...
</thead>
<tbody>
{@ info.list}
<tr>
<td>{.size_}개 중 {.index_}번 째</td>
<td>{.name}</td>
<td>{.title}</td>
<td>
<ul>
{@ .roles}
<li>{..size_}개 중 {..index_}번 째</li>
<li>{..role_name}</li>
<li>{..role_code}</li>
{/}
</ul>
</td>
</tr>
{/}
</tbody>
</table>
HTML
복사
•
size_, index_ 도 마찬가지로, 하위단계의 크기와, 상위단계 크기를 명시적으로 구분하기 위해서 사용됩니다. 하위 단계 반복문에서 ..size_ 가 아닌 .size_ 를 사용하였다면, 상위 단계의 목록 갯수를 가져오게 되는 것이죠.
변수 출력 디버깅
<pre>{=print_r(info.list)}</pre>
HTML
복사
•
<pre> 태그를 사용하면, 데이터를 깔끔하게 표출합니다.
•
이를 통해 PHP와 혼합하여, 템플릿으로 출력할 데이터를 미리 확인하면서 작업할 수 있습니다.
•
하단에 출력할 데이터를 미리 전체로 출력해서 데이터의 구조를 확인하고, 이를 보고 View단을 작업하면 좀 더 빠른 작업을 도와줄 수 있습니다.
[주의!] 특수기호는 ‘_’ 만 사용하기.
•
제가 템플릿 언더바를 사용하면서, 가장 어이가 없었던 부분이기도 합니다.
•
변수명을 info.data-list 로 할당했다고 해봅시다.
•
하지만, 템플릿 언더바는 해당 변수를 찾지 못합니다.
•
info.datalist로 하거나 info.data_list 로 변경할 수 있도록 합니다. 아니면, info.dataList 와 같이 CamelCase 형태로 작성해도 좋습니다.
[주의!!] 백틱 문법은 사용금지.
•
JavaScript에는 변수를 문자열에 좀 더 쉽게 포함시킬 수 있게 + 연산대신 백틱 `` 사이에 ${변수명} 으로 값을 채워 넣어서 사용하는 문법이 있습니다.
•
const strData = `str: ${inputString}`; 이런 형태로 작성하는 것이죠
•
하지만 템플릿 언더바의 문법은 {}을 사용해서 데이터를 렌더링하게 됩니다.
•
백틱 문법을 사용할 것이라면, 따로 외부 스크립트로 빼서 로딩시키는 방식으로 수행하여야 합니다.
마무리
템플릿 언더바에 대해 어느정도 이해가 되셨나요?
생각보다 효율적이고, 간단하고, 단순한 문법을 가졌다는 것을 알았으면 좋겠습니다.
공식문서가 너무 대충되어 있어서 아쉬움이 있지만, 장점이 많은 템플릿 엔진 이라고 생각합니다.
저는 .의 갯수를 통해서 현재 중첩된 반복문에서 상위 데이터, 하위 데이터를 구분한다는 점이 가상 인상깊었습니다. 아주 간단하면서 직관적인 문법이라고 생각했습니다.
감사합니다.