Thymeleaf
▪︎ Thymeleaf란?
- View Template Engine으로, JSP, Freemarkerd와 같이 서버에서 클라이언트에게 응답할 브라우저 화면을 만들어주는 역할
▪︎ Thymeleaf의 목표
- 주요 목표 : 템플릿을 만들 때 유지관리가 쉽도록 하는 것
→ 디자인 프로토타입으로 사용되는 템플릿에 영향을 미치지 않는 방식인 Natural Templates을 기반으로 함
- Natural Templates : 기존 HTML 코드와 구조를 변경하지 않고 덧붙이는 방식
▪︎ Thymeleaf의 장점
- 코드를 변경하지 않기 때문에 디자인 팀과 개발 팀 간의 협업이 편리해짐
- JSP와 달리, Servlet Code로 변환되지 않기 때문에 비즈니스 로직과 분리되어 오로지 View에 집중할 수 있음
- 서버 상에서 동작하지 않아도 되기 때문에, 서버 동작 없이 화면 확인 가능 → 더미 데이터를 넣고 화면 디자인 및 테스트에 용이함
▪︎ Thymeleaf와 Spring Boot
- 위와 같은 타임리프 장점때문에 Spring에서도 Spring Boot와 타임리프를 함께 사용하는 것을 권장함
- Spring Boot에서는 JSP 사용 시 호환 및 환경 설정에 어려움이 많기 때문
반대로 타임리프는 간편하게 Dependency 추가 작업으로 사용 가능
▪︎ Thymeleaf의 특징
1. 서버 사이드 HTML 렌더링 ( SSR )
- 타임리프는 백엔드 서버에서 HTML을 동적으로 렌더링하는 용도로 사용됨
- 사용법은 SSR이 다 비슷하기에 학습하기에도 어렵지 않고, 페이지가 어느 정도 정적이고 빠른 생산성이 필요한 경우 백엔드 개발자가 개발해야 하는 일이 생기는데 이 경우 타임리프는 좋은 선택지임
2. 네츄럴 템플릿
- 타임리프는 순수한 HTML을 최대한 유지하려는 특징이 있음
→ 이게 JSP와의 큰 차이점!
- 타임리프로 작성한 파일은 확장자도 '.html'이고, 웹 브라우저에서 직접 파일을 열어도 내용 확인 가능
→ 물론 이 경우 동적인 결과 렌더링은 되지 않지만, HTML 마크업 언어가 어떻게 되는지 확인 가능
3. 스프링 통합 지원
- 타임리프는 스프링과 자연스럽게 통합되어 스프링의 다양한 기능을 쉽게 사용 가능
1. 타임리프 사용 선언
▪︎ 타임리프를 사용하기 위해서는 적용할 HTML 문서에 네임스페이스 추가
<html xmlns:th="http://www.thymeleaf.org">
- xmlns:th
: 타임리프의 th속성을 사용하기 위해 선언된 네임스페이스
순수 HTML로만 이루어진 페이지의 경우, 선언하지 않아도 무관함
2. 타임리프 표현식과 문법
▪︎ 표현식 : 전달받은 데이터를 사용자들이 볼 수 있는 뷰로 만들기 위해 사용됨
표현식 | 설명 |
${ ... } | 변수의 값 표현식 |
#{ ... } | 속성 파일 값 표현식 |
@{ ... } | URL 표현식 |
*{ ... } | 선택한 변수의 표현식 th:object에서 선택한 객체에 접근 |
▪︎ 문법
표현식 | 설명 | 예제 |
th:text | 텍스트를 표현할 때 사용 | th:text=${ person.name } |
th:each | 컬렉션을 반복할 때 사용 | th:each="person : ${ persons }" |
th:if | 조건이 true인 때만 표시 | th:if="${ person.age } >= 20" |
th:unless | 조건이 false인 때만 표시 | th:unless="${ person.age } >= 20" |
th:href | 이동 경로 | th:href="@{ /person(id=${ person.id })}" |
th:with | 변수값으로 지정 | th:with="name=${ person.name }" |
th:object | 선택한 객체로 지정 | th:object="${ person }" |
3. 텍스트 - text, utext
🔹 서버에서 Model에 담아준 각종 속성(attribute)들을 서버사이드 템플릿 엔진인 타임리프에서는 여러 방법으로 표현 가능
1) 텍스트 출력 기능
① HTML 태그의 콘텐츠에 직접 출력하는 기능 - th:text
② 콘텐츠 안에서 직접 출력하는 기능 - [[ ... ]]
▪︎ ExampleController.java
package org.choongang.ewha.hithymeleaf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ExampleController {
@GetMapping("/example")
public void example(Model model) {
model.addAttribute("data", "Hello Spring!");
}
}
▪︎ Example.html
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>텍스트 출력 예시</h1>
<ul>
<li>① th:text 사용 <span th:text="${ data }"></span></li>
<li>② 컨텐츠 안에서 직접 출력하기 [[${ data }]]</li>
</ul>
</body>
</html>
▪︎ 실행결과
2) Escape
🔹 만약 서버에서 속성으로 추가할 데이터에 HTML 태그를 추가하여 타임리프에서 태그효과까지 같이 사용하고 싶다면 어떻게 해야할까?
→ 컨트롤러에서 "Hello Spring!"이 아닌 "<b> Hello Spring! </b>"을 데이터로 전달해보자
( <b> 태그 : 다른 부가적인 목적 없이 단순히 굵게 표현하는 텍스트를 정의할 때 사용 )
▪︎ ExampleController.java
package org.choongang.ewha.hithymeleaf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ExampleController {
@GetMapping("/example")
public void example(Model model) {
model.addAttribute("data", "<b>Hello Spring!</b>");
}
}
▪︎ 실행결과
: 의도와는 다르게 태그가 그대로 출력되었음
▪︎ 페이지 소스 보기
: <b> 태그가 <b>로 되어 있음
- 타임리프가 제공하는 th:text, [[ ... ]]는 기본적으로 이스케이프(escape)를 제공
( ∵ 특수문자로 인하여 HTML이 깨지는 것을 방지하기 위해 지원됨 )
• HTML 엔티티
: HTML에서 사용하는 특수 문자( ex. <, > )를 태그의 시작이 아닌 문자로 표현할 수 있도록 &It;, > 와 같이 변경됨
• 이스케이프
: HTML에서 사용하는 특수 문자을 HTML 엔티티로 변경하는 것을 의미
HTML 태그 | 문자 표현 |
< | &It; |
> | > |
3) Unescape
🔹 HTML 엔티티로 변경하지 않고 HTML 태그로 사용하고 싶다면 어떻게 해야할까? Unescape!
- 이스케이프 기능을 사용하지 않는 방법
escape 사용 O | escape 사용 X |
th:text | th:utext |
[[ ... ]] | [( ... )] |
▪︎ Example.html
※ 참고
th:inline - 이 태그 안에 있는 내용은 타임리프가 해석하지 말라는 의미의 옵션
<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>
▪︎ 실행결과
4. 변수 - SpringEL
- 기본적으로 변수 표현식은 ${ ... } 으로 사용하여 단순히 값을 표시
→ 이 변수 표현식에는 SpringEL이라는 스프링이 제공하는 표현식을 사용할 수 있음
1) SpringEL 표현식
• 단순한 변수일 경우, ${ data }로 바로 표현 가능하지만, Object나 List같은 객체의 경우 다음과 같이 사용함
◎ Object
• data.field : data의 field 프로퍼티에 접근 ( data.getField( ) )
• data['field'] : 위와 동일 ( data.getField( ) )
• data.getField( ) : data의 getField( ) 메서드를 직접 호출 가능
◎ List
• list[0].field : List의 첫 번째 데이터를 찾아 field 프로퍼티에 접근
• list[0]['field'] : 위와 동일
• list[0].getField( ) : List에서 첫 번째 데이터를 찾아 메서드를 직접 호출 가능
• list.get(0).xxx : List의 get 메서드를 통해 데이터를 찾아 프로퍼티에 접근 가능
◎ Map
• map['key'].field : Map에서 key를 찾아 field 프로퍼티에 접근
• map['key']['field'] : 위와 동일
• map['key'].getField( ) : Map에서 key를 찾아 직접 메서드 호출 가능
2) 지역 변수 선언
- th:with를 이용해 지역변수를 사용 가능
단, 지역변수이므로 선언한 태그 안에서만 사용 가능
▪︎ variable.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</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>
<div th:with="item=${users[0]}">
<ul>
<li>이름 : <span th:text="${item.username}"></span></li>
<li>나이 : [[${item.age}]]</li>
</ul>
</div>
</body>
</html>
▪︎ ExampleController.java
@GetMapping("/variable")
public String variable(Model model) {
// 1. Object
User userA = new User("userA", 10);
User userB = new User("userB", 20);
// 2. List
List<User> list = new ArrayList<>(Arrays.asList(userA, userB));
// * Arrays.asList() 메서드 : 배열을 리스트로 변환
// 3. Map
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;
}
}
▪︎ 실행결과
5. 기본 객체들
1) 타임리프에서 제공하는 기본 객체들
객체 | 설명 |
${ #request } | HttpRequest 객체 접근 |
${ #response } | HttpResponse 객체 접근 |
${ #locale } | Locale 객체 접근 |
${ #session } | HttpSession 객체 접근 |
${ #servletContext } | ServletContext 객체 접근 |
2) 기본객체에 대한 접근 편의 메서드
- 기본 객체의 프로퍼티 접근을 하기 위해서 편의 메서드가 없다면,
request.getParameter( "data" ) 와 같이 호출해야 함
- 타임리프에서는 편의 메서드를 제공함
• HTTP 요청 파라미터 접근 : param
⇒ ex ) ${ param.paramData }
• HTTP 세션 접근 : session
⇒ ex ) ${ session.sessionData }
• 스프링 빈 접근 : @
⇒ ex ) ${ @helloBean.hello('Spring') }
▪︎ ExampleController.java
@GetMapping("/basic-objects")
public String basicObject(HttpSession httpSession) {
httpSession.setAttribute("sessionData", "Hello Session");
return "basic/basic-objects";
}
@Component("helloBean")
// * @Component 애너테이션이 있는 클래스는 빈으로 등록됨
static class HelloBean{
public String hello(String data) {
return "Hello " + data ;
}
}
▪︎ objects.html
<!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>
'백엔드 > Spring Boot' 카테고리의 다른 글
7. 블로그 화면 구성하기 (2) - 블로그 글 목록 뷰 구현하기 (0) | 2023.08.04 |
---|---|
타임리프 (0) | 2023.08.04 |
7. 블로그 화면 구성하기 (1) - 타임리프 (0) | 2023.08.02 |
6. 블로그 기획하고 API 만들기 (3) - 블로그 글 작성을 위한 API 구현❸ (1) | 2023.07.31 |
6. 블로그 기획하고 API 만들기 (3) - 블로그 글 작성을 위한 API 구현❷ (0) | 2023.07.27 |