유용한 정보

thymeleaf (server-side template engine) 사용법 정리 - 2

CyberI 2017. 8. 24. 17:34

 

 

 


지역변수 (Local Variables)

Thymeleaf에서 지역변수의 범위는 변수가 정의된 DOM을 포함한 하위의 DOM까지를 포함합니다.

예를들면 다음과 같습니다.

1
2
3
4
5
<tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
</tr>
cs

위 fragment는 each 속성을 통한 반복구문인데 여기에 사용된 prod<tr>를 포함하여 그 하위의 태그에서만 사용이 가능합니다.

위와 같이 반복문 이외에도 th:with속성을 이용하여 지역변수를 사용할수 있는데 구문은 다음과 같습니다.

1
2
3
4
5
6
<div th:with="firstPer=${persons[0]},secondPer=${{persons[1]}">
  <p>
    The name of the first person is <span th:text="${firstPer.name}">Tom</span>.
    The name of the second person is <span th:text="${secondPer.name}">Jeny</span>.
  </p>
</div>
cs

 

,를 구분자로 여러개의 지역변수를 선언할수 있고 다음과 같이 동일한 속성에 정의된 변수의 재사용이 가능합니다.

1
<div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>
cs
 

또한 아래와 같이 th:with는 우선순위가 비교적 높기때문에 하나의 태그의 다양한 속성내에서 사용이 가능합니다.

1
2
3
4
5
<p>
  Today is: 
  <span th:with="df=#{date.format}" 
        th:text="${#calendars.format(today,df)}">13 February 2011</span>
</p>
cs

 

df 라는 변수를 정의하고 th:text에서 df 변수의 사용이 가능합니다. 여기에서 날짜데이터를 바인드 시키기위해 span태그가 사용이 되었는데 span태그가 불필요 하다면 span 대신 th:block을 사용하면 됩니다.

 

속성 우선순위

Thymeleaf 속성들의 우선순위는 다음 표와 같습니다.

Order Feature Attributes
1 Fragment inclusion th:include
th:replace
2 Fragment iteration th:each
3 Conditional evaluation th:if
th:unless
th:switch
th:case
4 Local variable definition th:object
th:with
5 General attribute modification th:attr
th:attrprepend
th:attrappend
6 Specific attribute modification th:value
th:href
th:src
...
7 Text (tag body modification) th:text
th:utext
8 Fragment specification th:fragment
9 Fragment removal th:remove

 

태그내의 정의된 속성의 순서와 별개로 위와같은 순서대로 속성이 적용 되므로 다음과 같이 표현을 해도 무방합니다. 다만 읽기는 힘들겠죠?

1
2
3
<ul>
  <li th:text="${item.description}" th:each="item : ${items}">Item description here...</li>
</ul>
cs

 

 

주석 & 블록

HTML과 XML에서 사용되는 표준 주석구문인 <!-- ... --> 은 thymeleaf 에서 별도로 처리하지 않고 그대로 표현합니다.

하지만 thymeleaf에서만 처리되는 특수한 주석이 2종류가 더 있는데요. 첫번째는 Parser-level 주석입니다.

1. Thymelef parser-level comments blocks

Parser-level주석은 정적인 페이지에서는 주석으로 남겨져있다가 thymeleaf 처리가 될때 제거되는 주석입니다.

1
2
<!--/* This code will be removed at thymeleaf parsing time! */-->
 
cs

<!--/*  와 */--> 사이의 내용은 thymeleaf 처리후 제거가 되므로 불필요한 주석을 클라이언트에 노출시키지 않게 처리가 가능합니다.

이뿐만 아니라 다음과 같이 사용하면 정적인 페이지에서 DOM을 표현했다가 thymeleaf 처리후 제거가 되도록 표현이 가능합니다.

1
2
3
4
5
<!--/*--> 
  <div>
     you can see me only before thymeleaf processes me!
  </div>
<!--*/-->
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
<table>
   <tr th:each="x : ${xs}">
     ...
   </tr>
   <!--/*-->
   <tr>
     ...
   </tr>
   <tr>
     ...
   </tr>
   <!--*/-->
</table>
cs

1편에서 소개해드린 th:remove 속성과 비슷한 용도로 사용이 가능하겠네요.

 

2. Thymelef prototype-only comment blocks

