이 글을 작성하고 난 뒤에는 내가 나중에 타임리프를 사용할 때에, 문법이 잘 기억이 나지 않을 때 이 글만 보아도 기본적인 타임리프 기능들을 사용할 수 있었으면 한다.
클라이언트 사이드 렌더링(Client Side Rendering, CSR)이 아닌 서버에서 동적으로 HTML을 만들어 렌더링하는 할수있는 템플릿
스프링에서는 공식으로 타임리프를 사용할 것을 권장하며 스프링의 다양한 기능을 편리하게 사용할 수 있게끔 제공한다.
최상단 HTML 코드에 다음과 같이 선언한다.
<html xmlns:th="http://www.thymeleaf.org">
@GetMapping("...")
public String ...(Model model) {
model.addAttribute("data", "Hello Spring!");
return "...";
}
<ul>
<li>th:text 사용 : <span th:text="${data}"></span></li>
<li>컨텐츠 안에서 직접 출력하기 : [[${data}]]</li>
</ul>
스프링에서 Model에 담아 넘겨줄때부터 직접 <b></b>
코드를 작성하여 넘겨주어 보자.
@GetMapping("...")
public String ...(Model model) {
model.addAttribute("data", "Hello <b>Spring!</b>");
return "...";
}
이렇게 하면 개발자가 의도한 바로는 "Spring!"이 강조 처리된 "Hello Spring!"이 출력되어야 하겠지만 사실은 Hello <b>Spring!<b>
가 출력된다. 웹브라우저는 '<' 를 HTML 태그의 시작으로 인식한다. 따라서 '<'를 HTML 태그가 아닌 단순히 문자로 표현할 수 있는 방법이 필요한데 이것을 HTML 엔티티라 한다. 그리고 이렇게 HTML에서 사용하는 특수문자를 HTML 엔티티로 변경하는 것을 이스케이프(escape)라 하는데 타임리프는 기본적으로 th:text, [[...]]을 통해 이스케이프(escape)를 제공한다.
<
>
그렇다면 이스케이프(escape)를 지원하지 않으려면 어떻게 해야할까? 이 역시 타임리프에서 th:utext, [(...)]를 통해 지원한다.
th:utext와 [(...)]을 통해 이스케이프 되지 않은 기능을 제공한다.
<ul>
<li>th:text 사용 : <span th:utext="${data}"></span></li>
<li>컨텐츠 안에서 직접 출력하기 : [(${data})]</li>
</ul>
이렇게 한다면 개발자가 의도한 대로 "Hello Spring!"이 출력되는 것을 확인할 수 있다.
타임리프에서 변수를 사용할 때는 변수 표현식을 사용한다. 변수 표현식은 다음과 같다. "${...}
"
그리고 이 변수 표현식에는 SpringEL이라는 스프링이 제공하는 표현식을 사용할 수 있다.
@GetMapping("...")
public String ...(Model model) {
User userA = new User("userA");
List<User> list = new ArrayList<>();
list.add(userA);
Map<String, Object> map = new Map<>();
map.put("userA", userA);
model.addAttribute("userObject", user);
model.addAttribute("userList", list);
model.addAttribute("userMap", map);
return "...";
}
class User {
private String name;
public User(String name) {
this.name = name;
}
}
<ul>
<li>${userObject.name} = <span th:utext="${userObject.name}"></span></li>
<li>${userObject.['name']} = <span th:utext=">${userObject.['name']}"></span></li>
<li>${userObject.getName()} = <span th:utext="${userObject.getName()}"></span></li>
</ul>
<ul>
<li>${userList[0].name} = <span th:utext="${userList[0].name}"></span></li>
<li>${userList[0].['name']} = <span th:utext=">${userList[0].['name']}"></span></li>
<li>${userList[0].getName()} = <span th:utext="${userList[0].getName()}"></span></li>
</ul>
<ul>
<li>${userMap['userA'].name} = <span th:utext="${userMap['userA'].name}"></span></li>
<li>${userMap['userA'].['name']} = <span th:utext=">${userMap['userA'].['name']}"></span></li>
<li>${userMap['userA'].getName()} = <span th:utext="${userMap['userA'].getName()}"></span></li>
</ul>
타임리프 내에서도 지역변수를 선언하여 사용할 수 있으며 선언한 태그안에서만 사용가능 하다.
<div th:with="person=${users[0]}"> <!-- (1) -->
<p>사람의 이름은 <span th:text="${person.name}"></span></p> <!-- (2) -->
</div>
person
이고 users[0]
을 값으로 가지는 지역변수 선언.person
이 users[0]
을 가지고 있으므로 변수표현식 ${person.name}
을 통하여 출력.person
변수는 div 내에서만 사용가능.사람의 이름은 userA
타임리프는 다음과 같은 기본 객체들을 제공한다.
${#request}
${#response}
${#session}
${#servletContext}
${#locale}
#request
는 HttpServletRequest
객체에 접근하듯 reqeust.geParameter("...")
와 같이 접근해야 하지만 이러한 점을 해결하기 위한 편의 객체를 제공한다.
<p th:text="${params.username}">username</p>
http 호출시 http://....?username=gwjeon
로 접속되었다면 username
이 parameter에 존재하기 때문에 출력 결과는 gwjeon
이 된다.
@GetMapping("...")
public String ...(HttpSession session) {
session.setAttribute("sessionData", "Hello Spring");
return "...";
}
<p th:text="${session.sessionData}">sessionData</p>
세션 접근도 지원한다. 컨트롤러에서 세션에 sessionData
를 key로 한 Hello Spring
문자열을 저장하였다. 출력 결과는 Hello Spring
이 된다.
스프링 빈에 접근하는것도 지원한다.
@Component("helloBean")
class HelloBean {
public String hello(String data) {
return "Hello " + data;
}
}
다음과 같은 스프링 빈이 있다고 가정 했을때
<p th:text="${@helloBean.hello('Hello Srping')}">springBean</p>
와 같이 한다면 출력 결과는 Hello Spring
이 된다. @helloBean
은 빈이름이 되며 첫글자는 소문자로 변경되어 표기하고 .hello
를 통해 빈에 등록된 메서드에 Hello Spring 문자열을 argument로 전달한다.
타임리프에서 지원하는 유틸리 객체는 다음과 같다.
#message
: 메시지, 국제화 처리#uris
: URI 이스케이프 지원#dates
: java.util.Date 서식 지원#calendars
: java.util.Calendar 서식 지원#temporals
: 자바8 날짜 서식 지원#numbers
: 숫자 서식 지원#strings
: 문자 관련 편의 기능#objects
: 객체 관련 기능 제공#bools
: boolean 관련 기능 제공#arrays
: 배열 관련 기능 제공#lists , #sets , #maps
: 컬렉션 관련 기능 제공#ids
: 아이디 처리 관련 기능 제공워낙 종류가 많으니 어떠한 것이 있는지만 기억하고 사용하기 전 매뉴얼을 참고하자.
타임리프에서 JAVA8 날짜인 LocalDate , LocalDateTime , Instant 를 사용하려면 추가 라이브러리 thymeleaf-extras-java8time
가 필요하며 스프링 부트 타임리프를 사용하면 해당 라이브러리가 자동으로 추가된다. JAVA8 날짜 유틸리티 객체는 #temporals
이다.
타임리프에서는 기본적으로 Url은 모두 @{}
안에 정의 되어야 한다.
@GetMapping("...")
public String ...(Model model) {
model.addAttribute("param1", "data1");
model.addAttribute("param2", "data2");
return "...";
}
<ul>
<li><a th:href="@{/hello}">basic url</a></li> <!-- (1) -->
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li> <!-- (2) -->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li> <!-- (3) -->
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li> <!-- (4) -->
</ul>
기본적으로 url 구조를 먼저 작성하고 뒤에 ()괄호안에 동적으로 들어갈 value를 할당하는 구조이다.
/hello
로 이동한다./hello?param1=data1¶m=data2
로 이동한다./hello/{...}/{...}
로 이동한다./hello/{...}?param2=data2
로 이동한다. 이 경우는 url상에 variable인 param1까지만 할당되고 param2는 해당되는것이 없음으로 자동으로 parameter로 할당된다.타임리프에는 다음과 같은 리터럴이 있다.
타임리프 문자 리터럴은 항상 '...'
작은 따옴표로 감싸야 한다.
<span th:text="hello"></span>
위 처럼 공백 없이 쭉 이어지는 문자라면 작은 따옴표를 생략할 수 있다.
<span th:text="hello spring!"></span> <!-- (1) -->
<span th:text="'hello spring!'"></span> <!-- (2) -->
하지만 매번 이렇게 리터럴을 작은 따옴표를 통하여 작성하는 것은 비효율적이다.
리터럴 대체문법을 사용하면 매우 간편하게 해결할 수 있다.
<span th:text="|hello spring!|"></span>
위 처럼 문자를 |...|
으로 감싸주면 작은 따옴표로 감싸지 않아도 된다. 여기서 의문이 든다.
"'hello spring!'"
이나 "|hello spring!|"
이나 똑같이 문자를 감싸야 하는건 동일한데 도대체 무엇이 다른것인가?
리터럴 대체문법은 마치 JavaScript의 리터럴 템플릿 문법과 같다.
예를들어 다음과 같은 controller가 있다고 가정하자.
@GetMapping("...")
public String ...(Model model) {
model.addAttribute("data", "Spring!");
return "...";
}
<span th:text="'hello ' + ${data}"></span> <!-- (1) -->
<span th:text="|hello ${data}|"></span> <!-- (2) -->
똑같은 hello Spring! 이라는 문자를 출력하는 내용이지만 2가지 방법으로 갈린다.
hello
문자 뒤에 공백을 추가했다.|...|
로 감싸기만 했을 뿐인데 마치 문자를 사용하듯 편리하다. |...|
안에 있는 공백을 포함한 문자들을 출력하고 ${data}
변수부분은 Spring!
로 치환된다.