본문 바로가기
개발/html,Thymeleaf

Thymeleaf 정리-1 [기본적인 표현방법]

by 카앙구운 2021. 5. 6.
728x90
반응형

Thymeleaf의 표현 방법

1. Variable Expressions  : ${...}

해당 Context에 포함된 변수를 사용

1
<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>
cs

 

2. Select Variable Expressions  : *{...}

Context에 포함된 변수를 사용한는 걸 보면 1번과 표현방식이 동일하지만 가까운 DOM에 th:object가로 존재하는 변수가 있다면 그 변수값에 표함된 값을 나타냅니다.(property 나 map의 value와 같이)

1
2
3
4
5
6
<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
 
cs

 

3. Message Expressions  : #{...}

미리 정의된  message properties 파일이 존재하고 thymeleaf engine에 등록되었다면 #표현식으로 메시지를 표시할 수 있습니다.

message properties

1
home.welcome=안녕하세요? 반갑습니다.
cs

다음과 같이 사용할 수 있습니다.

 

1
<p th:text="#{home.welcome}">인사말</p>
cs

 

만일 동적으로 사용하고싶다면 다음과 같이 표현할 수 있습니다.

1
home.welcome=안녕하세요? 반갑습니다. {0}

cs

 

1
<p th:text="#{home.welcome(${session.user.name})}">인사말(고객명)</p>
cs

 

4. Link URL Expressions  : @{...}

@표현식을 이용하여 URL를 표현할 수 있습니다.

1
2
3
4
5
6
7
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
 
cs

문자 더하기

문자를 더하는 방법은 크게 두 가지가 있습니다.

1. '|' 와 '|' 연산자 사이에 문자와 변수 표현식을 입력하면 static문자와 변수 문자가 이어져서 출력됩니다.

1
<span th:text="|Welcome to our application, ${user.name}!|">
cs

 

2. 아래와 같이 '+' 연산자를 이용해서 문자를 더 할 수 있습니다.

1
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
cs

 

이 둘을 혼합하여 사용도 가능합니다.

1
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
cs

 

속상값 설정

속성값을 설정하는 방법은 크게 2가지가 있습니다. 첫번째는 th:atrr 를 통해서 원하는 속성을 여러 개를 설정하는 방법이 있습니다. 두번째는 별도의 th:value, th:action, th:href 등 속성을 지정하여 별도로 설정할 수 있습니다.

1
2
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
cs

[src,title, alt 속성 추가]

아래와 같은 속성을 각각 선택하여 추가 설정 할 수 있습니다.

th:abbr th:accept th:accept-charset
th:accesskey th:action th:align
th:alt th:archive th:audio
th:autocomplete th:axis th:background
th:bgcolor th:border th:cellpadding
th:cellspacing th:challenge th:charset
th:cite th:class th:classid
th:codebase th:codetype th:cols
th:colspan th:compact th:content
th:contenteditable th:contextmenu th:data
th:datetime th:dir th:draggable
th:dropzone th:enctype th:for
th:form th:formaction th:formenctype
th:formmethod th:formtarget th:frame
th:frameborder th:headers th:height
th:high th:href th:hreflang
th:hspace th:http-equiv th:icon
th:id th:keytype th:kind
th:label th:lang th:list
th:longdesc th:low th:manifest
th:marginheight th:marginwidth th:max
th:maxlength th:media th:method
th:min th:name th:optimum
th:pattern th:placeholder th:poster
th:preload th:radiogroup th:rel
th:rev th:rows th:rowspan
th:rules th:sandbox th:scheme
th:scope th:scrolling th:size
th:sizes th:span th:spellcheck
th:src th:srclang th:standby
th:start th:step th:style
th:summary th:tabindex th:target
th:title th:type th:usemap
th:value th:valuetype th:vspace
th:width th:wrap th:xmlbase
th:xmllang th:xmlspace

 

속상값 append

속성값을 추가로 설정할 수 있는 append를 사용하고  변수 cssStyle 값을 'warning' 추가하려한다면 다음과 같습니다.

1
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
cs

th:appendclass와 해당 변수값인 cssStyle을 추가하여 다음과 같은 결과값을 출력합니다.

1
<input type="button" value="Do it!" class="btn warning" />
cs

 

boolean 고정값 설정

1
<input type="checkbox" name="active" th:checked="${user.active}" />
cs

위와 같은 방법으로 설정할 수 있으며 boolean 고정값ㅇ르 설정할 수 있는 속성들은 다음과 같습니다.

th:async th:autofocus th:autoplay
th:checked th:controls th:declare
th:default th:defer th:disabled
th:formnovalidate th:hidden th:ismap
th:loop th:multiple th:novalidate
th:nowrap th:open th:pubdate
th:readonly th:required th:reversed
th:scoped th:seamless th:selected

HTML5 표준 표기법 지원

th:* 방법으로 표기하는 것 이외에도 data-{prefix}-{name} 문법으로 표현이 가능합니다.

