객체를 화면에 출력하는 방식
| org.zerock.domain.MemberVO |
@Data
@AllArgsConstructor
public class MemberVO{
private int mno;
private String mid;
private String mpw;
private String mname;
private Timestamp regdate; // java.sql.Timestamp;
}
| SampleController에 이를 이용하는 sample2()를 작성한다. |
@GetMapping("/sample2")
public void sample2(Model model){
MemberVO vo = new MemberVO(123, "u00", "p00", "홍길동",
new Timestamp(System.out.currentTimeMillis()));
model.addAttribute("vo", vo);
}
| sample2.html |
<html xmlns:th="http://www.thymeleaf.org">
... 생략
<h1 th:text="${vo}">Thymeleaf Test Page</h1>
<div th:utext='${"<h3>" + vo.mid + "</h3>"}'></div>
<div th:text='${"<h3>" + vo.mid + "</h3>"}'></div>


리스트를 화면에 출력하기 th:each
화면에서 가장 많이 사용하는 루프의 처리는 th:each를이용해서 처리(th:each는 리스트나 java.util.Iterable, java.util.Map )배열 등을 사용할 수 있다.
th:each에 사용하는 표현식은 th:each="var : ${list}"와 같은 방식으로 작성('var'는 루프를 돌때 n번째 요소를 의미)한다.
| org.zorock.controller.SampleController.java |
@GetMapping("/sample3")
public void Sample3(Model model){
List<MemberVO> list = new ArrayList<>();
for(int i = 0; i < 10; i++){
list.add(new MemberVO(123, "u0", "p0" + i + "홍길동" + i +, new Timestamp(System.currentTimeMillis())));
}
model.addAttribute("list", list);
}
| sample3.html |
<html xmlns:th="http://www.thymeleaf.org">
... 생략
<table style="border:1px solid gray">
<tr>
<td>MID</td>
<td>MNAME</td>
<td>REGDATE</td>
</tr>
<tr th:each="member : ${list}">
<td th:text="${member.mid}"></td>
<td th:text="${member.mname}"></td>
<td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}"></td>
</tr>
</table>
</body>

th:each에는 반복의 상태에 대한 변수를 지정하여, 필요한 추가 정보들을 추출할 수 있다. th:each에 현재 상태에 대한 변수를 선언하면 다음과 같은 항목들을 사용할 수 있다.
| index | 0부터 시작하는 인덱스 번호 |
| count | 1부터 시작하는 번호 |
| size | 현재 대상의 length 혹은 size |
| odd/even | 현재 번호의 홀수/짝수 여부 |
| first/last | 처음 요서인지 마지막 요소인지를 판단 |
<table style="border:1px solid gray">
<tr>
<td>MID</td>
<td>MNAME</td>
<td>REGDATE</td>
</tr>
<tr th:each="member, iterState : ${list}">
<td th:text="${iterState.index}"></td>
<td th:text="${iterState.size}"></td>
<td th:text="${iterState.odd + ' ' + iterState.even}"></td>
<td th:text="${member.mid}"></td>
<td th:text="${member.mname}"></td>
<td th:text="${#dates.format(member.regdate, 'yyyy-MM-dd')}"></td>
</tr>
</table>

