조건 ? true시 실행 : false시 실행
${data} ? : default
data가 존재하면 data를 보여주고 없다면 default에 적힌 값을 보여준다.th:*
로 *
이라는 속성값을 랜더링 시에 대체한다. 만약에 속성이 정의되지 않았다면 새로 추가한다.
<input type="text" name="mock" th:name="userA" /> 타임리프 렌더링 후 <input type="text" name="userA" />
속성 추가하는 방법도 있다.
th:attrappend
: 속성 값의 뒤에 값을 추가한다.
th:attrprepend
: 속성 값의 앞에 값을 추가한다.
th:classappend
: class 속성에 자연스럽게 추가한다.
HTML에서는 checked 속성의 true/false 상태와 무관하게 해당 특성이 선언되면 무조건 checked 처리가 된다. 반면에 th:checked
를 사용하면 true인 경우 checked되고 false인 경우엔 checked되지 않는다. 나중에 checked값을 기반으로 logic을 짠다면 매우 유용할 것 같다.
<input type="checkbox" name="active" th:checked="false" /> 타임리프 렌더링 후: <input type="checkbox" name="active" />
th:each
를 사용하는데 해당 태그 안에서 사용할 수 있다. 이전에 봤던 변수 선언 th:with
처럼 범위가 있다고 생각하면 된다.
<tr th:each="user : ${users}">
users는 list인데 다른 언어에서 반복문을 도는 것처럼 user가 하나씩 매칭되며 돌아간다.
th:each
는 List
뿐만 아니라 배열
, java.util.Iterable
, java.util.Enumeration
을 구현한 모든 객체를 반복에 사용할 수 있다. Map
도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry
라고 한다.
<tr th:each="user, userStat : ${users}">
반복의 두번째 파라미터를 설정해서 반복의 상태를 확인 할 수 있다. 두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명( user ) + Stat 가 된다.
여기서는 user + Stat = userStat 이므로 생략 가능!!!
<span th:if="user.age lt 20" th:text="미성년자출입금지"/>
if조건이 충족되지 못하면 랜더링 시 span태그 자체가 사라진다.
이외에도 unless
, switch
-case
가 있다.
<h1>1. 표준 HTML 주석</h1> <!-- <span th:text="${data}">html data</span> --> <h1>2. 타임리프 파서 주석</h1> <!--/* [[${data}]] */--> <!--/*--> <span th:text="${data}">html data</span> <!--*/--> <h1>3. 타임리프 프로토타입 주석</h1> <!--/*/ <span th:text="${data}">html data</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
태그를 두개씩 반복문으로 돌리고 싶을 때 유용하다. 랜더링 시에 <th:block>
은 사라진다.
타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.
<script th:inline="javascript">
var username = [[${user.username}]];
var username = userA;
var username = "userA";
userA
라는 변수 이름이 그대로 남아있다. 타임리프 입장에서는 정확하게 렌더링 한 것이지만 아마 개발자가 기대한 것은 다음과 같은 "userA"라는 문자일 것이다. 결과적으로 userA
가 변수명으로 사용되어서 자바스크립트 오류가 발생한다. 다음으로 나오는 숫자 age의 경우에는 "
가 필요 없기 때문에 정상 렌더링 된다."
를 포함해준다. 추가로 자바스크립트에서 문제가 될 수 있는 문자가 포함되어 있으면 이스케이프 처리도 해준다. 예) "
\"
타임리프는 HTML 파일을 직접 열어도 동작하는 내추럴 템플릿 기능을 제공한다. 자바스크립트 인라인 기능을 사용하면 주석을 활용해서 이 기능을 사용할 수 있다.
결과 화면 인라인 사용 전 var user = BasicController.User(username=userA, age=10); 인라인 사용 후 var user = {"username":"userA","age":10};
var username2 = /*[[${user.username}]]*/ "test username";
var username2 = /*userA*/ "test username";
var username2 = "userA";
타임리프의 자바스크립트 인라인 기능을 사용하면 객체를 JSON으로 자동으로 변환해준다.
var user = [[${user}]];
var user = BasicController.User(username=userA, age=10);
var user = {"username":"userA","age":10};
<script th:inline="javascript"> [# th:each="user, stat : ${users}"] var user[[${stat.count}]] = [[${user}]]; [/] </script>
결과 화면
<script> var user1 = {"username":"userA","age":10}; var user2 = {"username":"userB","age":20}; var user3 = {"username":"userC","age":30}; </script>
프로그래밍에서 함수와 비슷한 느낌이다. 반복되는 부분을 fragment
조각으로 만들고 필요할 때마다 가져다 사용하는 거다.
<footer th:fragment="조각이름"> 안녕 난 푸터야 </footer>
이게 함수같은 조각이다.
파라미터를 설정할 수도 있다.
<footer th:fragment="조각이름 (param1, param2)"> <p>파라미터 자리 입니다.</p> <p th:text="${param1}"></p> <p th:text="${param2}"></p> </footer>
이 조각을 가져다 쓰는 부분을 보자.
th:insert
, th:replace
가 있는데 th:insert
는 말 그대로 현재 폼에서 해당 부분에 조각을 추가하는 것이고 th:replace
는 해당 태그를 그냥 불러오는 조각으로 갈아치우는 거다.
<div th:insert="~{조각의 경로/경로/경로 :: 조각이름}"/> <div th:replace="~{조각의 경로/경로/경로 :: 조각이름}"/> <div th:replace="조각의 경로/경로/경로 :: 조각이름"></div>
파라미터가 있는 조각을 사용할 때는
<div th:replace="~{조각의 경로/경로/경로 :: 조각이름 ('데이터1', '데이터2')}"></div>
템플릿 조각이랑 살짝 다른 개념인데 아까는 내가 만든 틀에 필요한 조각을 가져다 쓴거지만, 이제 레이아웃은 이미 정해진 큰 틀에 내가 쓰고 싶은 내용을 추가하는 형식이다.
예를 들어서 <head>
에 공통으로 사용하는 css
, javascript
같은 정보들이 있는데, 이러한 공통 정보들을 한 곳에 모아두고, 공통으로 사용하지만, 각 페이지마다 필요한 정보를 더 추가해서 사용하고 싶다면 다음과 같이 사용하면 된다.
<html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="common_header(title,links)"> <title th:replace="${title}">레이아웃 타이틀</title> <!-- 공통 --> <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script> <!-- 추가 --> <th:block th:replace="${links}" /> </head>
title, links에 태그 자체를 파라미터로 넘겨주면 th:replace
부분이 대체되는 것이다.
<html xmlns:th="http://www.thymeleaf.org"> <head th:replace="레이아웃경로/base :: common_header(~{::title},~{::link})"> <title>메인 타이틀</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"> </head> <body> 메인 컨텐츠 </body> </html>
결과
<!DOCTYPE html> <html> <head> <title>메인 타이틀</title> <!-- 공통 --> <link rel="stylesheet" type="text/css" media="all" href="/css/awesomeapp.css"> <link rel="shortcut icon" href="/images/favicon.ico"> <script type="text/javascript" src="/sh/scripts/codebase.js"></script> <!-- 추가 --> <link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/themes/smoothness/jquery-ui.css"> </head> <body> 메인 컨텐츠 </body> </html>
아니면 <html>
태그에 th:fragment
속성을 추가하여 사용할 수도 있다.
layoutFile.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>
layoutExtendMain.html
<!DOCTYPE html> <html th:replace="~{레이아웃경로/layoutFile :: layout(~{::title}, ~{::section})}" xmlns:th="http://www.thymeleaf.org"> <head> <title>메인 페이지 타이틀</title> </head> <body> <section> <p>메인 페이지 컨텐츠</p> <div>메인 페이지 포함 내용</div> </section> </body> </html>
결과
<!DOCTYPE html> <html> <head> <title>메인 페이지 타이틀</title> </head> <body> <h1>레이아웃 H1</h1> <section> <p>메인 페이지 컨텐츠</p> <div>메인 페이지 포함 내용</div> </section> <footer> 레이아웃 푸터 </footer> </body> </html>
layoutFile.html
을 보면 기본 레이아웃을 가지고 있는데, <html>
에 th:fragment
속성이 정의되어있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.
layoutExtendMain.html
는 현재 페이지인데, <html>
자체를 th:replace
를 사용해서 변경하는 것을 확인할 수 있다. 결국 layoutFile.html
에 필요한 내용을 전달하면서 <html>
자체를 layoutFile.html
로 변경한다.