스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - (2) 타임리프 기본기능 - 텍스트, 변수, 객체접근
인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술편을 학습하고 정리한 내용 입니다.
텍스트 - text, utext
타임 리프의 가장 기본 기능인 텍스트를 출력하는 기능을 알아보자.
타임리프는 기본적으로 HTML 태그의 속성에 기능을 정의해서 동작한다.
HTML의 콘텐츠(content)에 데이터를 출력할 때는 다음과 같이 th:text를 사용하면 된다.
<span th:text="${data}">
HTML 태그의 속성이 아니라 HTML 콘텐츠 영역 안에서 직접 데이터를 출력하고 싶다면
[[...]] 이런 식으로 사용
컨텐츠 안에서 직접 출력하기 = [[${data}]]
이제 직접 테스트 해보자.
hello.thymeleaf.basic.BasicController
1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("/text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "Hello Spring!");
return "basic/text-basic";
}
}
간단한 컨트롤러 만들었다.
resources/templates/basic/text-basic.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
<li>th:text 사용 <span th:text="${data}"></span></li>
<li>컨텐츠 안에서 직접 출력하기 = [[${data}]]</li>
</ul>
</body>
</html>
다음과 같이 두 케이스를 보면

잘 나오는 모습이다.
Escape
HTML 문서는 <, >같은 특수 문자를 기반으로 정의된다.
따라서 뷰 템플릿으로 HTML 화면을 생성할 때는 출력하는 데이터에 이러한 특수 문자가 있는 것을 주의해서 사용해야 한다.
앞에서 만든 예제의 데이터를 다음과 같이 변경해 보자.
1
model.addAttribute("data", "Hello <b>Spring!</b>");
나는 Hello Spring! 이런 식으로 출력 되길 원하지만, 실제로는

이런 식으로 그대로 나와버리고, 소스를 확인해 보면

치환돼서 나와버린다.
HTML 엔티티
웹 브라우저는 <를 HTML 태그의 시작으로 인식한다. 그래서 <를 태그의 시작이 아니라 문자로 표현할 수 있는 방법이 필요한데, 이것을 HTML 엔티티라 한다. 그리고 이렇게 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 이스케이프 (escape)라 한다.
그리고 타임리프가 제공하는 th:text, [[...]]는 기본적으로 이스케이프를 제공한다.
Unescape
이스케이프 기능을 사용하지 않으려면,
타임리프에선 다음 두 기능을 이용하자.
th:text→th:utext[[...]]→[(...)]
1
2
3
4
5
@GetMapping("/text-unescaped")
public String textUnescaped(Model model) {
model.addAttribute("data", "Hello <b>Spring!</b>");
return "basic/text-unescaped";
}
컨트롤러에 다음과 같이 추가하고,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>text vs utext</h1>
<ul>
<li>th:text = <span th:text="${data}"></span></li>
<li>th:utext = <span th:utext="${data}"></span></li>
</ul>
<h1><span th:inline="none">[[...]] vs [(...)]</span></h1>
<ul>
<li><span th:inline="none">[[...]] = </span>[[${data}]]</li>
<li><span th:inline="none">[(...)] = </span>[(${data})]</li>
</ul>
</body>
</html>
th:inline="none" : 타임리프는 [[...]] 를 해석하기 때문에, 화면에 [[...]] 글자를 보여줄 수 없다.
이 태그 안에서는 타임리프가 해석하지 말라는 옵션이다

내가 원하는 대로 나왔다.
주의!!
실제 서비스를 개발하다 보면 escape를 사용하지 않아서 HTML이 정상 렌더링 되지 않는 수 많은 문제가 발생한다. escape를 기본으로 하고, 꼭 필요할 때만 unescape를 사용하자.
변수 - SpringEL
타임리프에서 변수를 사용할 때는 변수 표현식을 사용한다.
변수 표현 식 : ${...}
그리고 이 변수 표현 식에는 스프링 EL이라는 스프링이 제공하는 표현 식을 사용할 수 있다.
BasicController 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@GetMapping("/variable")
public String variable(Model model) {
User userA = new User("userA", 10);
User userB = new User("userB", 20);
List<User> list = new ArrayList<>();
list.add(userA);
list.add(userB);
Map<String, User> map = new HashMap<>();
map.put("userA", userA);
map.put("userB", userB);
model.addAttribute("user", userA);
model.addAttribute("users", list);
model.addAttribute("userMap", map);
return "basic/variable";
}
@Data
static class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
모델에 다양한 자료구조(객체, 리스트, 맵)을 넣어서 타임리프에서 표현해 보자.
resources/templates/basic/variable.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>SpringEL 표현식</h1>
<ul>Object
<li>${user.username} = <span th:text="${user.username}"></span></li>
<li>${user['username']} = <span th:text="${user['username']}"></span></li>
<li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List
<li>${users[0].username} = <span th:text="${users[0].username}"></span></li>
<li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span></li>
<li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map
<li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
<li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
<li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
</body>
</html>

이제 하나씩 보자.
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를 사용하면 지역 변수를 선언해서 사용할 수 있다.
지역 변수는 선언한 태그 안에서만 사용할 수 있다.
1
2
3
4
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
기본 객체들
타임 리프는 기본 객체들을 제공한다.
${#request}- 스프링 부트 3.0부터 제공하지 않는다.${#response}- 스프링 부트 3.0부터 제공하지 않는다.${#session}- 스프링 부트 3.0부터 제공하지 않는다.${#servletContext}- 스프링 부트 3.0부터 제공하지 않는다.${#locale}
BasicController 추가
1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/basic-objects")
public String basicObjects(HttpSession session) {
session.setAttribute("sessionData", "Hello Session!");
return "basic/basic-objects";
}
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello " + data;
}
}
resources/templates/basic/basic-objects.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>request = <span th:text="${#request}"></span></li>
<li>response = <span th:text="${#response}"></span></li>
<li>session = <span th:text="${#session}"></span></li>
<li>servletContext = <span th:text="${#servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ul>
<h1>편의 객체</h1>
<ul>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ul>
</body>
</html>
결론부터 말하자면 스프링 부트 3.0 이상에선 오류난다.

스프링 부트 3.0 이상이라면 다음과 같이 작성한다. (미만이라면 위에 코드처럼 작성 가능)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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";
}
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello " + data;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>request = <span th:text="${request}"></span></li>
<li>response = <span th:text="${response}"></span></li>
<li>session = <span th:text="${session}"></span></li>
<li>servletContext = <span th:text="${servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ul>
<h1>편의 객체</h1>
<ul>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ul>
</body>
</html>
스프링 부트 3.0이라면 직접 model 에 해당 객체를 추가해서 사용해야 한다.

이제 잘 나온다.
편의 객체
1
2
3
4
5
6
<h1>편의 객체</h1>
<ul>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ul>
지금 호출 url이
- http://localhost:8080/basic/basic-objects?paramData=HelloParam
다음과 같다.
<span th:text="${param.paramData}"></span> 다음과 같이 호출할 수 있다.
정리
- HTTP 요청 파라미터 접근 :
param- 예)
${param.paramData}
- 예)
- HTTP 세션 접근 :
session- 예)
${session.sessionData}
- 예)
- 스프링 빈 접근 :
@- 예)
${@helloBean.hello('Spring !')}
- 예)
댓글남기기