이번 포스트에서는 Thymeleaf에서의 제어문에 대해 살펴보자.
반복문
th:each
Thymeleaf에서 반복문을 사용하기 위해서는 th:each를 사용한다. 반복의 대상은 배열이나 Collection, Stream 등이 가능하다.
간단히 th:each="item, status: ${iterable}" 문장의 사용예를 보면서 사용법을 익혀보자.
<table>
<thead>
<tr><th>no</th><th>id</th><th>age</th><th>addr</th></tr>
</thead>
<tbody>
<tr th:each="user, status: ${list}" th:class="${status.odd}?'odd'">
<td th:text="${status.count}">count</td>
<td th:text="${user.name}">name</td>
<td th:text="${user.age}">age</td>
<td th:text="${user.addr}">addr</td>
</tr>
</tbody>
</table>
th:each는 for-each 문장과 유사하게 iterable 한 요소를 하나씩 item으로 분리해서 사용한다. item은 다시 ctx의 속성으로 등록되므로 ${item} 형태로 사용할 수 있다.
상태 관리
옵션으로 사용되는 status는 반복도는 상황을 체크하기 위한 여러 속성이 제공된다.
- status.index: 0부터 시작하는 index
- status.count: 1부터 시작하는 index
- status.size: iterable 요소의 전체 size
- status.current: 현재 반복도는 요소
- status.even, status.odd: 각각 현재 행이 짝수인지, 홀수인지를 나타내는 속성으로 1부터 시작
참고로 status 변수를 추가로 설정하지 않고 간단히 userStat 처럼 사용할 수도 있다.
<table>
<thead>
<tr><th>no</th><th>id</th><th>age</th><th>addr</th></tr>
</thead>
<tbody>
<tr th:each="user: ${list}" th:class="${userStat.odd}?'odd'">
<td th:text="${userStat.count}">count</td>
<td th:text="${user.name}">name</td>
<td th:text="${user.age}">age</td>
<td th:text="${user.addr}">addr</td>
</tr>
</tbody>
</table>
th:block
th:each는 하나의 요소(위의 경우는 tr)가 반복되는데 필요에 따라 여러 요소가 반복돼야 하는 경우도 있다. 이런 경우 <div>, <span>과 같은 태그를 이용해서 묶어서 처리하기도 하지만 반복을 위해 불필요한 DOM 요소가 들어가기도 하고 두 개의 <tr>을 묶어야 하는 경우 등은 상당히 억지스럽다.
이럴 때 th:block 태그(속성이 아니다.)를 사용할 수 있는데 th:block은 html을 생성하는 과정까지만 존재하고 DOM을 구성하지는 않는다.
<th:block th:each="user: ${list}">
<span th:text="${user.name}"></span>, <span th:text="${user.age}"></span>
<hr>
</th:block>
조건문
th:if, th:unless
Thymeleaf에서는 조건문을 사용할 때 th:if 문장을 사용하며 값으로는 꼭 boolean 타입이 아니더라도 true/false로 평가할 수 있는 값들이 들어오면 된다. (Javascript에서 truthy, falsy와 동일하다.)
true로 평가되는 경우 |
false로 평가되는 경우 |
값이 null 이 아니고 - boolean 타입이라면 true인 경우 - number 타입이라면 0이 아닌 경우 - character인 경우 empty character가 아닌 경우 - String이라면 false, off, no가 아닌 경우 - 그리고 나머지 타입인 경우 |
값이 null 이거나 - 좌측의 상황이 아닌 경우 |
특히 String의 경우 "false"라는 값이 있더라도 false로 평가되는게 주의가 필요하다.
사용법은 매우 단순하다. 만약 isGood이라는 attribute가 true로 설정된 경우라면
model.addAttribute("isGood", true);
html 단에서는 isGood에 따라 태그를 보이거나 보이지 않을 수 있다.
<a href="test.html" th:if="${isGood}">true: 보임</a>
<a href="test.html" th:if="${!isGood}">false: 보이지 않음</a>
<a href="test.html" th:unless="${isGood}">true: 보이지 않음</a>
참고로 th:if의 반대 개념으로 th:unless를 사용할 수도 있다.
th:switch, th:case
switch~case 형태의 문장을 사용해볼 수도 있는데 일반적으로 상위 태그에 switch를 걸고 특정 case에 하위 태그를 출력하려는 경우 사용된다.
만약 role이라는 값이 manager로 설정된 경우라면
model.addAttribute("role", "manager");
html단에서 다음과 같이 사용해볼 수 있다. default에 해당하는 조건을 작성할 때에는 th:case의 값으로 *를 사용한다.
<div th:switch="${role}">
<p th:case="'admin'">admin</p>
<p th:case="'manager'">manager</p>
<p th:case="*">anonymous</p>
</div>