+,- , * , / , %
타임리프는 기본적으로 HTML 테크의 속성에 기능을 정의해서 동작한다. HTML의 콘텐츠에 데이터를 출력할 때는 다음과 같이 th:text="${지정값}"
를 사용하면 된다
HTML 태그의 속성이 아닌 콘텐츠 영역안에서 직접 데이터를 출력하고 싶으면 [[...]]
를 사용하면 된다.
컨텐츠 안에서 직접 출력하기 = [[${data}]]
Escape
HTML문서는 th:text로 "Hello <b>Spring!</b>"
를 입력하게 하면 웹브라우저에서 그대로 "Hello <b>Spring!</b>"
라고 노출된다.
웹브라우저는 <
를 HTML 태그의 시작으로 인식해서 문자로 표현할 수 있는 방법이 필요하다.
타임리프가 제공하는 th:text
, [[...]]
는 기본적으로 이스케이프 (escape)를 제공한다.
타임리프가 제공하는 이스케이프 기능을 사용하고 싶지 않다면 th:utext 또는 [(...)]
둘 중에 하나를 선택해서 사용하면 된다.
Object
user.username
: user의 username을 프로퍼티 접근 user.getUsername()
user['username']
: 위와 같음 user.getUsername()
user.getUsername()
: user의 getUsername()
을 직접 호출List
users[0].username
: List에서 첫 번째 회원을 찾고 username 프로퍼티 접근 list.get(0).getUsername()
users[0]['username']
users[0].getUsername()
: List에서 첫 번째 회원을 찾고 메서드 직접 호출Map
userMap['userA'].username
: Map에서 userA를 찾고, username 프로퍼티 접근map.get("userA").getUsername()
userMap['userA']['username']
userMap['userA'].getUsername()
: Map에서 userA를 찾고 메서드 직접 호출지역변수
th:with
를 사용하면 지역변수를 선언해서 사용이 가능하지만, 지역변수는 선언한 태그 내에서만 사용할 수 있다.
@GetMapping("/basic-objects")
public String basicObjects(Model model, HttpServletRequest request,
HttpServletResponse response, HttpSession session) {
session.setAttribute("sessionData", "Hello Session");
model.addAttribute("request", request);
model.addAttribute("response", response);
model.addAttribute("servletContext", request.getServletContext());
return "basic/basic-objects";
}
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
#message
: 메시지, 국제화 처리
#uris
: URI 이스케이프 지원
#dates
: java.util.Date
서식 지원
#calendars
: java.util.Calendar
서식 지원
#temporals
: 자바8 날짜 서식 지원
#numbers
: 숫자 서식 지원
#strings
: 문자 관련 편의 기능
#objects
: 객체 관련 기능 제공
#bools
: boolean 관련 기능 제공
#arrays
: 배열 관련 기능 제공
#lists
, #sets
, #maps
: 컬렉션 관련 기능 제공
타임리프에서는 URL을 생성할 때 @{...}
문법을 사용하면 된다.
<li>
<a th:href="@{/hello}">basic url</a></li>
<li>
<a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query
param
</a>
</li>
<li>
<a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">
path variable
</a>
</li>
<li>
<a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">
pathvariable + query parameter
</a>
</li>
@{/hello(param1=${param1}, param2=${param2})}
/hello?param1=data1¶m2=data2
@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}
/hello/data1/data2
()
부분은 경로 변수로 처리된다./hello/data1?param2=data2
리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.
타임리프에서는 문자 리터럴은 항상 ''로 감싸야 한다. 하지만 항상 ''로 감싸는 것은 너무 귀찮은 일이라 공백없이 쭉 이어진다면 하나의 의미있는 토큰으로 인지해 작은 따옴표를 생략할 수 있다.
<span th:text="hello world!"></span>
이런 경우 공백이 있어 하나의 의미있는 토큰으로도 인식되지 않아 오류가 발생한다.
<span th:text="'hello world!'"></span>
이렇게 '로 감싸면 정상 동작한다.
<span th:text="|hello ${data}|">
리터럴의 대체 문법을 사용하면 마치 템플릿을 사용하는 것처럼 편리하다.
<li>10 + 2 = <span th:text="10 + 2"></span></li>
<li>10 % 2 == 0 = <span th:text="10 % 2 == 0"></span></li>
<li>(10 % 2 == 0)? '짝수':'홀수' = <span th:text="(10 % 2 == 0)? '짝 수':'홀수'"></span></li>
<li>${data}?: '데이터가 없습니다.' = <span th:text="${data}?: '데이터가 없습 니다.'"></span></li>
<li>${nullData}?: '데이터가 없습니다.'= <span th:text="${nullData}?: '데 이터가 없습니다.'"></span></li>
<li>${data}?: _ = <span th:text="${data}?: _">데이터가 없습니다.</span></
li>
조건식: 자바와 비슷하다.
no-operation(_)
: _
인 경우 아무것도 실행이 되지 않으며, 태그내의 내용이 출력된다.
@GetMapping("/each")
public String each(Model model) {
addUsers(model);
return "basic/each";
}
private void addUsers(Model model) {
List<User> list = new ArrayList<>();
list.add(new User("userA", 10));
list.add(new User("userB", 20));
list.add(new User("userC", 30));
model.addAttribute("users", list);
}
<h1>기본 테이블</h1> <table border="1">
<tr>
<th>username</th>
<th>age</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
</table>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">username</td>
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
<td>
index = <span th:text="${userStat.index}"></span>
count = <span th:text="${userStat.count}"></span>
size = <span th:text="${userStat.size}"></span>
even? = <span th:text="${userStat.even}"></span>
odd? = <span th:text="${userStat.odd}"></span>
first? = <span th:text="${userStat.first}"></span>
last? = <span th:text="${userStat.last}"></span>
current = <span th:text="${userStat.current}"></span>
</td>
</tr>
반복 기능
<tr th:each="user : ${users}">
th:each
는 List
뿐만 아니라 배열,java.util.Iterable
, java.util.Enumeration
을 구현한 모든 객체를 반복에 사용할 수 있다.Map
도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry
이다.반복 기능
<tr th:each="user, userStat : ${users}">
Stat
가 된다.반복 상태 유지 기능
index
: 0부터 시작하는 값count
: 1부터 시작하는 값size
: 전체 사이즈even
, odd
: 홀수, 짝수 여부( boolean
) first
, last
:처음, 마지막 여부( boolean
)current
: 현재 객체 <tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">1</td>
<td th:text="${user.username}">username</td>
<td>
<span th:text="${user.age}">0</span>
<span th:text="'미성년자'" th:if="${user.age lt 20}">
</span> <span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
</td>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">1</td>
<td th:text="${user.username}">username</td>
<td th:switch="${user.age}">
<span th:case="10">10살</span>
<span th:case="20">20살</span>
<span th:case="*">기타</span>
</td>
</tr>
<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
<th:block>
은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다
<th:block th:each="user : ${users}">
<div>
사용자 이름1 <span th:text="${user.username}"></span>
사용자 나이1 <span th:text="${user.age}"></span>
</div>
<div>
요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
</div>
</th:block>
<div>
요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
</div>
user 객체에 전달되지 않은 새로운 내용을 반복하고 싶을 때 block
을 사용한다.
<footer th:fragment="copy">
푸터 자리 입니다.
</footer>
<footer th:fragment="copyParam (param1, param2)">
<p>파라미터 자리 입니다.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
<h1>부분 포함</h1>
<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 단순 표현식</h2>
<div th:replace="template/fragment/footer :: copy"></div>
<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></ div>
th:insert
를 사용하면 현재 태그( div
) 내부에 추가한다.
th:replace
를 사용하면 현재 태그( div
)를 대체한다.
앞서 이야기한 개념을 <head>
정도에만 적용하는게 아니라 <html>
전체에 적용할 수도 있다.
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">레이아웃 타이틀</title> </head>
<body>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
<p>레이아웃 컨텐츠</p>
</div>
<footer>
레이아웃 푸터
</footer>
</body>
</html>
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title},
~{::section})}" xmlns:th="http://www.thymeleaf.org">
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div> </section>
</body>
</html>
layoutFile.html
을 보면 기본 레이아웃을 가지고 있는데, <html>
에 th:fragment
속성이 정의되어 있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.
layoutExtendMain.html
는 현재 페이지인데, <html>
자체를 th:replace
를 사용해서 변경하는 것을 확인 할 수 있다. 결국 layoutFile.html
에 필요한 내용을 전달하면서 <html>
자체를 layoutFile.html
로 변경 한다.
출처: https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EC%A4%91%EA%B8%89-1/dashboard