1
2
3
4
5
6
<table>
    <tr data-th-each="user : ${users}">
        <td data-th-text="${user.login}">...</td>
        <td data-th-text="${user.name}">...</td>
    </tr>
</table>
cs

 

 

반복문

반복 기능은 th:each 또는 data-th-each 속성으로 사용 가능하며 박복에 사용 가능한 변수 타입은 다음과 같습니다.

1. ArrayList나 HashSet처럼 Iterable 인터페이스를 구현한 객체

2. Map 인터페이스로 구현한 객체(Map의 key value 나열)

3.모든 배열

 

ex: list형 데이터를 가지고 있는 것을 테이블로 표현

1
2
3
4
5
6
7
8
9
10
11
12
<table>
      <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>IN STOCK</th>
      </tr>
      <tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
      </tr>
 </table>
cs

message properties에 #{true} #{false] 설정

반복을 돌면서 각 index마다 상태값을 가져올 수 있습니다.iter변수 뒤에 ','를 추가하여 상태 변수를 정의하고 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
  </tr>
  <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
  </tr>
</table>
cs
index	현재 반복 인덱스  (0부터 시작)		
count	현재 반복 인덱스  (1부터 시작)	
size	총 요소 수
current	현재 요소
even	현재 반복이 짝수인지 여부 (boolean) 
odd	현재 반복이 홀수인지 여부 (boolean)
first	현재 반복이 첫번째인지 여부 (boolean) 
last	현재 반복이 마지막인지 여부 (boolean)

 

조건문

특정 조건일 때만 해당 영역이 보여지는 것이 필요할 때는 조건 속성을 통해서 랜더링 여부를 결정 할 수 있습니다.

th:if속성을 통해서 사용 가능하며 else와 같은 역할을 하는 th:unless 사용도 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<table>
    <tr>
        <th>NAME</th>
        <th>PRICE</th>
        <th>IN STOCK</th>
    </tr>
    <tr th:if="${#lists.size(prods)} > 0" th:each="prod,iterStat : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}">yes</td>
    </tr>
    <tr th:unless="${#lists.size(prods)} > 0">
        <td colspan="3">No Data.</td>
    </tr>
</table>
cs

