์์ฑ์ : ๋ฐฐ์ ์ฐ
Mail : baejeu@naver.com
Language : Java 17
IDE : Intellij
Framework : SpringBoot (3.2.5)
โก ํ์๋ฆฌํ ๊ณต์ ์ฌ์ดํธ https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#what-is-thymeleaf
โก ์ฝ๋ฉํ๋ ํฌ๋ก๋ฆฌ https://velog.io/@alicesykim95/Thymeleaf
โก README Simple Icons https://simpleicons.org/?q=Thy
โก makeaplayground https://makeaplayground.tistory.com/187
โก ์ธํ๋ฐ ๊น์ํ ๊ฐ์ฌ๋ ์คํ๋ง MVC ๊ฐ์ https://www.inflearn.com/roadmaps/373
ํ์๋ฆฌํ๋ View Templete Engine ์ผ๋ก JSP, Freemarkerd, Velocity ์ ๊ฐ์ด SSR(Server Side Rendering) ์ด๋ค.
์ฆ ์๋ฒ์์ ํด๋ผ์ด์ธํธ์๊ฒ ๋์ ์ผ๋ก ์๋ตํ ๋ธ๋ผ์ฐ์ ํ๋ฉด์ ๋ง๋ค์ด์ฃผ๋ ์ญํ ์ ํ๋ค.
-
์์ฐ์ค๋ฌ์ด ๋ฌธ๋ฒ : ํ์๋ฆฌํ๋ ์์ฐ์ค๋ฌ์ด ๋ฌธ๋ฒ์ ์ ๊ณตํ์ฌ HTML ํ์ผ์ ์ฝ๊ฒ ํตํฉํ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด ๊ฐ๋ฐ์๋ค์ ๋น ๋ฅด๊ฒ ํ ํ๋ฆฟ์ ์์ฑํ๊ณ ์ ์ง๋ณด์ํ ์ ์๋ค.
-
ํตํฉ ์ฉ์ด์ฑ : Spring ์์๋ Spring Boot์ Thymeleaf๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค. JSP ์ฌ์ฉ ์ ํธํ ๋ฐ ํ๊ฒฝ์ค์ ์ ์ด๋ ค์์ด ๋ง๋ค. ๋ฐ๋๋ก ํ์๋ฆฌํ๋ ๊ฐํธํ๊ฒ Depedency ์ถ๊ฐ ์์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
-
View ์ง์ค : JSP์ ๋ฌ๋ฆฌ Servlet Code๋ก ๋ณํ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๋ฆฌ๋์ด View์ ์ง์คํ ์ ์๋ค.
-
์ฌ์ฉ์ ํธ์ : ์๋ฒ์์์ ๋์ํ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ ์๋ฒ ๋์ ์์ด ํ๋ฉด์ ํ์ธํ ์ ์๋ค. ๋๋ฌธ์ ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ณ ํ๋ฉด ๋์์ธ ๋ฐ ํ ์คํธ์ ์ฉ์ดํ๋ค.
-
์ต์ข ์ฌ์ฉ์ ์นํ์ : ํ์๋ฆฌํ๋ ์ต์ข ์ฌ์ฉ์๊ฐ ์ดํดํ๊ธฐ ์ฌ์ด ํ ํ๋ฆฟ์ ์์ฑํ ์ ์์ต๋๋ค. ํ ํ๋ฆฟ์ ํ๊ทธ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์์ฑ์ ๋ณ๊ฒฝํ์ฌ ์ต์ข ์ถ๋ ฅ๋ฌผ์ ์กฐ์ํ ์ ์์ผ๋ฉฐ, ์ด๋ ๋์์ด๋์ ํ์ ํ ๋ ์ ์ฉํฉ๋ ๋ค.
-
ํ ํ๋ฆฟ ์บ์ฑ: ํ์๋ฆฌํ๋ ํ ํ๋ฆฟ ์บ์ฑ์ ์ง์ํ์ฌ ํจ์จ์ ์ธ ์ฑ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ฐ๋ณต์ ์ผ๋ก ์ฌ์ฉ๋๋ ํ ํ๋ฆฟ์ ๋ฏธ๋ฆฌ ์ปดํ์ผํ๊ณ ์บ์ํ์ฌ ์๋๋ฅผ ํฅ์์ํต๋๋ค. ๊ทธ๋์ ์ด๊ธฐ ํ์ด์ง ๋ก๋ฉ ์๋๊ฐ ๋น ๋ฅด๋ค.
- ์์กด์ฑ ์ถ๊ฐ
Maven์ pom.xml์, Gradle์ build.gradle์ ํ์๋ฆฌํ์ dependency๋ฅผ ์ถ๊ฐํด์ค๋ค.
๐ Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
๐ Gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
- application.properties ์ฌ๋ฌ ์ค์
# Thymeleaf ์ฌ์ฉ
spring.thymeleaf.enabled=true
# cache ์ฌ์ฉ /์ฌ์ฉ ์ ์ปดํ์ผํ ํ์ผ๋ง ์ฌ์ฉ
spring.thymeleaf.cache=false
# ํ
ํ๋ฆฟ ์์น / View ํ์ผ ์์น
spring.thymeleaf.prefix=classpath:templates/
# View ํ์ผ ๊ธฐ๋ณธ ํ์ฅ์
spring.thymeleaf.suffix=.html
- ํ์๋ฆฌํ๋ฅผ ์ ์ฉํ๋ HTML ๋ฌธ์์ ๋ค์์คํ์ด์ค ์ถ๊ฐ
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${name}">Name</h1>
</body>
</html>
๋ณ์ ํํ์ ${โฆ}
์ ํ ๋ณ์ ํํ์ *{โฆ}
๋ฉ์ธ์ง ํํ์ #{โฆ}
๋งํฌ 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, get, le)
๋๋ฑ ์ฐ์ฐ ==, != (eq, ne)
If- then (if) ? (then)
If-then-else (if) ? (then) : (else)
Default: (value) ? : (defaultvalue)
No-Operation: _
- ๊ฐ๋ : ์น ๋ธ๋ผ์ฐ์ ๋ < ๋ฅผ HTML ํ๊ทธ๋ก ์ธ์ํ๋ค. ๋ฐ๋ผ์ < ๋ฅผ ํ๊ทธ์ ์์์ด ์๋๋ผ ๋ฌธ์๋ก ํํํ๋ ๋ฐฉ๋ฒ์ด ํ์ํ๋ฐ ์ด๊ฒ์ HTML ์ํฐํฐ๋ผ ํ๋ค.
- escape : HTML์์ ์ฌ์ฉํ๋ ํน์ ๋ฌธ์๋ฅผ HTML Entity๋ก ๋ณ๊ฒฝํ๋ ๊ฒ
- unescape : escape ๋ฐ๋ (๊ทธ๋๋ก ํ๊ทธ๋ก ์ฌ์ฉ)
- <๋ฅผ <๋ก ์ด์ค์ผ์ดํ
- >๋ฅผ >๋ก ์ด์ค์ผ์ดํ
- &๋ฅผ &๋ก ์ด์ค์ผ์ดํ
- "๋ฅผ "๋ก ์ด์ค์ผ์ดํ
- '๋ฅผ '๋ก ์ด์ค์ผ์ดํ
<!DOCTYPE html>
<html>
<head>
<title>๋ฌธ์ ์ ๋ชฉ</title>
</head>
<body>
<!-- ๋ด์ฉ ์์ฑ -->
</body>
</html>
html : HTML ๋ฌธ์์ ์์๊ณผ ๋์ ๋ํ๋ด๋ ํ๊ทธ์ ๋๋ค.
<head>
<title>๋ฌธ์ ์ ๋ชฉ</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css">
</head>
head : ๋ฌธ์์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ ๋ถ๋ถ์ ์ ์ํฉ๋๋ค.
<title>๋์ ์น ํ์ด์ง</title>
title : ๋ฌธ์์ ์ ๋ชฉ์ ์ ์ํฉ๋๋ค
body : ๋ฌธ์์ ๋ณธ๋ฌธ์ ๋ํ๋ด๋ ๋ถ๋ถ์ ์ ์ํฉ๋๋ค.
<h1>์ฒซ ๋ฒ์งธ ์ ๋ชฉ</h1>
<h2>๋ ๋ฒ์งธ ์ ๋ชฉ</h2>
h? : ์ ๋ชฉ์ ํ์ํฉ๋๋ค h1์ด ๊ฐ์ฅ ํฌ๊ณ h6๊ฐ ๊ฐ์ฅ ์์ต๋๋ค.
<p>์ด๊ฒ์ ๋ฌธ๋จ์
๋๋ค.</p>
p : ๋ฌธ๋จ์ ํ์ํฉ๋๋ค
<a href="https://www.example.com">example ์ฌ์ดํธ</a>
a : ํ์ดํผ๋งํฌ๋ฅผ ์ ์ ํฉ๋๋ค.
<img src="์ด๋ฏธ์ง์ฃผ์.jpg" alt="example ์ด๋ฏธ์ง">
img : ์ด๋ฏธ์ง๋ฅผ ์ฝ์ ํฉ๋๋ค.
<div id="content">
<!-- ๋ด์ฉ -->
</div>
div : ๊ตฌํ์ ์ ์ํ๋ฉฐ ์ฃผ๋ก CSS ์คํ์ผ๋ง์ด๋ JavaScript๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
ul, ol, li : HTML์์ ๋ชฉ๋ก์ ์ ์ํ๋ ๋ฐ ์ฌ์ฉ๋๋ ํ๊ทธ๋ค์ ๋๋ค.
๐ ul Unordered List
- ์์๊ฐ ์๋ ๋ชฉ๋ก์ ๋ํ๋ ๋๋ค.
- ๋ชฉ๋ก์ ๊ฐ ํญ๋ชฉ์ ์ , ๋ณํ, ํน์ ๋ค๋ฅธ ๊ธฐํธ๋ก ๋งํฌ์ ๋ฉ๋๋ค.
- ์ฃผ๋ก ํ๋ก์ ํธ ๊ฐ์, ์ฌ์ดํธ์ ๋ฉ๋ด ๋ฑ์ ์ฌ์ฉ๋ฉ๋๋ค.
<ul>
<li>ํญ๋ชฉ 1</li>
<li>ํญ๋ชฉ 2</li>
<li>ํญ๋ชฉ 3</li>
</ul>
๐ ol Ordered List
- ์์๊ฐ ์๋ ๋ชฉ๋ก์ ๋ํ๋ ๋๋ค.
- ๊ฐ ํญ๋ชฉ์ ๋ฒํธ(์ซ์)๋ก ์์๊ฐ ๋งค๊ฒจ์ง๋๋ค.
- ์์๊ฐ ์๋ ํญ๋ชฉ์ ๋ํ๋ผ๋ ์ฌ์ฉ๋ฉ๋๋ค.
<ol>
<li>์ฒซ ๋ฒ์งธ ํญ๋ชฉ</li>
<li>๋ ๋ฒ์งธ ํญ๋ชฉ</li>
<li>์ธ ๋ฒ์งธ ํญ๋ชฉ</li>
</ol>
๐ li List Item
- ๋ชฉ๋ก ๋ด์ ๊ฐ ํญ๋ชฉ์ ์ ์ํฉ๋๋ค.
- ul ๋๋ ol ์์์ ์ฌ์ฉ๋๋ฉฐ, ๊ฐ ํญ๋ชฉ์ ๋ํ๋ ๋๋ค.
์ ๋ฆฌ : ์์๊ฐ ์๋ ๋ชฉ๋ก o1์ ํญ๋ชฉ ๊ฐ์ ์ผ์ ํ ์์๊ฐ ์์ด์ผ ํ๊ณ ์์๊ฐ ์๋ ul์ ํญ๋ชฉ ๊ฐ์ ์์๊ฐ ์ค์ํ์ง ์๋๋ค.
<p>์ด ๋ฌธ์ฅ์ <span style="color: blue;">ํ๋์</span>์ผ๋ก ํ์๋ฉ๋๋ค.</p>
span : ํ ์คํธ์ ์ผ๋ถ๋ฅผ ๊ทธ๋ฃนํํ๊ฑฐ๋ ์คํ์ผ์ ์ ์ฉํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
<p>This is <b>bold</b> text.</p>
- b ํ๊ทธ๋ ํ ์คํธ๋ฅผ ๊ตต๊ฒ(bold)ํ์ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
- ์๋ฏธ์ ์ธ ์ค์์ฑ์ ๊ฐ์ง ์๊ณ , ์๊ฐ์ ์ผ๋ก ํ ์คํธ๋ฅผ ๊ฐ์กฐํ๊ณ ์ ํ ๋ ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- ์ ์์ ์์๋ "bold"๋ผ๋ ๋จ์ด๊ฐ ๊ตต๊ฒ ํ์๋ฉ๋๋ค.
<p>This is the first line.<br>This is the second line.</p>
- br ํ๊ทธ๋ ์ค ๋ฐ๊ฟ(line break)์ ๋ํ๋ ๋๋ค.
- ์๋ก์ด ์ค๋ก ํ ์คํธ๋ฅผ ์ด๋์ํค๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
- ์ ์์ ์์๋ "This is the first line" ๋ค์ ์๋ก์ด ์ค์ "This is the second line"์ด ์์๋ฉ๋๋ค
1 ํด๋ผ์ด์ธํธ์๊ฒ ๋๋๋ง ๋์ง ์๋ ์ฃผ์ (ํ์๋ฆฌํ ์์ง์ด ํ ํ๋ฆฟ์ ์ฒ๋ฆฌํ ๋ ๋ฌด์)
- ์๋ฐ ์คํฌ๋ฆฝํธ ํ์ค HTML ์ฃผ์ ํ์๋ฆฌํ๊ฐ ๋ ๋๋ง ํ์ง ์๊ณ , ๊ทธ๋๋ก ๋จ๊ฒจ๋๋ค.
1-1 ๋จ์ผ ๋ผ์ธ ์ฃผ์ (Single-line Comment)
<!--/* Comment here */-->
1-2 ๋ค์ค ๋ผ์ธ ์ฃผ์ (Multi-line Comment)
<!--/*
์ฌ๋ฌ ์ค ์ฃผ์
*/-->
2 ํ์๋ฆฌํ ํ์ ์ฃผ์
- ๋ ๋๋ง์์ ์ฃผ์ ๋ถ๋ถ์ ์ ๊ฑฐํ๋ค.
<!--/* [[${data}]] */-->
3 ํด๋ผ์ด์ธํธ์๊ฒ ๋๋๋ง ๋๋ ์ฃผ์ (ํ์๋ฆฌํ ํ๋กํ ํ์ ์ฃผ์)
<!--/*/ <span>ํ์ค HTML ์ฃผ์</span> /*/-->
ํ์๋ฆฌํ ํ๋กํ ํ์ ์ ์ฝ๊ฐ ํน์ดํ๋ฐ, HTML ์ฃผ์์ ์ฝ๊ฐ์ ๊ตฌ๋ฌธ์ ๋ํ๋ค. HTML ํ์ผ์ ์น ๋ธ๋ผ์ฐ์ ์์ ๊ทธ๋๋ก ์ด์ด๋ณด๋ฉด ์ ํฌํจํ๋ HTML ์ฃผ์์ด๊ธฐ ๋๋ฌธ์ ์ด ๋ถ๋ถ์ด ์น ๋ธ๋ผ์ฐ์ ๊ฐ ๋ ๋๋งํ์ง ์๋๋ค. ํ์๋ฆฌํ ๋ ๋๋ง์ ๊ฑฐ์น๋ฉด ์ด ๋ถ๋ถ์ด ์ ์ ๋ ๋๋ง ๋๋ค. HTML ํ์ผ์ ๊ทธ๋๋ก ์ด์ด๋ณด๋ฉด ์ฃผ์์ฒ๋ฆฌ๊ฐ ๋์ง๋ง, ํ์๋ฆฌํ๋ฅผ ํตํด ๋ ๋๋ง ํ ๊ฒฝ์ฐ์๋ง ์ถ๋ ฅ๋๋ค.
๐ก ๋ฌธ๋ฒ: th:text="${}" escape
<h1 th:text="${data}"></h1>
โญ๏ธ ์ค๋ช
- ์ผ๋ฐ ํ ์คํธ๋ฅผ ๋ฐํํ ๋ ์ฌ์ฉ
- [์ปจํธ๋กค๋ฌ] model.addAttribute("data", "Hello <b>Spring</b>!");
- HTML ํ๊ทธ ๋ํ String ๊ฐ์ผ๋ก ๋ณํ => ์ถ๋ ฅ: Hello <b>Spring</b>
<td th:text="${item.price}">10000</td>
- ๋ด์ฉ์ ๊ฐ์ th:text ์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ๋ค.
- ์ฌ๊ธฐ์๋ 10000์ ${item.price} ์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ๋ค.
<li>์ปจํ
์ธ ์์์ ์ง์ ์ถ๋ ฅํ๊ธฐ = [[${data}]] </li>
- HTML ํ๊ทธ์ ์์ฑ์ด ์๋๋ผ HTML ์ฝํ ์ธ ์์ญ์์์ ์ง์ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ๊ณ ์ถ๋ค๋ฉด [[...]] ๋ฅผ ์ฌ์ฉํ๋ค.
- [[...]]์ escape ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ค. unescape๋ก ์ฌ์ฉํ๊ณ ์ถ์ผ๋ฉด [(...)]
๐ก ๋ฌธ๋ฒ: th:utext="${}" unescape
<h1 th:utext="${data}"></h1>
โญ๏ธ ์ค๋ช
- [์ปจํธ๋กค๋ฌ] model.addAttribute("data", "Hello <b>Spring</b>!");
- th:text์ ๋์ผํ๊ฒ ํ ์คํธ๋ฅผ ๋ฐํํ์ง๋ง, HTML ํ๊ทธ๋ฅผ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค
- ์ถ๋ ฅ: Hello Spring!
- "Spring" ์ด b ํ๊ทธ๋ก ๋จ์ด๊ฐ ๊ตต๊ฒ ํ์๋๋ค.
- Spring EL(Expression Language)์ ์คํ๋ง ํ๋ ์์ํฌ์์ ์ฌ์ฉ๋๋ ํํ ์ธ์ด, ์ฃผ๋ก ์คํ๋ง์ ์ค์ ํ์ผ, JSPํ์ด์ง, Thymeleaf ๋ฑ์์ ์ฌ์ฉ๋ฉ๋๋ค.
- ๋ณ์, ์ฐ์ฐ์, ๋ฉ์๋ ํธ์ถ, ์์ฑ ์ ๊ทผ ๋ฑ์ ์ง์ํ๋ฉฐ ๋ค์ํ ์ ํ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋๋ฐ ์ ์ฉํฉ๋๋ค.
โญ๏ธ ์์ ์ค๋ช
- ์๋๋ฆฌ์ค : ์ปจํธ๋กค๋ฌ์์ model์ 3๊ฐ์ ๋ฐ์ดํฐ(Book, List, Map<String, Book>)๋ฅผ ๋ฃ๋๋ค.
@Controller
@RequestMapping("/books")
public class BookController {
@Data
static class Book{
private String name;
public Book(String name) {
this.name = name;
}
}
@GetMapping("/book-data")
public String getBook(Model model){
Book book1 = new Book("comic");
Book book2 = new Book("novel");
List<Book> list = new ArrayList<>();
list.add(book1);
list.add(book2);
Map<String, Book> map = new HashMap<>();
map.put("book1", book1);
map.put("book2", book2);
model.addAttribute("book", book1);
model.addAttribute("books", list);
model.addAttribute("map", map);
return "books";
}
}
- HTML + Thymeleaf๋ก ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ณด์ฌ์ฃผ๊ธฐ
<h2>SpringEL ํํ์ ์ฐ์ต</h2>
<ul> <!-- Object (Book) -->
<li><span th:text="${book.name}"></span></li>
<li><span th:text="${book['name']}"></span></li>
<li><span th:text="${book.getName()}"></span></li>
</ul>
<ul> <!-- List<Book> -->
<li><span th:text="${books[0].name}"></span></li>
<li><span th:text="${books[0]['name']}"></span></li>
<li><span th:text="${books[0].getName()}"></span></li>
</ul>
<ul> <!-- Map<String, Book> -->
<li><span th:text="${map['book1'].name}"></span> </li>
<li><span th:text="${map['book1']['name']}"></span> </li>
<li><span th:text="${map['book2'].getName()}"></span> </li>
</ul>
- ๋ชจ๋ ์๋ฐ ํ๋กํผํฐ getXxx๋ฅผ ์ฌ์ฉํ๊ณ ['๋ณ์์ด๋ฆ'], getXxx, .๋ณ์์ด๋ฆ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
<div th:if="${errors.containsKey('globalError')}">
์ฌ๊ธฐ์ errors๋ ์๋ฒ์์ Model์ ๋๊ฒจ์ค Map ๋ฐ์ดํฐ์ด๋ค. ๋ง์ฝ errors๊ฐ null ์ด๋ผ๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ํธ์ถํ๋ ์๊ฐ NullPointerException์ด ๋ฐ์ํ๋ค. ๊ทธ๋ผ ์ด๋ป๊ฒ ํด์ผํ ๊น?
<div th:if="${errors?.containsKey('globalError')}">
errors?.์ errors๊ฐ null ์ผ๋ NullPointerException์ด ๋ฐ์ํ๋ ๋์ , null์ ๋ฐํํ๋ ๋ฌธ๋ฒ์ด๋ค. th:if์์ null์ ์คํจ๋ก ์ฒ๋ฆฌ๋๋ฏ๋ก ์ค๋ฅ ๋ฉ์ธ์ง๊ฐ ์ถ๋ ฅ๋์ง ์๋๋ค. ์ด๊ฒ์ ์คํ๋ง์ SpringEL์ด ์ ๊ณตํ๋ ๋ฌธ๋ฒ์ด๋ค.
๐ก ๋ฌธ๋ฒ: th:with
<h2>์ง์ญ๋ณ์ th:with</h2> <!-- scope๋ <div> scope </div> -->
<div th:with="first=${books[0]}">
<p>์ฒซ ๋ฒ์งธ ์ฑ
์ด๋ฆ์? <span th:text="${first.name}"></span></p>
</div>
โญ๏ธ ์ค๋ช
- ์ด ์ง์ญ ๋ณ์์ scope๋ ์ง์ญ ๋ณ์๋ฅผ ์ ์ํ ํ๊ทธ ์์ ~ ๋
๐ก ๋ฌธ๋ฒ: th:src="${}"
<img class="img" th:src="${dataList.IMG_URL}">
โญ๏ธ ์ค๋ช
- ์ด๋ฏธ์ง src ์ฌ์ฉ์
๐ก ๋ฌธ๋ฒ: URL ๋งํฌ ํํ์
URL ๋งํฌ ํํ์์ ์ฌ์ฉํ๋ฉด ์๋ธ๋ฆฟ ์ปจํ ์คํธ๋ฅผ ์๋์ผ๋ก ํฌํจํ๋ค.
์๋ธ๋ฆฟ ์ปจํ ์คํธ?
์น ์ ํ๋ฆฌ์ผ์ด์ ํ๊ฒฝ์ ๋ํ๋ด๋ ๊ฐ์ฒด(์๋ธ๋ฆฟ ์ปจํ ์ด๋:Apache, Tomcat) ์์ ๊ด๋ฆฌ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ๊ณต์ ๋๋ ์ ๋ณด
@{...}
<a th:href="@{/items/{id}(id=${item.id})}">View Detail</a>
โญ๏ธ ์ค๋ช
-
a ํ๊ทธ๋ HTML ํ๊ทธ๋ก ํ์ดํผ๋งํฌ๋ฅผ ์์ฑํ๋๋ฐ ์ฌ์ฉ
-
th:href="@{....} : ํ์๋ฆฌํ ๋งํฌ์์ฑ ํํ์
-
PathVariable ๋ฃ๊ธฐ
-
{id}(id=${item.id}) ์ด๋ฐ์์ผ๋ก ํ๋ฉด item.id๊ฐ PathVariable๋ก ๋์ด๊ฐ์ง๋ค.
-
QueryString, ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ๋ฃ๊ธฐ
th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
์์ฑ๋ ๋งํฌ: http://localhost:8080/basic/items/1?query=test
- ๐ ๋ชจ๋ธ์์ ๋์ด์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก url ๊ฒฝ๋ก ๋ง๋๋ ์์
model.addAttribute("param1", "data1");
model.addAttribute("param2", "data2");
<ul>
<!-- /hello -->
<li><a th:href="@{/hello}">basic url</a></li>
<!-- /hello?param1=data1¶m2=data2 -->
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
<!-- /hello/data1/dat2 -->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<!-- /hello/data1?param2=data2 -->
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
โญ๏ธ ํ์๋ฆฌํ ์ฐ์ฐ์ ์๋ฐ์ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค. HTML ์์์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ HTML ์ํฐํฐ๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ์ ์ฃผ์!
- ๐ ์ฐ์ ์ฐ์ฐ
<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>
๊ฒฐ๊ณผ : true
- ๐ ๋น๊ต ์ฐ์ฐ
<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 = false<li>1 > 10 = <span th:text="1 gt 10"></span></li>
๊ฒฐ๊ณผ : 1 > 10 = false<li>1 < 10 = <span th:text="1 < 10"></span></li>
๊ฒฐ๊ณผ : 1 < 10 = true<li>1 > 10 = <span th:text="1 < 10"></span></li>
๊ฒฐ๊ณผ : 1 < 10 = true<li>1 > 10 = <span th:text="1 lt 10"></span></li>
๊ฒฐ๊ณผ : 1 < 10 = true<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
๊ฒฐ๊ณผ : 1 >= 10 = false<li>1 >= 10 = <span th:text="1 ge 10"></span></li>
๊ฒฐ๊ณผ : 1 >= 10 = false<li>1 <= 10 = <span th:text="1 <= 10"></span></li>
๊ฒฐ๊ณผ : 1 <= 10 = true<li>1 <= 10 = <span th:text="1 le 10"></span></li>
๊ฒฐ๊ณผ : 1 <= 10 = true<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
- ๐ ์กฐ๊ฑด์
<li> (10 % 2 == 0)? '์ง์':'ํ์' = <span th:text="(10 % 2 == 0)? '์ง์':'ํ์'"></span></li>
๊ฒฐ๊ณผ : (10 % 2 == 0)? '์ง์':'ํ์' = ์ง์
- ๐ Elvis ์ฐ์ฐ์
model.addAttribute("nullData", null); model.addAttribute("data", "Spring!");
- null check๋ฅผ ๊ฐ๋จํ๊ฒ ์ํํ๋๋ฐ ์ฌ์ฉ ?:
<li><span th:text="${data}?: '๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.'"></span></li>
๊ฒฐ๊ณผ : Spring!<li><span th:text="${nullData}?: '๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.'"></span></li>
๊ฒฐ๊ณผ : ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.
- ๐ No-Operation
- _ ์ธ ๊ฒฝ์ฐ ๋ง์น ํ์๋ฆฌํ๊ฐ ์คํ๋์ง ์๋ ๊ฒ ์ฒ๋ผ ๋์ํ๋ค.
<li><span th:text="${data}?: _ "></span></li>
๊ฒฐ๊ณผ : Spring!<li><span th:text="${nullData}?: _ "></span></li>
๊ฒฐ๊ณผ :
โญ๏ธ ํ์๋ฆฌํ๋ ์ฃผ๋ก HTML ํ๊ทธ์ th:* ์์ฑ์ ์ง์ ํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค. th:*๋ก ์์ฑ์ ์ ์ฉํ๋ฉด ๊ธฐ์กด ์์ฑ์ ๋์ฒดํ๋ค. ๊ธฐ์กด ์์ฑ์ด ์์ผ๋ฉด ์๋ก ๋ง๋ ๋ค.
- ๐ ์์ฑ ์ค์
- ํ์ผ :
<input type="text" name="mock" th:name="userA" />
- ๋ ๋๋ง :
<input type="text" name="userA" />
- ํ์ผ :
- ๐ ์์ฑ ์ถ๊ฐ
- ํ์ผ :
<input type="text" class="text" th:attrappend="class=' large'" />
- ๋ ๋๋ง :
<input type="text" class="text large" />
- ์ค๋ช : th:attrappend ์์ฑ ๊ฐ์ ๋ค์ ๊ฐ์ ์ถ๊ฐ
- ํ์ผ :
<input type="text" class="text" th:attrprepend="class='large '" />
- ๋ ๋๋ง :
<input type="text" class="large text" />
- ์ค๋ช : th:attrprepend ์์ฑ ๊ฐ์ ์์ ๊ฐ์ ์ถ๊ฐ
- ํ์ผ :
<input type="text" class="text" th:classappend="large" />
- ๋ ๋๋ง :
<input type="text" class="text large" />
- ์ค๋ช : th:classappend class ์์ฑ์ ์์ฐ์ค๋ฝ๊ฒ ๊ฐ ์ถ๊ฐ
- ํ์ผ :
- ๐ checked ์ฒ๋ฆฌ
<input type="checkbox" name="active" checked="false" />
HTML์์๋ boolean ๊ฐ๊ณผ ์๊ด์์ด checked ์ฒ๋ฆฌ- ํ์ผ :
<input type="checkbox" name="active" th:checked="false" />
- ๋ ๋๋ง :
<input type="checkbox" name="active" />
- ์ค๋ช : th:checked=false๋ฉด checked ์์ฑ ์์ฒด๋ฅผ ์ ๊ฑฐํ๋ค. true๋ฉด ์ ์ง
๐ ๋ฐ๋ณต ๊ธฐ๋ฅ
<tr th:each="item : ${items}">
<td th:text="${item.id}"></td>
<td th:text="${item.itemName}"></td>
<td th:text="${item.price}"></td>
</tr>
๐ ๋ฐ๋ณต ์ํ ์ ์ง
<table border="1">
<tr>
<th>count</th>
<th>index</th>
<th>username</th>
<th>age</th>
<th>size</th>
<th>even</th>
<th>odd</th>
<th>first</th>
<th>last</th>
<th>current</th>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">username</td>
<td th:text="${userStat.index}">index</td>
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
<td th:text="${userStat.size}">0</td>
<td th:text="${userStat.even}">true</td>
<td th:text="${userStat.odd}">true</td>
<td th:text="${userStat.first}">true</td>
<td th:text="${userStat.last}">true</td>
<td th:text="${userStat.current}">true</td>
</tr>
</table>
- ๋ฐ๋ณต์ ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ฅผ ์ค์ ํด์ ๋ฐ๋ณต์ ์ํ๋ฅผ ํ์ธ ํ ์ ์์ต๋๋ค.
- ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ ์๋ต์ด ๊ฐ๋ฅํ๋ฐ, ์๋ตํ๋ฉด ์ง์ ํ ๋ณ์๋ช + Stat๊ฐ ๋ฉ๋๋ค.
- ๋ฐ๋ณต ์ํ ์ ์ง ๊ธฐ๋ฅ
- index : 0๋ถํฐ ์์ํ๋ ๊ฐ
- count : 1๋ถํฐ ์์ํ๋ ๊ฐ
- size : ์ ์ฒด ์ฌ์ด์ฆ
- even , odd : ์ง์ ํ์ ์ฌ๋ถ (boolean) count๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ค.
- first, last : ์ฒ์, ๋ง์ง๋ง ์ฌ๋ถ (boolean) count๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋ค.
- current : ํ์ฌ ๊ฐ์ฒด
<div th:switch="${userType}">
<p th:case="'admin'">๊ด๋ฆฌ์ ํ์ด์ง์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค.</p>
<p th:case="'user'">์ฌ์ฉ์ ํ์ด์ง์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค.</p>
<p th:case="'guest'">๊ฒ์คํธ ํ์ด์ง์ ์ค์ ๊ฒ์ ํ์ํฉ๋๋ค.</p>
<p th:case="*">์ ์ ์๋ ์ฌ์ฉ์์
๋๋ค.</p>
</div>
- th:case="*" ๋ง์กฑํ๋ ์กฐ๊ฑด์ด ์์ ๋ ์ฌ์ฉํ๋ default ๊ฐ
<span th:text="'๋ฏธ์ฑ๋
์'" th:if="${user.age lt 20}"></span>
<span th:text="'๋ฏธ์ฑ๋
์'" th:unless="${user.age ge 20}"></span>
- ๋ง์ฝ user age < 20 '๋ฏธ์ฑ๋ ์'๊ฐ ์ถ๋ ฅ
- ๋ง์ฝ user age >= 20 ๋ ๋๋ง x
- ํ์๋ฆฌํ์์ ๋ฌธ์ ๋ฆฌํฐ๋ด์ ํญ์ ์์ ๋ฐ์ดํ๋ก ๊ฐ์ธ์ผ ํ๋ค.
<span th:text="'hello'">
- ๋ง์ฝ ๊ณต๋ฐฑ ์์ด ์ญ ์ด์ด์ง๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์ ๋ฐ์ํ๋ฅผ ์๋ตํ ์ ์๋ค.
<span th:text="hello">
- ์์
- ์ค๋ฅ
<span th:text="hello world!">
์ค๊ฐ์ ๊ณต๋ฐฑ์ด ์์ด์ ์ค๋ฅ - ์์
<span th:text="'hello world!'">
- ์ค๋ฅ
- ๋ฆฌํฐ๋ด ๋์ฒด |...|
- ๐ ์ ๋ฆฌ
<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>
- ํ์๋ฆฌํ ํน์ฑ์ HTML ํ๊ทธ์์ ์์ฑ์ผ๋ก ๊ธฐ๋ฅ์ ์ ์ํด์ ์ฌ์ฉํ๋๋ฐ, ์ฌ์ฉํ๊ธฐ ์ ๋งคํ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ฉด๋๋ค.
- ๋ ๋๋ง์ ์ ๊ฑฐ๋๋ค.
<th:block> </th:block>
<input type="text" id="itemName" th:field="*{itemName}"
th:class="${errors?.containsKey('itemName')} ? 'form-control field-error' : 'form-control'"
class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์">
์ง๊ธ ์ด ์ฝ๋๋ฅผ ๋ณด๋ฉด th:class๊ฐ if - else ๋ฌธ์ผ๋ก itemName key๊ฐ ์์ผ๋ฉด "form-control field-error" ์์ผ๋ฉด "form-control"
th:classappend๋ฅผ ํ์ฉํด์ ๊ฐ๋จํ๊ฒ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.
<input type="text" id="itemName" th:field="*{itemName}"
th:classappend="${errors?.containsKey('itemName')} ? 'field-error' : _"
class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์">
์ด๋ ๊ฒ ํ๋ฉด errros(Map)์ itemName key๊ฐ ์์ผ๋ฉด class์ field-error๋ฅผ ์ถ๊ฐํด์ class = "form-control field-error"์ ๊ฐ์ด ๋์ผํ๊ฒ ๋์ํ๋ค.
์์ผ๋ฉด ๊ธฐ์กด class์ธ form-control
์คํ๋ง ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ BidingResult๋ฅผ ํ์ฉํ ๊ฒ์ฆ
java import org.springframework.validation.BindingResult;
์๋ฒ์์๋ BindingResult์ 2๊ฐ์ง error ํ์ ์ ๋ฐํํ๋ค.
- ObjectError : ํน์ ํ๋์ ์๋ฌ๊ฐ ์๋ ๋ณตํฉ์ ์ธ ์๋ฌ
- FieldError : ํน์ ํ๋์ ์๋ฌ
ObjectError(Global error) ์ถ๋ ฅ
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">์ ์ฒด ์ค๋ฅ ๋ฉ์์ง</p>
</div>
${#fiends.hasGlobalErrors()} error๊ฐ ์์ผ๋ฉด ๋์ Global error๋ 0 ~ n๊ฐ์ด๋ค. th:each ๋ฐ๋ณต๋ฌธ์ ํ์ฉํด์ defaultMessage๋ฅผ ์ถ๋ ฅํ๋ค.
FieldError ์ถ๋ ฅ
<label for="itemName" th:text="#{label.item.itemName}">์ํ๋ช
</label>
<input type="text" id="itemName" th:field="*{itemName}"
th:errorclass="field-error" class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์">
<div class="field-error" th:errors="*{itemName}"> ์ํ๋ช
์ค๋ฅ</div>
๋ณดํต ํด๋น ์๋ฌ๊ฐ ์์ผ๋ฉด if - else ๋ฅผ ํตํด์ th:appendclass ํ์ฌ "field-error form-control" ์ด๋ ๊ฒ ํ๋๋ฐ th:errorclass๋ฅผ ํตํด์ ๋ก์ง์ ์์ฒญ ๊ฐ๋จํ๊ฒ ์ค์๋ค.
th:errors = BindingRessult ์์ ๋๊ฒจ์ค Object์ ๋ง์ถฐ์ ํด๋น field์ error๊ฐ ์๋ค๋ฉด BindingResult์ ๋๊ฒจ์ค defaultMessage๋ฅผ ์ถ๋ ฅํ๋ค.
์ ๋ฆฌ
- ํ์๋ฆฌํ๋ ์คํ๋ง์ BindingResult๋ฅผ ํ์ฉํด์ ํธ๋ฆฌํ๊ฒ ๊ฒ์ฆ ์ค๋ฅ๋ฅผ ํํํ๋ ๊ธฐ๋ฅ์ ์ ๊ณต
#fiends
: BindingResult๊ฐ ์ ๊ณตํ๋ ๊ฒ์ฆ ์ค๋ฅ์ ์ ๊ทผํ ์ ์๋ค.th:errors
: ํด๋น ํ๋์ ์ค๋ฅ๊ฐ ์๋ ๊ฒฝ์ฐ์ ํ๊ทธ๋ฅผ ์ถ๋ ฅํ๋ค.th:errorclass
: th:field์์ ์ง์ ํ ํ๋์ ์ค๋ฅ๊ฐ ์์ผ๋ฉด class ์ ๋ณด๋ฅผ ์ถ๊ฐํ๋ค.
- ํ์๋ฆฌํ๋ ์๋ฐ์คํฌ๋ฆฝํธ์์ ํ์๋ฆฌํ๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- ์ฌ์ฉ๋ฒ :
<script th:inline="javascript"> </script>
๐ ์ฌ์ฉ ์
<script>
var username = "[[${user.username}]]";
var age = [[${user.age}]];
//์๋ฐ์คํฌ๋ฆฝํธ ๋ด์ถ๋ด ํ
ํ๋ฆฟ
var username2 = /*[[${user.username}]]*/ "test username";
//๊ฐ์ฒด
var user = [[${user}]];
</script>
๐ ์ฌ์ฉ ํ
<script th:inline="javascript">
var username = [[${user.username}]];
var age = [[${user.age}]];
//์๋ฐ์คํฌ๋ฆฝํธ ๋ด์ถ๋ด ํ
ํ๋ฆฟ
var username2 = /*[[${user.username}]]*/ "test username";
//๊ฐ์ฒด
var user = [[${user}]];
</script>
-
ํ ์คํธ ๋ ๋๋ง
- ์ธ๋ผ์ธ ์ฌ์ฉ ์ var username = userA ๊ฐ๋ฐ์๊ฐ ๊ธฐ๋ํ๋ ๊ฒ์ "userA" ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ฅ ๋ฐ์
- ์ธ๋ผ์ธ ์ฌ์ฉ ํ var username = "userA"
- ์ธ๋ผ์ธ ์ฌ์ฉ ํ ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ๋ฌธ์ ํ์ ์ธ ๊ฒฝ์ฐ "๋ฅผ ํฌํจํด ์ค๋ค. ์ถ๊ฐ๋ก ์๋ฐ์คํฌ๋ฆฝํธ์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ๋ฌธ์๊ฐ ํฌํจ๋์ด ์์ผ๋ฉด ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ๋ ํด์ค๋ค.
-
์๋ฐ์คํฌ๋ฆฝํธ ๋ด์ถ๋ด ํ ํ๋ฆฟ
- ํ์๋ฆฌํ๋ HTML ํ์ผ์ ์ง์ ์ด์ด๋ ๋์ํ๋ ๋ด์ถ๋ด ํ ํ๋ฆฟ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ฃผ์์ ํ์ฉํด์ ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
- ์ธ๋ผ์ธ ์ฌ์ฉ ์ var username2 = /userA/ "test username";
- ์ธ๋ผ์ธ ์ฌ์ฉ ํ var username2 = "userA";
-
๊ฐ์ฒด
- ํ์๋ฆฌํ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด๋ฅผ JSON์ผ๋ก ์๋์ผ๋ก ๋ณํํด์ค๋ค.
- var user = [[${user}]];
- ์ธ๋ผ์ธ ์ฌ์ฉ ์ var user = BasicController.User(username=userA, age=10);
- ์ธ๋ผ์ธ ์ฌ์ฉ ํ var user = {"username":"userA","age":10};
- ์ธ๋ผ์ธ ์ฌ์ฉ ์ ์ ๊ฐ์ฒด์ toString()์ด ํธ์ถ๋ ๊ฐ์ด๋ค.
- ์ธ๋ผ์ธ ์ฌ์ฉ ํ๋ ๊ฐ์ฒด๋ฅผ JSON์ผ๋ก ๋ณํํด์ค๋ค.
๐ ์ธ๋ผ์ธ ๋ฐ๋ณต๋ฌธ
<script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
์น ํ์ด์ง๋ฅผ ๊ฐ๋ฐํ ๋๋ ๊ณตํต ์์ญ์ด ๋ง๋ค. ์๋จ ์์ญ, ํ๋จ ์์ญ, ์ข์ธก ์นดํ ๊ณ ๋ฆฌ ๋ฑ๋ฑ ์ฌ๋ฌ ํ์ด์ง์์ ํจ๊ป ์ฌ์ฉํ๋ ์์ญ๋ค์ด ์๋ค. ์ด๋ฐ ๋ถ๋ถ์ ์ฝ๋๋ฅผ ๋ณต์ฌํด์ ์ฌ์ฉํ๋ค๋ฉด ๋ณ๊ฒฝ์ ์ฌ๋ฌ ํ์ด์ง๋ฅผ ๋ค ์์ ํด์ผ ํ๋ฏ๋ก ์๋นํ ๋นํจ์จ์ ์ด๋ค. ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ํ ํ๋ฆฟ ์กฐ๊ฐ๊ณผ ๋ ์ด์์ ๊ธฐ๋ฅ์ ์ง์ํ๋ค.
๐ footer.html
์ฌ๋ฌ ํ์ด์ง์์ ๋ค ๋๊ฐ์ ์์ญ์ ์ฌ์ฉํ๋ค๋ฉด footer๋ฅผ ํ์ฉํ๋ค. ๋ค๋ฅธ ํ์๋ฆฌํ ํ์ผ์ด ํด๋น ํ์ผ(footer.html)์ ๋ถ๋ฌ์ ์ฌ์ฉํ๋ ์ฉ๋ th:fragment ๊ฐ ์๋ ํ๊ทธ๋ ๋ค๋ฅธ๊ณณ์ ํฌํจ๋๋ ์ฝ๋ ์กฐ๊ฐ์ผ๋ก ์ดํดํ๋ฉด ๋๋ค.
<!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>
๐ fragmentMain.html
footer๋ฅผ ๋ถ๋ฌ์ ์ฌ์ฉํ๋ html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>๋ถ๋ถ ํฌํจ</h1>
<h2>๋ถ๋ถ ํฌํจ insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<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>
template/fragment/footer :: copy
:template/fragment/footer.html
ํ ํ๋ฆฟ์ ์๋th:fragment="copy"
๋ถ๋ถ์ ํ ํ๋ฆฟ ์กฐ๊ฐ์ผ๋ก ์ฌ์ฉํ๋ค๋ ์๋ฏธ- ๋ถ๋ถํฌํจ insert
<div th:insert="~{template/fragment/footer :: copy}"></div>
:th:insert
๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฌ ํ๊ทธ<div>
๋ด๋ถ์ ์ถ๊ฐํ๋ค.- ์น ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๋ ์์ค
<div><footer> ํธํฐ ์๋ฆฌ ์ ๋๋ค. </footer></div>
- ๋ถ๋ถํฌํจ replace
<div th:replace="~{template/fragment/footer :: copy}"></div>
:th:replace
๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฌ ํ๊ทธ<div>
๋ฅผ ๋์ฒดํ๋ค.- ์น ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๋ ์์ค
<footer> ํธํฐ ์๋ฆฌ ์ ๋๋ค. </footer>
- ๋ถ๋ถ ํฌํจ ๋จ์ ํํ์
<div th:replace="template/fragment/footer :: copy"></div>
:~{...}
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์์น์ด์ง๋ง ํ ํ๋ฆฟ ์กฐ๊ฐ์ ์ฌ์ฉํ๋ ์ฝ๋๊ฐ ๋จ์ํ๋ฉด ์๋ต
- ํ๋ผ๋ฏธํฐ ์ฌ์ฉ
<div th:replace="~{template/fragment/footer :: copyParam ('๋ฐ์ดํฐ1', '๋ฐ์ดํฐ2')}"></div>
: ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฌํด์ ๋์ ์ผ๋ก ์กฐ๊ฐ์ ๋ ๋๋ง- ์น ๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๋ ์์ค
<footer> <p>ํ๋ผ๋ฏธํฐ ์๋ฆฌ ์ ๋๋ค.</p> <p>๋ฐ์ดํฐ1</p> <p>๋ฐ์ดํฐ2</p> </footer>
- ๊ธฐ๋ณธ ์ ๊ณต ๊ฐ์ฒด : request, response, session, servletContext, locale
- ์ฌ์ฉ๋ฒ : ${#request}, ${#response}, ${#session}, ${#servletContext}, ${#locale}
- ๊ธฐ๋ณธ ์ ๊ณต ๊ฐ์ฒด : locale
- ์ฌ์ฉ๋ฒ : ${#locale}
- ์คํ๋ง ๋ถํธ 3.0 ์ด์์ locale ๊ฐ์ฒด ๋ง๊ณ ๋ ์ง์์ ์ํ๋ค. ๋ง์ฝ ์ฌ์ฉํ๋ฉด ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Caused by: java.lang.IllegalArgumentException: The
'request','session','servletContext' and 'response' expression utility objects
are no longer available by default for template expressions and their use is not
recommended. In cases where they are really needed, they should be manually
added as context variables.
- ๋๋จธ์ง ๊ฐ์ฒด๋ค์ ๋ชจ๋ธ(Model)์ ์ง์ ๋ฃ์ด์ ์ฌ์ฉํ๋ค.
- HTTP ์์ฒญ ํ๋ผ๋ฏธํฐ ์ ๊ทผ : param
- ์) ${param.paramData}
- HTTP ์ธ์
์ ๊ทผ : session
- ์) ${session.sessionData}
- sessionData๋ ์๋ฒ์์ ๋ฃ์ด์ค ๋ฐ์ดํฐ(session.setAttribute) key
- ์คํ๋ง ๋น ์ ๊ทผ: @
- ์) ${@helloBean.hello('Spring!')}
- ๋น ์ด๋ฆ(helloBean) ์ผ๋ก ์ ๊ทผ hello๋ ํด๋น ๋น์ ๋ฉ์๋
- ํ์๋ฆฌํ๋ ๋ฌธ์, ์ซ์, ๋ ์ง, URI๋ฑ์ ํธ๋ฆฌํ๊ฒ ๋ค๋ฃจ๋ ๋ค์ํ ์ ํธ๋ฆฌํฐ ๊ฐ์ฒด๋ค์ ์ ๊ณตํ๋ค.
- ํ์๋ฆฌํ ๊ณต์ ํํ์ด์ง์์ ํ์ํ ๋ ์ฐพ์์ ์ฌ์ฉํ๋ค.
โก ํ์๋ฆฌํ ์ ํธ๋ฆฌํฐ ๊ฐ์ฒด https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expression-utility-objects
โก ํ์๋ฆฌํ ์ ํธ๋ฆฌํฐ ๊ฐ์ฒด ์์ https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expressionutility-objects
- project๋ฅผ ๋ค์ด๋ฐ๊ณ localhost:8181๋ก ์ ์์ ํ๋ฉด ํ ์ฌ๋ผ ์ฐจ ํ๋งค๋ผ๋ ๋งํฌ๊ฐ ์์ต๋๋ค.
- ๋ค์ด๊ฐ์ ๋ณด๋ฉด ํ์๋ฆฌํ๋ฅผ ํ์ฉํ ์ํ ๋ฑ๋ก, ์ํ ์์ , ์ํ ๋ฆฌ์คํธ ๋ณผ ์ ์์ต๋๋ค.
- README์ ์ค๋ช ์๋ ๋ฌธ๋ฒ๋ค th:object, th:field, select, checkbox, radio, ${#ids.prev('...')}
Back-End Package โก Thymleaf/study/pratice1/*
SSR Package โก resources/templates/practice1/*