Thymeleaf - 유용한 문법 정리

2 분 소요

개요

thymeleaf 템플릿 엔진을 사용하면서 발생하였던 문제들에 대해 해결 방법과 이를 통해 알게된 유용한 문법들을 정리하고 있습니다. 새로운 것을 알게 될 때 마다 하나씩 내용을 추가하려고 합니다!

1. for-each 역방향 루틴

th:each

image

<tbody>
    <tr th:each="order : ${orders}" class="align-text-top">
        <form th:action="@{/basic/orders/cancel}" method="post">
            <td>
            <a 
                th:href="@{/basic/stores/{storeId}(storeId=${order.store.id})}"
                th:text="${order.store.storeName}">이름
            </a>
            </td>
            ... 생략 ...
        </form>
    </tr>
</tbody>

기존 thymeleaf에서는 for-each 루틴을 지원하여 컬렉션 변수에 대한 반복을 쉽게 수행할 수 있다. 그런데 위와 같은 주문 테이블에서 생성되는 주문 리스트의 순서를 역순으로 놓고 싶었고, 방법을 찾아보게 되었다.

#numbers.sequence(start, end, step)

기본적으로 thymeleaf에서는 #numbers 라는 숫자 포맷 메소드를 지원한다. #numbers에 다양한 메소드들이 존재하는데 이번에 알아볼 것은 #numbers.sequence()이다.

아마 형태를 보고 유추할 수 있겠지만 이는 python의 built-in 메소드인 range(start, end, step) 와 같이 원하는 범위에 대해 시퀀스를 생성해준다.

아래와 같이 적용이 가능하다.

<tbody>
    <tr th:each="i : ${#numbers.sequence(orders.size()-1, 0, -1)}" class="align-text-top">
        <form action="cancel" th:action="@{/basic/orders/cancel}" method="post">
            <td>
            <a 
                th:href="@{/basic/stores/{storeId}(storeId=${orders[i].store.id})}" 
                th:text="${orders[i].store.storeName}">이름
            </a>
            </td>
            ... 생략 ...
        </form>
    </tr>
</tbody>

th:with

문제는 해결했지만 내부 <td> 태그에서도 i를 계속 사용해야 한다는 점이 마음에 들지 않는다. 다른 방법을 찾아보았다.

th:with을 통해 thymeleaf 템플릿 엔진 내부에서 새로운 변수 값을 생성해줄 수 있다.

ex) th:with="newVariable=${someModelAttribute}"

응용하여 기존 코드를 변경해보았다.

<tbody>
    <tr th:each="i : ${#numbers.sequence(orders.size()-1, 0, -1)}" class="align-text-top"
        th:with="order=${orders[i]}">
        <form action="cancel" th:action="@{/basic/orders/cancel}" method="post">
            <td>
            <a 
                th:href="@{/basic/stores/{storeId}(storeId=${order.store.id})}" 
                th:text="${order.store.storeName}">이름
            </a>
            </td>
            ... 생략 ...
        </form>
    </tr>
</tbody>

image

#numbersth:with 모두 유용하게 사용이 가능하다!

2. 삼항 연산자 & Enum

Enum type 비교

위에서 작성하던 주문 목록 View 처럼 여러 주문들 중에서 현재 상태가 ORDER 인 주문들만 주문 취소가 가능하게끔 만들고 싶었다.

먼저 주문의 상태를 나타내는 OrderStatus는 Enum 클래스로 구성되었다.

public enum OrderStatus {
    ORDER, CANCEL
}

따라서, thymeleaf에서 Enum 값을 비교해야 해서 방법을 찾아보았다.

thymeleaf 문법에는 그런 내용이 없었지만 스프링에서 제공하는 SpEL(Spring Expression Language)를 이용하여 타입을 파싱하는 방법이 있었다.

예약어 T를 이용하여 T(class.path.EnumClass).VALUE 와 같이 표현이 가능하다. 실제 코드에 적용해보았다.

<td>
<button name=orderId 
        th:if="${order.status == T(hello.itemservice.domain.order.OrderStatus).ORDER}" 
        th:value="${order.id}" type="submit" class="btn btn-danger">취소
</button>
</td>

image

if condition 삼항 연산자

원하는 결과를 얻었지만 CANCEL 상태의 주문에는 버튼이 아예 생성되지 않아서 버튼이 있는 부분에 비해 높이 값이 감소하게 되었다. 이를 해결하기 위해 HTMl 태그 스타일 중에서 visibility: hidden을 이용해야겠다고 생각했다.

인상 깊었던 점은 if condition을 사용할 때 반드시 th:if를 사용하지 않아도 된다는 점이었다. thymeleaf 에서도 삼항 연산자를 지원하고 있었다.

if (condition) ? A : B; // 삼항 연산자
<td>
<button name=orderId 
        th:style="${order.status == T(hello.itemservice.domain.order.OrderStatus).ORDER ? '' : 'visibility:hidden'}" 
        th:value="${order.id}" type="submit" class="btn btn-danger">취소
</button>
</td>

위와 같이 th:style 값에서 삼항 연산자를 추가하여 if condition에 만족하지 않으면 visibility 값을 hidden으로 설정하도록 하였다.

image

원했던 결과를 얻을 수 있었다!

댓글남기기