<html xmlns:th="http://www.thymeleaf.org">
HTML의 콘텐츠(content)에 데이터를 출력할 때 th:text
사용
ex) <span th:text="${data}">
HTML 테그의 속성이 아니라 HTML 콘텐츠 영역안에서 직접 데이터를 출력하고 싶을 때 [[...]]
사용
ex) 컨텐츠 안에서 직접 출력하기 = [[${data}]]
만약 Hello Spring!과 같이 Spring! 부분에 <b></b>
태그를 넣는다면 어떻게 될까?
소스를 보게 되면
Hello <b>Spring!</b>
와 같이 나오게 된다.
그렇다면 이를 어떻게 해결해야 할까?
다음과 같은 두가지 방법이 있다.
th:text
-> th:utext
[[...]]
-> [(...)]
이렇게 작성 후 실행해 보면 정상적으로 수행되는 것을 확인할 수 있다.
${...}
SpringEL의 다양한 표현식
user.username
: user의 username을 프로퍼티 접근 -> user.getUsername()
user['username']
: 위와 같음 -> user.getUsername()
user.getUsername()
: user의 getUsername() 을 직접 호출users[0].username
: List에서 첫 번째 회원을 찾고 username 프로퍼티 접근 -> list.get(0).getUsername()
users[0]['username']
: 위와 같음users[0].getUsername()
: List에서 첫 번째 회원을 찾고 메서드 직접 호출userMap['userA'].username
: Map에서 userA를 찾고, username 프로퍼티 접근 -> map.get("userA").getUsername()
userMap['userA']['username']
: 위와 같음userMap['userA'].getUsername()
: Map에서 userA를 찾고 메서드 직접 호출타임리프는 기본적으로 다양한 객체들을 제공한다.
그 중 편의 객체는 다음과 같다.
param
${param.paramData}
session
${session.sessionData}
@
${@helloBean.hello('Spring!')
다양한 객체가 존재하는데 자세한 내용은 다음 링크에서 볼 수 있다.
타임리프에서 URL을 생성할 때는 @{...}
문법을 사용하면 된다.
단순한 URL
@{/hello}
-> /hello
쿼리 파라미터
@{/hello(param1=${param1}, param2=${param2})}
-> /hello?param1=data1¶m2=data2
() 에 있는 부분은 쿼리 파라미터로 처리된다.
경로 변수
@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}
-> /hello/data1/data2
URL 경로상에 변수가 있으면 () 부분은 경로 변수로 처리된다.
경로 변수 + 쿼리 파라미터
@{/hello/{param1}(param1=${param1}, param2=${param2})}
-> /hello/data1?param2=data2
경로 변수와 쿼리 파라미터를 함께 사용할 수 있다.
예시)
<ul>
<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})}">path variable + query parameter</a></li>
</ul>
타임리프는 다음과 같은 리터럴이 있다.
타임리프에서 문자 리터럴은 항상 ' (작은 따옴표)로 감싸야 한다.
예시) <span th:text="'hello'">
만약 공백 없이 쭉 이어진다면 하나의 의미있는 토큰으로 인지해서 다음과 같이 작은 따옴표를 생략할 수 있다.
룰 : A-Z
, a-z
, 0-9
, []
, .
, -
, _
예시) <span th:text="hello">
비교연산 : HTML 엔티티를 사용해야 하는 부분을 주의하자,
>
(gt), <
(lt), >=
(ge), <=
(le), !
(not), ==
(eq), !=
(neq, ne)
조건식 : 자바의 조건식과 유사하다.
Elvis 연산자 : 조건식의 편의 버전
No-Operation : _ 인 경우 마치 타임리프가 실행되지 않는 것 처럼 동작한다. 이것을 잘 사용하면 HTML의 내용 그대로 활용할 수 있다. 마지막 예를 보면 데이터가 없습니다. 부분이 그대로 출력된다.
예시)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li>산술 연산
<ul>
<li>10 + 2 = <span th:text="10 + 2"></span></li>
<li>10 % 2 == 0 = <span th:text="10 % 2 == 0"></span></li>
</ul>
</li>
<li>비교 연산
<ul>
<li>1 > 10 = <span th:text="1 > 10"></span></li>
<li>1 gt 10 = <span th:text="1 gt 10"></span></li>
<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
<li>1 ge 10 = <span th:text="1 ge 10"></span></li>
<li>1 == 10 = <span th:text="1 == 10"></span></li>
<li>1 != 10 = <span th:text="1 != 10"></span></li>
</ul>
</li>
<li>조건식
<ul>
<li>(10 % 2 == 0)? '짝수':'홀수' = <span th:text="(10 % 2 == 0)?'짝수':'홀수'"></span></li>
</ul>
</li>
<li>Elvis 연산자
<ul>
<li>${data}?: '데이터가 없습니다.' = <span th:text="${data}?: '데이터가없습니다.'"></span></li>
<li>${nullData}?: '데이터가 없습니다.' = <span th:text="${nullData}?:'데이터가 없습니다.'"></span></li>
</ul>
</li>
<li>No-Operation
<ul>
<li>${data}?: _ = <span th:text="${data}?: _">데이터가 없습니다.</span></li>
<li>${nullData}?: _ = <span th:text="${nullData}?: _">데이터가 없습니다.</span></li>
</ul>
</li>
</ul>
</body>
</html>
실행 결과)
타임리프는 주로 HTML 태그에 th:*
속성을 지정하는 방식으로 동작한다. th:*
로 속성을 적용하면 기존 속성을 대체한다. 기존 속성이 없으면 새로 만든다.
th:*
속성을 지정하면 타임리프는 기존 속성을 th:*
로 지정한 속성으로 대체한다. 기존 속성이 없다면 새로 만든다.
<input type="text" name="mock" th:name="userA" />
-> 타임리프 렌더링 후 <input type="text" name="userA" />
th:attrappend
: 속성 값의 뒤에 값을 추가한다.
th:attrprepend
: 속성 값의 앞에 값을 추가한다.
th:classappend
: class 속성에 자연스럽게 추가한다.
HTML에서는 <input type="checkbox" name="active" checked="false" />
-> 이 경우에도 checked 속성이 있기 때문에 checked 처리가 되어버린다.
타임리프의 th:checked
는 값이 false
인 경우 checked
속성 자체를 제거한다.
<input type="checkbox" name="active" th:checked="false" />
-> 타임리프 렌더링 후 : <input type="checkbox" name="active" />
예시 코드)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>속성 설정</h1>
<input type="text" name="mock" th:name="userA" />
<h1>속성 추가</h1>
- th:attrappend = <input type="text" class="text" th:attrappend="class='large'" /><br/>
- th:attrprepend = <input type="text" class="text" th:attrprepend="class='large'" /><br/>
- th:classappend = <input type="text" class="text" th:classappend="large" /><br/>
<h1>checked 처리</h1>
- checked o <input type="checkbox" name="active" th:checked="true" /><br/>
- checked x <input type="checkbox" name="active" th:checked="false" /><br/>
- checked=false <input type="checkbox" name="active" checked="false" /><br/>
</body>
</html>
타임리프에서 반복은 th:each
를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.
<tr th:each="user : ${users}">
th:each
는 List 뿐만 아니라 배열, java.util.Iterable
, java.util.Enumeration
을 구현한 모든 객체를 반복에 사용할 수 있다. Map 도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry
다.<tr th:each="user, userStat : ${users}">
반복의 두번째 파라미터를 설정해서 반복의 상태를 확인 할 수 있다.
두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명( user
) + Stat
가 된다.
여기서는 user
+ Stat
= userStat
이므로 생략 가능하다.
예시 코드)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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>
<h1>반복 상태 유지</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
<th>etc</th>
</tr>
<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>
</table>
</body>
</html>
실행 화면)
if
, unless
타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.
만약 다음 조건이 false
인 경우 <span>...<span>
부분 자체가 렌더링 되지 않고 사라진다.
<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
switch
*
은 만족하는 조건이 없을 때 사용하는 디폴트이다
예시 코드)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>if, unless</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
</tr>
<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>
</table>
<h1>switch</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
</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>
</table>
</body>
</html>
실행 화면)
타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.
<script th:inline="javascript">
var username2 = /*[[${user.username}]]*/ "test username";
var username2 = /*userA*/ "test username";
var username2 = "userA";
이런 유용한 정보를 나눠주셔서 감사합니다.