그다음 특수 주석은 Parser-level 주석과 약간 반대되는 개념의 주석인데요. 정적페이지에서 주석으로 처리가 되고 thymeleaf처리후 나타나게 되는 주석입니다.

1
2
3
4
5
6
7
<span>hello!</span>
<!--/*/
  <div th:text="${...}">
    ...
  </div>
/*/-->
<span>goodbye!</span>
cs

위와 같이 <!--/*/ ... /*/--> 표현을 하게 되면 정적 페이지에선 Hello! goodbye! 만 보여지게 되지만 thymeleaf처리가 되면

1
2
3
4
5
6
7
<span>hello!</span>
 
  <div th:text="${...}">
    ...
  </div>
 
<span>goodbye!</span>
cs

위와 같이 처리가 되어 Hello! 와 goodbye! 사이에 별도의 서버사이드 처리가 가능하게 됩니다.

 

3. Synthetic th:block tag

th:block은 thymeleaf에 기본적으로 포함된 Standard Dialects 입니다.

간단히 설명하자면 th:block은 thymeleaf가 처리가 되면 자신에 포함된 속성을 처리하고 사라지게 됩니다.

다음과 같이 같은 레벨인 DOM을 여러개 반복시킬때 유용합니다.

1
2
3
4
5
6
7
8
9
10
11
<table>
  <th:block th:each="user : ${users}">
    <tr>
        <td th:text="${user.login}">...</td>
        <td th:text="${user.name}">...</td>
    </tr>
    <tr>
        <td colspan="2" th:text="${user.address}">...</td>
    </tr>
  </th:block>
</table>
cs

위의 예시에서는 2개의 tr 태그가 반복되면서 생성이 되겠죠?

th:block prototye-only 주석과 함께 사용될때 특히 유용한데요.

1
2
3
4
5
6
7
8
9
10
11
<table>
    <!--/*/ <th:block th:each="user : ${users}"> /*/-->
    <tr>
        <td th:text="${user.login}">...</td>
        <td th:text="${user.name}">...</td>
    </tr>
    <tr>
        <td colspan="2" th:text="${user.address}">...</td>
    </tr>
    <!--/*/ </th:block> /*/-->
</table>
cs

위의 예시처럼 prototype-only 주석과 함께 사용하게 되면 정적인 페이지에선 2개의 tr만 보였다가 thymeleaf 처리가 되면 1번째 예시처럼 반복 처리가 됩니다.

 

Inlining

1. Text inlining

th:text 속성을 사용하여 대부분의 문자 처리가 가능하지만 HTML에 직접 표현할 수 있습니다. 예를 들면 다음과 같은 표현식인데요.

1
<p>Hello, [[${session.user.name}]]!</p>
cs

다음과 같은 표현을 대신 할수 있습니다

1
<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>
cs

이렇게  [[  ....  ]] 문법을 사용하기 위해서는 th:inline 속성이 필요합니다. 실제로는 다음과 같이 사용을 해야합니다. inline모드는 text 모드와 javascript 모드가 있습니다. 지금은 text inline 이 필요 하므로 th:inline="text" 값으로 정의 하겠습니다.

1
<p th:inline="text">Hello, [[${session.user.name}]]!</p>
cs

위와 같이 모든 태그에 th:inline 속성을 표시할 필요는 없고 다음과 같이

1
2
3
4
5
6
7
8
9
<body th:inline="text">
 
   ...
 
   <p>Hello, [[${session.user.name}]]!</p>
 
   ...
 
</body>
cs

상위 태그에 th:inline속성을 정의하면 모든 하위 노드에서 inline 문법을 사용할 수 있습니다.

이렇게 간편하게 th:text 속성 없이 텍스트 값을 바인딩 할수가 있는데요. 하지만 이렇게 사용하게 되면 정적 페이지 에서는 inline 표현식이 그대로 나타나게 되므로 더이상 프로토타입 페이지로 사용하기 어려울수 있습니다.

 

2. Script inlining (Javascript and Dart)

스크립트 inline 기능은 javascript 과 Dart를 지원합니다.

1
2
3
4
5
6
7
8
9
<script th:inline="javascript">
/*<![CDATA[*/
    ...
 
    var username = [[${session.user.name}]];
 
    ...
/*]]>*/
</script>
cs

위와 같이 script태그에 th:inline="javascript" 속성을 정의하면 그 안에 포함된 스크립트 에서는 [[ .. ]] 표현식으로 서버 데이터를 스크립트영역에 표현할 수 있습니다.

