* 공식메뉴얼 - 기본기능
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
Tutorial: Using Thymeleaf
1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide a
www.thymeleaf.org
* 공식메뉴얼 - 스프링 통합
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
Tutorial: Thymeleaf + Spring
Preface This tutorial explains how Thymeleaf can be integrated with the Spring Framework, especially (but not only) Spring MVC. Note that Thymeleaf has integrations for both versions 3.x and 4.x of the Spring Framework, provided by two separate libraries c
www.thymeleaf.org
1. 타임리프 특징
- 서버 사이드 HTML 랜더링 (SSR) : 백엔드 서버에서 HTML을 동적으로 렌더링하는 용도
- 네츄럴 템플릿(natural templates) : 순수 HTML을 최대한 유지하면서 뷰 템플릿도 사용 > JSP는 브라우저에서 열면 깨짐.
- 스프링 통합지원
2. 텍스트 - text, utext
<li>th:text 사용 <span th:text="${data}"></span></li>
<li>컨텐츠 안에서 직접 출력하기 = [[${data}]]</li>
* Escape
- 이스케이프 : HTML에서 사용하는 특수 문사를 HTML엔티티로 변경하는 것.
model에 < / > 를 넣으면, < / > 로 데이터가 출력됨.
타임리프는 화면에 출력할때, 이스케이프를 제공함 (HTML엔티티로 인식해서 출력)
* Unescape
<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>
3. 변수 - SpringEL
@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);
}
<!-- 아래 전부 "userA" 출력됨 -->
<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>
<!-- 지역변수는 선언한 scope 내에서만 사용가능 -->
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
4. 기본 객체들
// spring boot 3.0 이상 sample
public String basicObjects(Model model, HttpServletRequest request,
HttpServletResponse response, HttpSession session) {
//user가 웹브라우저를 종료하기 전까지는 유지되는 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;
}
}
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>request = <span th:text="${request}"></span></li>
<!-- request = org.apache.catalina.connector.RequestFacade@294edf1a -->
<li>response = <span th:text="${response}"></span></li>
<!-- response = org.apache.catalina.connector.ResponseFacade@16dd622d -->
<li>session = <span th:text="${session}"></span></li>
<!-- session = org.apache.catalina.session.StandardSessionFacade@28a6393a -->
<li>servletContext = <span th:text="${servletContext}"></span></li>
<!-- servletContext = org.apache.catalina.core.ApplicationContextFacade@78fe897d -->
<li>locale = <span th:text="${#locale}"></span></li>
<!-- locale = ko -->
</ul>
<!-- url : localhost:8080/basic/basic-objects?paramData=HelloParam -->
<h1>편의 객체</h1>
<ul>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<!-- Request Parameter = HelloParam -->
<li>session = <span th:text="${session.sessionData}"></span></li>
<!-- session = Hello Session -->
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
<!-- spring bean = Hello Spring! -->
</ul>
5. 유틸리티 객체와 날짜
- 문자, 숫자, 날짜, URI 등을 편리하게 다루는 다양한 유틸리티 객체들을 제공.
* 유틸리티 객체 예시
Tutorial: Using Thymeleaf
1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide a
www.thymeleaf.org
* 타임리프 자바8 날짜 지원 라이브러리 : thymeleaf-extras-java8time
* 스프링부트 3.2 이상은 타임리프 자바8 날짜 지원 라이브러리가 이미 포함되어있음.
* 날짜
@GetMapping("/date")
public String date(Model model) {
model.addAttribute("localDateTime", LocalDateTime.now());
return "basic/date";
}
<h1>LocalDateTime</h1>
<ul>
<li>default = <span th:text="${localDateTime}"></span></li>
<!-- default = 2021-06-02T23:03:22.372328 -->
<li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span></li>
<!-- yyyy-MM-dd HH:mm:ss = 2021-06-02 23:03:22 -->
</ul>
<h1>LocalDateTime - Utils</h1>
<ul>
<li>${#temporals.day(localDateTime)} = <span th:text="${#temporals.day(localDateTime)}"></span></li>
<!-- ${#temporals.day(localDateTime)} = 2 -->
<li>${#temporals.month(localDateTime)} = <span th:text="${#temporals.month(localDateTime)}"></span></li>
<!-- ${#temporals.month(localDateTime)} = 6 -->
<li>${#temporals.monthName(localDateTime)} = <span th:text="${#temporals.monthName(localDateTime)}"></span></li>
<!-- ${#temporals.monthName(localDateTime)} = 6월 -->
<li>${#temporals.monthNameShort(localDateTime)} = <span th:text="${#temporals.monthNameShort(localDateTime)}"></span></li>
<!-- ${#temporals.monthNameShort(localDateTime)} = 6월 -->
<li>${#temporals.year(localDateTime)} = <span th:text="${#temporals.year(localDateTime)}"></span></li>
<!-- ${#temporals.year(localDateTime)} = 2021 -->
<li>${#temporals.dayOfWeek(localDateTime)} = <span th:text="${#temporals.dayOfWeek(localDateTime)}"></span></li>
<!-- ${#temporals.dayOfWeek(localDateTime)} = 3 -->
<li>${#temporals.dayOfWeekName(localDateTime)} = <span th:text="${#temporals.dayOfWeekName(localDateTime)}"></span></li>
<!-- ${#temporals.dayOfWeekName(localDateTime)} = 수요일 -->
<li>${#temporals.dayOfWeekNameShort(localDateTime)} = <span th:text="${#temporals.dayOfWeekNameShort(localDateTime)}"></span></li>
<!-- ${#temporals.dayOfWeekNameShort(localDateTime)} = 수 -->
<li>${#temporals.hour(localDateTime)} = <span th:text="${#temporals.hour(localDateTime)}"></span></li>
<!-- ${#temporals.hour(localDateTime)} = 23 -->
<li>${#temporals.minute(localDateTime)} = <span th:text="${#temporals.minute(localDateTime)}"></span></li>
<!-- ${#temporals.minute(localDateTime)} = 3 -->
<li>${#temporals.second(localDateTime)} = <span th:text="${#temporals.second(localDateTime)}"></span></li>
<!-- ${#temporals.second(localDateTime)} = 22 -->
<li>${#temporals.nanosecond(localDateTime)} = <span th:text="${#temporals.nanosecond(localDateTime)}"></span></li>
<!-- ${#temporals.nanosecond(localDateTime)} = 372328000 -->
</ul>
6. URL 링크
@GetMapping("link")
public String link(Model model){
model.addAttribute("param1", "data1");
model.addAttribute("param2", "data2");
return "basic/link";
}
<h1>URL 링크</h1>
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<!-- /hello -->
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
<!-- /hello?param1=data1¶m2=data2 -->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<!-- /hello/data1/data2 -->
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
<!-- /hello/data1?param2=data2 -->
/hello <!-- 절대경로 -->
hello <!-- 상대경로 -->
</ul>
7. 리터럴(Literals)
- 소스 코드상에서 고정된 값 (문자, 숫자, 불린, null)
- 타임리프에서 문자 리터럴은 항상 ' (작은따옴표)로 감싸야함.
But, 공백 없이 쭉 이어진다면 하나의 의미있는 토큰으로 인지해서 생략가능
@GetMapping("/literal")
public String literal(Model model) {
model.addAttribute("data", "Spring!");
return "basic/literal";
}
<span th:text="'hello'"> (O)
<span th:text="hello"> (O)
<span th:text="hello world"> (X)
<span th:text="'hello world'"> (O)
<li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li>
<!-- hello world! -->
<li>'hello world!' = <span th:text="'hello world!'"></span></li>
<!-- hello world! -->
<li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li>
<!-- hello Spring! -->
<li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
<!-- hello Spring! -->
8. 연산
* 비교연산(HTML 엔티티를 사용해야 하는 부분에 주의* )
> (gt)
< (lt)
>= (ge)
<= (le)
! (not)
== (eq)
!= (neq, ne)
@GetMapping("/operation")
public String operation(Model model) {
model.addAttribute("nullData", null);
model.addAttribute("data", "Spring!");
return "basic/operation";
}
<ul>
<li>산술 연산
<ul>
<li>10 + 2 = <span th:text="10 + 2"></span></li>
<!-- 10 + 2 = 12 -->
<li>10 % 2 == 0 = <span th:text="10 % 2 == 0"></span></li>
<!-- 10 % 2 == 0 = true -->
</ul>
</li>
<li>비교 연산
<ul>
<li>1 > 10 = <span th:text="1 > 10"></span></li>
<!-- 1 > 10 = false -->
<li>1 gt 10 = <span th:text="1 gt 10"></span></li> <!-- 크다 -->
<!-- 1 gt 10 = false -->
<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
<!-- 1 >= 10 = false -->
<li>1 ge 10 = <span th:text="1 ge 10"></span></li> <!-- 크거나 같다 -->
<!-- 1 ge 10 = false -->
<li>1 == 10 = <span th:text="1 == 10"></span></li>
<!-- 1 == 10 = false -->
<li>1 != 10 = <span th:text="1 != 10"></span></li>
<!-- 1 != 10 = true -->
</ul>
</li>
<li>조건식
<ul>
<li>(10 % 2 == 0)? '짝수':'홀수' = <span th:text="(10 % 2 == 0)? '짝수':'홀수'"></span></li>
<!-- (10 % 2 == 0)? '짝수':'홀수' = 짝수 -->
</ul>
</li>
<li>Elvis 연산자 (조건식의 편의 버전)
<ul>
<li>${data}?: '데이터가 없습니다.' = <span th:text="${data}?: '데이터가 없습니다.'"></span></li>
<!-- ${data}?: '데이터가 없습니다.' = Spring! -->
<li>${nullData}?: '데이터가 없습니다.' = <span th:text="${nullData}?: '데이터가 없습니다.'"></span></li>
<!-- ${nullData}?: '데이터가 없습니다.' = 데이터가 없습니다. -->
</ul>
</li>
<li>No-Operation (_ 인경우 마치 타임리프가 실행되지 않은 것 처럼 동작)
<ul>
<li>${data}?: _ = <span th:text="${data}?: _">데이터가 없습니다.</span></li>
<!-- ${data}?: _ = Spring! -->
<li>${nullData}?: _ = <span th:text="${nullData}?: _">데이터가 없습니다.</span></li>
<!-- ${nullData}?: _ = 데이터가 없습니다. -->
</ul>
</li>
</ul>
9. 속성값 설정
- th:* 속성을 지정하면 타임리프는 기존속성을 th:* 로 지정한 속성으로 대체하고, 기존속성이없으면 새로 만들어줌.
@GetMapping("/attribute")
public String attribute() {
return "basic/attribute";
}
<h1>속성 설정</h1>
<input type="text" name="mock" th:name="userA" />
<!-- <input type="text" name="userA" /> -->
<h1>속성 추가</h1>
<!-- attrappend/attrprepend 는 띄어쓰기 안하면 합체됨.. -->
- th:attrappend = <input type="text" class="text" th:attrappend="class=' large'" /><br/>
<!-- - th:attrappend = <input type="text" class="text large" /><br/> -->
- th:attrprepend = <input type="text" class="text" th:attrprepend="class='large '" /><br/>
<!-- - th:attrprepend = <input type="text" class="large text" /><br/> -->
- th:classappend = <input type="text" class="text" th:classappend="large" /><br/>
<!-- - th:classappend = <input type="text" class="text large" /><br/> -->
<h1>checked 처리</h1>
- checked=false <input type="checkbox" name="active" checked="false" /><br/>
<!-- checked에 true가 들어가든, false가 들어가든 html은 checked가 있으므로 체크됨. -->
<!-- - checked=false <input type="checkbox name="active" checked="false" /><br/> -->
- checked o <input type="checkbox" name="active" th:checked="true" /><br/>
<!-- th:checked="${isChecked}" 이런식으로 쓸수 있음. -->
<!-- - checked o <input type="checkbox name="active" checked="checked" /><br/> -->
- checked x <input type="checkbox" name="active" th:checked="false" /><br/>
<!-- - checked X <input type="checkbox name="active" /><br/> -->
10. 반복
- th:each 를 사용(List, 배열, java.util.Iterable, java.util.Enumeration, Map)
@GetMapping("/each")
public String each(Model model){
addUsers(model);
return "basic/each";
}
private voide addUsers(Model model){
List<User> list = new ArrayList<>();
list.add(new User("UserA", 10));
list.add(new User("UserB", 20));
list.add(new User("UserC", 30));
model.addAttribute("users", list);
}
<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>
<!--
username | age
UserA | 10
UserB | 20
UserC | 30
-->
<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}">
<!-- userStat 생략가능. 생략하면 지정한변수명 + Stat 이 됨 -->
<!-- <tr th:each="user: ${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> <!-- 0부터 시작하는 값 -->
count = <span th:text="${userStat.count}"></span> <!-- 1부터 시작하는 값 -->
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>
<!--
count | username | age | etc
1 | UserA | 10 | index = 0 count = 1 size = 3 even? = false odd? = true first = true last? = false current = BasicController.User(username=UserA, age=10)
2 | UserB | 20 | index = 1 count = 2 size = 3 even? = true odd? = false first = false last? = false current = BasicController.User(username=UserB, age=20)
3 | UserC | 30 | index = 2 count = 3 size = 3 even? = false odd? = true first = false last? = true current = BasicController.User(username=UserC, age=30)
-->
11. 조건부 평가
- 타임리프의 조건식 : if, unless(if의 반대), switch
- 조건에 만족하지 않으면 해당태그 자체가 사라짐.
@GetMapping("/condition")
public String condition(Model model){
addUsers(model);
return "basic/condition";
}
<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>
<!--
count | username | age
1 | UserA | 10 미성년자 미성년자
2 | UserB | 20
3 | UserC | 30
-->
<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>
<!--
count | username | age
1 | UserA | 10살
2 | UserB | 20살
3 | UserC | 기타
-->
12. 주석
<!-- 화면에는 모든 주석 내용이 출력되지 않음. F5로 확인했을때 다름 -->
<h1> 1. 표준 HTML 주석(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>
/*/-->
13. 블록
- 타임리프 자체 태그
- <th:block> 은 랜더링시 제거됨.
@GetMapping("/block")
public String block(Model model){
addUsers(model);
return "basic/block";
}
<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>
<!--
사용자 이름1 UserA 사용자나이1 10
요약 UserA / 10
사용자 이름1 UserB 사용자나이1 20
요약 UserB / 20
사용자 이름1 UserC 사용자나이1 30
요약 UserC / 30
-->
14. 자바스크립트 인라인
- 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능 제공
- <script th:inline="javascript">
@GetMapping("/javascript")
public String javascript(Model model){
model.adddAttribute("user", new User("UserA", 10));
addUsers(model);
return "basic/javascript";
}
<!-- 자바스크립트 인라인 사용 전 -->
<script>
var username = [[${user.username}]]; //error : "[[${user.username}]]" 이라고 써야함.
var age = [[${user.age}]];
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
// var username2 = /*UserA*/ "test username";
//객체
var user = [[${user}]];
//var user = BasicController.User(username=UserA, age=10);
</script>
<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
var username = [[${user.username}]]; //var username = "UserA"; 알아서 문자를 ""안에 넣어줌.
var age = [[${user.age}]]; //var age = 10;
//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";
//var username2 = "UserA";
//객체 ( 객체를 JSON으로 변환해줌 )
var user = [[${user}]];
//var user = {"username":"UserA", "age":10};
</script>
* 자바스크립트 인라인 each
<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>
*/
15. 템플릿 조각
- 웹 페이지를 개발할때 공통영역을 처리하기 위한 기능
java/hello/thymeleaf/basic/TemplateController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/template")
public class TemplateController {
@GetMapping("fragment")
public String template() {
return "template/fragment/fragmentMain";
}
}
templates/template/fragment/footer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="copy">
푸터 자리 입니다.
</footer>
<footer th:fragment="copyParam (param1, param2)">
<p>파라미터 자리 입니다.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
</body>
</html>
templates/template/fragment/fragmentMain.html
- insert : 현재의 태그 안에 copy된 fragment가 들어감.
- replace : 현재의 태그를 copy된 fragment로 교체함.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>부분 포함</h1>
<!-- div 안에 fragment 출력 -->
<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<!-- div 는 사라지고 fragment 로 대체됨 -->
<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 단순 표현식</h2>
<!-- ~{...}를 사용하는게 원칙이나, 코드가 단순하면 이부분 생략가능 -->
<div th:replace="template/fragment/footer :: copy"></div>
<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
</body>
</html>
16. 템플릿 레이아웃1
java/hello/thymeleaf/basic/TemplateController.java
@GetMapping("/layout")
public String layout(){
return "template/layout/layoutMain";
}
/templates/template/layout/base.html
<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>
/templates/template/layout/layoutMain.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="template/layout/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>
<!--
<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" th:href="@{/css/bootstrap.min.css}">
<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>
<body>
메인 컨텐츠
</body>
</html>
-->
17. 템플릿 레이아웃2
- html 전체에 layout 적용
java/hello/thymeleaf/basic/TemplateController.java
@GetMapping("/layoutExtend")
public String layoutExtends(){
return "template/layoutExtend/layoutExtendMain";
}
/templates/template/layoutExtend/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>
/templates/template/layoutExtend/layoutExtendMain.html
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/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 th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
<footer>
레이아웃 푸터
</footer>
</body>
</html>
-->
References
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 (김영한)
'Thymeleaf' 카테고리의 다른 글
[02] 타임리프 - 스프링 통합과 폼 (0) | 2024.08.01 |
---|