${#lists.size(prods)}는 list의 사이즈를 구하는 함수

위와 같이 prods의 size가 0인 경우에는 No Data. 문구를 포함하는 tr이 노출이 되고 0보다 큰 경우에는 정상적으로 prods의 값을 나타나게 됩니다.

th:if, th:unless 외에도 th:switch/th:case 속성도 다음과 같이 사용 가능합니다.

1
2
3
4
5
<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>
cs

 

fragment

JSP의 include처럼 다른 template의 특정 영역을 가져와서 나타낼 수 있습니다.

물론 현재의 template특정 영역도 사용 가능합니다. 홈페이지의 공통으로 들어가는 하단인 footer 영역이 별도의 html을 /WEB-INF/templates/footer.html 명으로 가정할 때  특정 영역을 가져오는 방법은 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
 
  <body>
  
    <div th:fragment="copy">
      &copy; 2011 The Good Thymes Virtual Grocery
    </div>
  
  </body>
  
</html>
cs

위 html은 footer.html이며  th:fragment 속성에 copy라는 값이 보여집니다.

다른 페이지에서 위의 fragment="copy"부분을 include하는 예제입니다.

1
2
3
4
5
6
7
<body>
 
  ...
 
  <div th:include="footer :: copy"></div>
  
</body>
cs

th:include="템플릿명 :: fragment명"의 형태로 가져올 수 있습니다.

위에서 .html을 생략할 수 있었던 이유는 템플릿 prefix를 /templates/로 설정했고 suffix를 .html로 설정했기 때문입니다. 

[spring boot]

spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

th:fragment 대신 id를 셋팅하고 해당 id를 참조하여 include 할 수 있습니다.

1
2
3
4
5
6
7
<body>
 
  ...
 
  <div th:include="footer :: copy"></div>
  
</body>
cs

th:include외에 th:replace 속성으로도 사용이 가능합니다.

include는 속한 내용에 대해서만 가져오게 되고 replace는 전체를 교체하는 방식으로 가져오게 됩니다.

그래서 다음과 같은 값이 나오게 됩니다.

1
2
3
<footer th:fragment="copy">
  &copy; 2011 The Good Thymes Virtual Grocery
</footer>

 

1
2
3
4
5
6
7
8
<body>
 
  ...
 
  <div th:include="footer :: copy"></div>
  <div th:replace="footer :: copy"></div>
  
</body>
cs

결과

1
2
3
4
5
6
7
8
9
10
11
12
<body>
 
  ...
 
  <div>
    &copy; 2011 The Good Thymes Virtual Grocery
  </div>
  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>
  
</body>
cs

 

JSP의 include에도 paameter를 전달할 수 있듯이 thymeleaf의 fragment도 parameter를 전달 할 수 있습니다.

1
2
3
<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
cs

footer.html

th:include="footer :: frag('spring','스프링')와 같이 사용할 수 있습니다.

1
<div th:include="footer::frag (${value1},${value2})">...</div>
cs

순서와 상관없이 변수명을 지정하여 사용할 수도 있습니다.

1
2
<div th:include="footer::frag (onevar=${value1},twovar=${value2})">...</div>
<div th:include="footer::frag (twovar=${value2},onevar=${value1})">...</div>
cs

두번째 방법과 같이 fragment에 변수가 정의 되어있지 않으면 변수명=변수값 형식으로만 변수값을 전달하여 include할 수 있습니다.

1
2
3
<div th:fragment="frag">
    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
cs

현재 템플릿에서 사용하는 경우 :: frag로 사용 가능합니다.(this :: frag 와 동일)

 

assert

th:assert 속성을 사용하여 조건을 ',' 로 연결하여 여러 조건을 사용할 수 있습니다. 모든 조건이 true가 아닌 경우 exception을 일으킵니다.

1
2
3
<div th:fragment="frag">
    <p th:assert="${onevar}=='a',${twovar}=='b'" th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>
cs

onevar가 'a'가 아니거나 twovar가 'b'가 아니면 exception

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
      <a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:unless="${#lists.isEmpty(prod.comments)}">view</a>
    </td>
  </tr>
</table>
cs

위와 같이 작성된 페이지는 thymeleaf template engine을 통하게 되면 전혀 문제가 되지 않습니다.
하지만 만약 thymeleaf를 처리하지 않고 브라우저에서 직접 열게 된다면 테이블 header만 표시되고 body영역은 표시 되지 않아 이것으로 현실적으로 프로토타입이 될 수 없습니다.

이럴때는 하나 이상의 제푸정보가 나타나야 정상적으로 프로토타입 형태로 사용할 수 있습니다
이때 사용할 수 있는 속성이  th:remove 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<table>
  <tr>
    <th>NAME</th>
    <th>PRICE</th>
    <th>IN STOCK</th>
    <th>COMMENTS</th>
  </tr>
  <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
    <td>
      <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
      <a href="comments.html" 
         th:href="@{/product/comments(prodId=${prod.id})}" 
         th:unless="${#lists.isEmpty(prod.comments)}">view</a>
    </td>
  </tr>
  <tr class="odd" th:remove="all">
    <td>Blue Lettuce</td>
    <td>9.55</td>
    <td>no</td>
    <td>
      <span>0</span> comment/s
    </td>
  </tr>
  <tr th:remove="all">
    <td>Mild Cinnamon</td>
    <td>1.99</td>
    <td>yes</td>
    <td>
      <span>3</span> comment/s
      <a href="comments.html">view</a>
    </td>
  </tr>
</table>
cs

위의 코드를 보시면 th:remove="all" 이 되어 이는 tr이 2개 보입니다. th:remove가 all이 되어있고 thymeleaf engine을 통하게 되면 모두 제거가 됩니다. 이로써 정적인 상황에서도 완벽한 테이블의 형태로 나타낼 수 있습니다.
thymeleaf engine을 통하지 않더라도 해당 목록은 나오게 되는 것입니다.

'all' 이외에도 4가지의 값이 더 있는데 자세한 설명은 다음과 같습니다.

1. all : 자신을 포함한 모든 자식 노드를 제거한다.
2. body : 자신을 제외하고 모든 자식 노드를 제거한다.
3. tag : 자신을 제거하고 모든 자식은 제거 하지 않는다.
4. all-but-first : 첫번째 자식 노드를 제외하고 모든 자식 노드를 제거한다.
5. none : 제거하지 않는다.(이 값은 동적인 평가(조건)를 이용해서 remove 시킬때 사용)
1
<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>
cs

 

4번의 all-but-first는 th:remove="all"을 중복으로 사용하지 않도록 할 때 사용합니다.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<table>
  <thead>
    <tr>
      <th>NAME</th>
      <th>PRICE</th>
      <th>IN STOCK</th>
      <th>COMMENTS</th>
    </tr>
  </thead>
  <tbody th:remove="all-but-first">
    <tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
      <td th:text="${prod.name}">Onions</td>
      <td th:text="${prod.price}">2.41</td>
      <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
      <td>
        <span th:text="${#lists.size(prod.comments)}">2</span> comment/s
        <a href="comments.html" 
           th:href="@{/product/comments(prodId=${prod.id})}" 
           th:unless="${#lists.isEmpty(prod.comments)}">view</a>
      </td>
    </tr>
    <tr class="odd">
      <td>Blue Lettuce</td>
      <td>9.55</td>
      <td>no</td>
      <td>
        <span>0</span> comment/s
      </td>
    </tr>
    <tr>
      <td>Mild Cinnamon</td>
      <td>1.99</td>
      <td>yes</td>
      <td>
        <span>3</span> comment/s
        <a href="comments.html">view</a>
      </td>
    </tr>
  </tbody>
</table>
cs
728x90
반응형

댓글