하지만 여기서 문제가 있습니다. 정적인 페이지로 열었을때 스크립트 영역의 [[ .. ]] 표현식은 문법 오류로 인식하게 되어 프로토타입 페이지로는 사용이 어려워 집니다.

 

이런 문제는 다음과 같이 /*[[ ... ]]*/ 표현식을 사용하게 되면 해결할수 있습니다.

1
2
3
4
5
6
7
8
9
<script th:inline="javascript">
/*<![CDATA[*/
    ...
 
    var username = /*[[${session.user.name}]]*/ 'Sebastian';
 
    ...
/*]]>*/
</script>
cs

 

 

위와 같이 표현하게 되면 정적 페이지에서는 /*[[${session.user.name}]]*/  부분이 주석처리 되어 username 변수의 값이  'Sebastian' 값으로 정의가 됩니다.

하지만 thymeleaf 처리가 되면 /*[[ .. ]]*/ 구문에 포함된 표현식을 실행하게 되고 그뒤에 있는 모든 코드는 제거 되어 실제 사용자의 이름을 가져올수 있습니다.

이렇게 문자열값 뿐만 아니라 다음과 같이 다양한 형식의 값을 표현할 수 있습니다.

  • Strings
  • Numbers
  • Booleans
  • Arrays
  • Collections
  • Maps
  • Beans (objects with getter and setter methods)

 

다음과 같이 Map 타입의 서버 변수를 표현하게 되면 javascript object형식으로 자동으로 변한해서 나타냅니다.

1
2
3
4
5
6
7
8
9
<script th:inline="javascript">
/*<![CDATA[*/
    ...
 
    var user = /*[[${session.user}]]*/ null;
 
    ...
/*]]>*/
</script>
cs

위의 표현식을 thymeleaf처리하게 되면 다음과 같이 표현됩니다.

1
2
3
4
5
6
7
8
9
10
<script th:inline="javascript">
/*<![CDATA[*/
    ...
 
    var user = {'age':null,'firstName':'John','lastName':'Apricot',
                'name':'John Apricot','nationality':'Antarctica'};
 
    ...
/*]]>*/
</script>
cs

정적 페이지에서는 user 변수의 값은 null이 겠죠?

자, 다음은 Adding Code 입니다.

thymeleaf 처리가 되었을때만 특정 스크립트를 추가하고 싶을때 사용할수 있는 기능으로 표현식은 다음과 같습니다.

 

1
2
3
4
5
6
7
8
9
10
var x = 23;
 
/*[+
 
var msg  = 'This is a working application';
 
+]*/
 
var f = function() {
    ...
cs

위와 같이 /*[+ ... +]*/ 표현식으로 작성하게 되면 정적 페이지엣더는 주석으로 처리되고 thymeleaf 처리가 되면 /*[+ ... +]*/ 표현식 안의 스크립트 내용이 작성됩니다. thymeleaf처리후 스크립트 내용은 다음과 같습니다.

1
2
3
4
5
6
var x = 23;
 
var msg  = 'This is a working application';
 
var f = function() {
...
cs

 

그 다음으로는 Removing Code 입니다.

Adding Code와 반대로 정적페이지에서 만 필요한 스크립트 코드를 작성하고 싶을때 사용합니다.

표현식은 /*[- */  ...  /* -]*/ 으로 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
var x = 23;
 
/*[- */
 
var msg  = 'This is a non-working template';
 
/* -]*/
 
var f = function() {
...
cs

보시는것과 같이 정적 페이지에서는 msg라는 변수가 정의가 되고 값이 할당 되지만 thymeleaf를 처리하게 되면 var msg  = 'This is a non-working template'; 부분이 삭제 됩니다.

 

..

네, 이렇게 Thymeleaf의 기본적인 사용법 정리를 마치도록 하겠습니다.

Thymeleaf를 처음 접해보는 분들에게는 조금이라도 도움이 되길 바라며, 해당 포스팅에 포함된 내용의 대부분은 http://www.thymeleaf.org 에서 원문으로 확인이 가능합니다.

 


thymeleaf 시리즈 1편 내용을 살펴보고 싶다면, 아래 링크를 클릭해주세요.

▶ thymeleaf (server-side template engine) 사용법 정리 - 1