반응형
타임리프 소개
- 서버 사이드 HTML 렌더링 (
SSR
) : 백엔드 서버에서 HTML을 동적으로 렌더링하는 용도로 사용된다. - 네츄럴 템플릿 : 순수 HTML을 최대한 유지하는 특징 (정적, 동적 둘다 사용 가능)
- 스프링 통합 지원 : 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원
타임리프 표현식
<html xmlns:th="http://www.thymeleaf.org">
- html 태그에 타임리프를 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
- gradle에 타임리프 라이브러리 추가
간단한 표현
- 변수 표현식: ${...}
- 선택 변수 표현식: *{...}
- 메시지 표현식: #{...}
- 링크 URL 표현식: @{...}
- 조각 표현식: ~{...}
리터럴
- 텍스트: 'one text', 'Another one!',...
- 숫자: 0, 34, 3.0, 12.3,...
- 불린: true, false
- 널: null
- 리터럴 토큰: one, sometext, main,...
- 문자 연산:
- 문자합치기:+
- 리터럴 대체: |The name is ${name}|
- 산술 연산:
- Binary operators: +, -, *, /, %
- Minus sign (unary operator): -
- 불린 연산:
- Binary operators: and, or
- Boolean negation (unary operator): !, not
- 비교와 동등:
- 비교:>,<,>=,<=(gt,lt,ge,le)
- 동등 연산: ==, != (eq, ne)
- 조건 연산:
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
- 특별한 토큰:
- No-Operation: _
텍스트 - text, utext
- 타임리프는 HTML의 콘텐츠에 데이터를 출력할 때
th:text
를 사용한다. - 컨텐츠 안에서 직접 출력하고 싶은 경우
[[${data}]]
를 사용한다. - 타임리프는
[[]]
를 해석하기 때문에th:inline="none"
으로 해석하지 말라는 옵션을 지정할 수 있다.
Escape vs Unescape
- html은
<
,>
를 태그의 시작으로 인식한다. 해당 문자들을 문자로 표현할 수 있는 방법이 필요한데, 이를 HTML 엔티티라 한다. 즉, 특수 문자를 HTML 엔티티로 변경하는 것을 Escape라고 한다. 타임리프에는th:text
,[[...]]
에서 기본적으로 이스케이프를 제공한다. - 즉, HTML 엔티티를 사용하지 않아야 원하는 태그를 사용할 수 있고 이것을 Unescape라고 한다. 타임리프에서는
th:utext
,[()]
를 제공하여 unescape를 사용한다. 실제 서비스를 개발하다보면 escape를 사용하지 않아서 HTML이 정상 렌더링이 되지 않는 현상이 발생하므로 필요할 때만 unescape를 사용하자.
SpringEL
- 변수 표현식에는 스프링 EL이라는 스프링이 제공하는 표현식을 이용할 수 있다.
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
th:with
를 사용하면 지역 변수를 선언해서 해당 태그 안에서 사용할 수 있다.
타임리프 객체
${#request}
,${#response}
,${#session}
,${#servletContext}
,${locale}
등의 객체를 제공한다. 하지만#request
는HttpServletRequest
객체를 그대로 제공하기 때문에 데이터를 조회하려면request.getParameter("data")
처럼 예전 방식으로 접근해야 한다. 이런 불편한 부분들을 해결하기 위해 편의 객체도 제공한다.- 편의 객체
- HTTP 요청 파라미터 접근 :
param
- HTTP 세션 접근 :
session
- 스프링 빈 접근 :
@
- HTTP 요청 파라미터 접근 :
유틸리티 객체와 날짜
#message
: 메시지, 국제화 처리#uris
: URI 이스케이프 지원#dates
:java.util.Date
서식 지원#calendars
:java.util.Calendar
서식 지원#temporals
: 자바 8 날짜 서식 지원#numbers
: 숫자 서식 지원#strings
: 문자 관련 편의 기능#objects
: 객체 관련 기능 제공#bools
: boolean 관련 기능 제공#arrays
: 배열 관련 기능 제공#lists
,#sets
,maps
: 컬렉션 관련 기능 제공#ids
: 아이디 처리 관련 기능 제공
URL 링크
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>URL 링크</h1>
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<!-- http://localhost:8080/hello?param1=data1¶m2=data2 -->
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
<!-- http://localhost:8080/hello/data1/data2 -->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<!-- http://localhost:8080/hello/data1?param2=data2-->
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
</body>
</html>
- 타임리프에서 URL을 생성할 때는
@{...}
문법을 사용하면 된다.
리터럴
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <h1>리터럴</h1>
<ul>
<!--주의! 다음 주석을 풀면 예외가 발생함-->
<!-- <li>"hello world!" = <span th:text="hello world!"></span></li>-->
<li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li> <li>'hello world!' = <span th:text="'hello world!'"></span></li>
<li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li> <li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
</ul>
</body>
</html>
- 리터럴이란 소스 코드 상에서 고정된 값을 말하는 용어
String a = "Hello"
: 문자 리터럴 ,10
: 숫자 리터럴- 타임리프에서 문자리터럴은 항상 ``(백틱)
으로 감싸야 한다. 단, 공백 없이 이어져있다면 토큰으로 인식해준다. 공백이 있는경우 백틱보다는
|...|`을 사용하자.
연산
- HTML 엔티티 :
>
: gt,<
: lt,>=
: ge,<=
: le,!
: not,==
: eq ,!=
: neq,ne - No-operation 인
_
를 사용하면 타임리프가 실행하지 않는 것처럼 동작하게 만들어서 HTML의 값을 그대로 출력할 수 있다.
속성 값 설정
- 타임리프는
th:*
를 사용하는데, 기존 속성을 대체해서 사용할 수 있다. 즉,<input type="text" name="mock" th:name="userA" />
라는 문장이 있을 때name="mock"
을th:name="userA"
로 대체한다.
반복
<!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>
- 타임리프에서 반복은
th:each
를 사용한다.th:each="user, userStat : ${users}"
에서 두 번째 파라미터는 현재user
의 상태 (status)에 대한 정보를 얻을 수 있다.
조건부 평가
<!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>
th:if
,th:unless
조건을 통해 해당 조건을 만족하지 않으면 해당 라인을 적용시키지 않는다.`th:switch
에서th:case
를 통해 조건을 넣으며*
은 해당 조건이 없을 때 적용될 값을 의미한다.
블록
div
가 여러 개 즉, 여러 블록을 하나의 블록으로 처리하고 싶을 때th:block
을 사용하여 처리할 수 있다. 렌더링 결과에는th:block
은 사라져 있다.
자바스크립트 인라인
<script th:inline="javascript">
를 사용하면 타임리프에서 자바스크립트의 인라인 기능을 사용할 수 있다. 해당 기능을 사용하면 문자열은""
를 객체는 Json 타입으로 변환해서 적용시켜 준다. 즉, 자바스크립트 타입 관리를 직접 해준다.
<!-- 자바스크립트 인라인 each --> <script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
[#]...[/]
를 사용해서 자바스크립트 안에서each
를 사용할 수 있다.
템플릿 조각
- 웹 페이지를 개발할 때는 공통 영역이 많이 있다. 예를 들어 상단 영역이나 하단 영역, 좌측 카테고리 등 여러 페이지에서 함께 사용하는 영역들이 있다. 타임리프에서는 이런 부분을 지원해주는 템플릿 조각, 레이아웃 기능이 있다.
th:조건= "~{/path :: 템플릿 조각명}"
으로 사용::
은 해당 영역을 템플릿 조각으로 가져와서 사용한다는 의미이다.th:insert
: 현재 태그div
내부에 추가th:replace
: 현재 태그div
를 대체:: 템플릿 조각명 ('파라미터1', '파라미터2', ...)
: 파라미터를 전달해서 동적으로 조각을 렌더링 할 수도 있다.
템플릿 레이아웃
- 공통으로 사용하지만 각 페이지마다 필요한 정보가 필요한 경우
th:fragment
로 지정해서 원하는 태그를 넣어줄 수 있다.
- 동일한 방식으로
html
태그에th:fragment
를 통해 html 전체에 적용할 수도 있다.
본 포스팅은 인프런 - 김영한님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 참고하였습니다.
반응형