Thymeleaf의 유틸리티 객체
▣ Expression Basic Objects(표현식 기본 객체)
- #ctx
- #vars
- #locale
- #httpServletRequest
- #httpSession
▣ Expression Utility Object(표현식 유틸 객체)
- #dates
- #calendars
- #numbers
- #strings
- #objects
- #bools
- #arrays
- #lists
- #sets
- #maps
- #aggregates
- #messages
표현식 기본 객체는 기존에 JSP에서 application이나 request, session 등을 사용할때 변수가 된다(예를 들어, #vars의 경우 생략한 상태로 주로 사용된다. 따라서 다음 두줄은 동일한 의미를 가진다.
<div>[[${result}]]</div>
<div>[[${#vars.result}]]</div>
유틸리티 객체
Thymeleaf의 표현식은 OGNL(Object-Graph Navigation Language) 표현식을 이용해 데이터를 출력하게 된다.
유틸리티 객체는 기존처럼 메소드를 호출하는 방식으로 사용할 수 있는 객체들이다. 에를 들어, 날짜를 포매팅(formating)하기 위해 다음과 같은 방식으로 사용한다.
| org.zerock.controller.SampleController.java |
@GetMapping("/sample7")
public void sample7(Model model){
model.addAttribute("now", new Date());
model.addAttribute("price", 123456789);
model.addAttribute("title", "This is a jost Sample");
model.addAttribute("options", Arrays.asList("AAAA","BBBB","CCCC","DDDD");
}
| sample7.html |
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
<meta content="text/html; charset=UTF-8" http-equlv="Content-Type">
<title>Thymeleaf Test Page</title>
</head>
<body>
<h1 th:text="${now}"></h1>
<h1 th:text="${price}"></h1>
<h1 th:text="${title}"></h1>
<h1 th:text="${options}"></h1>
</body>
</html>

날짜 관련 #dates, #calendars
날짜 관련 기능은 java.util.Date와 java.util.Calendar의 기능을 이용한다고 이해하면 된다.
<h2 th:text="${#dates.format(now, 'yyyy-MM-dd')}"></h2>
<div th:with="timeValue=${#dates.createToday()}">
<p>[[${timeValue}]]
</div>
#dates의 #calendars에는 createToday()를 이용해서 변수로 사용할 수도 있다. 위의 코드는 브라우저에서 다음고 ㅏ같은 결과를 만들어 낸다.

숫자 관련 #numbers
Integer나 Double, Float에 대한 포매팅을 처리할 때 주로 사용한다
<h2 th:text="${#numbers.formatInteger(price,3,'COMMA')}"></h2>
<div>
<p th:text="${#numbers.formatInteger(priceValue,3,'COMMA')}"></p>
<p th:text="${#numbers.formatDecimal(priceValue,5,10,'POINT')}"></p>
</div>
소수점의 경우 formatInteger를 이용하면 정수 처리가 되기 때문에, 주의해서 사용해야 한다.
문자관련 #strings
문자열을 관련해서 대소문자 변환이나 contains() 등 기본적 기능들 외에 문자열을 결합하는 join이나 리스트로 나누는 listsplit 등의 기능들이 지원된다.
<h1 th:text="${title}"></h1>
<span th:utext="${#strings.replace(title, 's', '<b>s</b>')}"></span>
<ul>
<li th:each="str:${#strings.listSplit(title, ' ')"}>[[${str}]]</li>
</ul>

Thymeleaf 링크 처리
일반적인 웹 페이지의 링크는 크게 두가지로 'http://www...'와 같은 형태의 절대(absolute path)경로와 현재 URL을 기준으로 이용하는 상대(context-relative) 경로이다.
스프링부트에서는 프로젝트를 실행하면 '/'을 기준으로 동작하기 때문에 경로에 대한 스프레스 없이 작성할 수 있지만, WAS에서는 특정 경로에서 프로젝트가 실행되는경우에 문제가 될 수 있다.
Thymeleaf는 이러한 문제를 해결하기 위해 '@{ }'를 이용해 경로에 대한 처리를 자동으로 처리할 수 있다.
링크에 대한 처리는 지금까지 'sample1','/sample2'와 같은 경로들이 존재하므로, 이를 화면에서 처리하는 예제를 보면 다음과 같다.
| org.zerock.controller.SampleController.java |
@GetMapping("/sample8")
public void sample8(Model model){ }
<ul>
<li><a th:href="@{http://localhost:8080/sample1}"> 절대경로 sample1</a></li>
<li><a th:href="@{/sample1}">현재 실행되는 컨텍스트의 경로가 반영 sample1</a></li>
<li><a th:href="@{~/sample1}">sample1</a></li>
</ul>
'@{http://...}'로 시작하는 코드는 절대 경로 처리에 사용된다.
'@{/sample1'에는 현재 실행되는 컨텍스트의 경로가 반영된다. 만일 컨텍스트의 경로가 '/라면 '/sample1'과 같은 경로가 되지만 컨텍스트의 경로가 'boot05'와 같은 경로라면 '/boot05/sample1'과 같은 경로가 된다.

'@{~/sampl1}'의 경우 현제 프로젝트가 '/' 경로에서 실행되었기 때문에 '@{/sample1}' 차이가 없게 된다.
Thymeleaf의 링크 처리에서 조금 특이한 부분은 파라미터를 전달하는 경우이다. 일반적으로 직접 링크를 생성하기 위해서 '파라미터의 이름=값'의 형태로 작성하지만, Thymeleaf를 이용하면 (이름(키)=값)의 형태로 링크를 생성할 수 있다.
<li><a th:href="@{/sample1(p1='aaa', p2='bbb')}>sample1</a></li>

Thymeleaf의 레이아웃 기능