Thymeleaf#1 ๊ธฐ๋ณธ๊ธฐ๋ฅ
by JiwonDev๊ณต์ ๋ ํผ๋ฐ์ค์ ์น์ ํ๊ฒ ์ ํ์์ง๋ง, ์ค๋ช ์ ์ฝ๋ ๊ฒ ๋ณด๋จ ์์ ๋ฅผ ๋ณด๋๊ฒ ํจ์ฌ ์ดํด๊ฐ ์ฝ๋ค. ํด๋น ๊ธ์ ์ญ ์ฝ์ด๋ณด๋ฉฐ ๊ธฐ๋ณธ๊ธฐ๋ฅผ ์ตํ๊ณ ์ถ๊ฐ๋ก ํ์ํ ๋ ๊ณต์ ๋ ํผ๋ฐ์ค๋ฅผ ์ฐธ๊ณ ํด์ ์ฌ์ฉํ๋๋ก ํ์.
# ๊ณต์ ๋ ํผ๋ฐ์ค
HTML์ ํํ๋ฅผ ์ ์งํ๋ฉด์ ์ฌ์ฉํ ์ ์๋ ํ์๋ฆฌํ๋์ฌ์ฉํ๊ธฐ ์ฝ๋ค. ํ๋ คํ ํด๋ผ์ด์ธํธ๊ฐ ํ์ํ๋ค๋ฉด React, Vue๊ฐ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ ๋ง์ง๋ง, ๊ฐ๋จํ ํ์ด์ง๋ UX, UI๊ฐ ํฌ๊ฒ ์ค์ํ์ง์๋ ๊ด๋ฆฌ์์ฉ HTML ๊ฐ์ ๊ฒฝ์ฐ์๋ ์ฌ์ ํ ์ธ๋งํ๋ค. ๋ฐฑ์๋ ๊ฐ๋ฐ์๋ผ๊ณ ํ๋๋ผ๋, View ํ ํ๋ฆฟ ์์ง ํ๋์ ๋๋ ์์๋ฌ์ผํ๋ค. ํ๋๋ง ๋ฐฐ์ฐ๋ฉด ๋ค ๋น์ท๋น์ทํ๊ธด ํ์ง๋ง Thymeleaf ๊ฐ ์คํ๋ง์์ ๊ณต์์ ์ผ๋ก ๋ฐ์ด์ฃผ๊ณ ์์ด์ ๊ฐ์ด ์ฐ๊ธฐ ํธ๋ฆฌํ๋ค.
- ๊ณต์ ์ฌ์ดํธ: https://www.thymeleaf.org/
- ๊ณต์ ๋ฉ๋ด์ผ - ๊ธฐ๋ณธ ๊ธฐ๋ฅ: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
- ๊ณต์ ๋ฉ๋ด์ผ - ์คํ๋ง ํตํฉ: https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
* ์ฐธ๊ณ ๋ก xhtml, thymeleaf ์ฒ๋ผ HTML์ ์ปค์คํ ํ๊ทธ๋ฅผ ์ถ๊ฐ๋ก ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, <html>์์ฑ์ผ๋ก xmlns (xml namespace)๋ฅผ ์ง์ ํด์ฃผ์ด์ผ ๋ค๋ฅธ ํ ํ๋ฆฟ๊ณผ ์ถฉ๋์ ๋ฐฉ์งํ๊ณ ํด๋น HTML์ ํ์๋ฆฌํ๊ฐ ์ฌ์ฉ๋์์์ ๋ช ์์ ์ผ๋ก ํ์ํ ์ ์๋ค.
<!DOCTYPE html>
<!-- ์ผ๋ฐ ์ต์
(xmlns) ๋์ ํ์๋ฆฌํ ์ต์
(xmlns:th) ์ฌ์ฉํ๋ฉด, ํ์๋ฆฌํ ๋ ๋๋ง ์ดํ ํด๋น ์์ฑ์ ์ญ์ ํ๋ค. -->
<html xmlns:th="http://www.thymeleaf.org">
...
# Thymeleaf ์์ ์ฌ์ฉํ๋ ํํ์
ํ์๋ฆฌํ๋ ๋ณดํต HTML ํ๊ทธ์ ์์ฑ์ ์ด์ฉํ์ฌ ์๋ฒ์์ ๋ ๋๋งํ ๋ด์ฉ์ ํ์ํ๋ค. ๊ทธ๋์ HTML ํ์ผ์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ค. ํ๊ทธ์ ์์ฑ์ด ์๋๋ผ ๋ด์ฉ์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด [[...]] ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
- ๋ชจ๋ธ ๊ฐ์ฒด์ ์๋ ๊ฐ data = "Hello World" ์ผ ๋
<h1>์ปจํ
์ธ ์ ๋ฐ์ดํฐ ์ถ๋ ฅํ๊ธฐ</h1>
<ul>
<li>th:text ์ฌ์ฉ <span th:text="${data}"> ๋์ฒด ํ
์คํธ </span></li>
<li>์ปจํ
์ธ ์์์ ์ง์ ์ถ๋ ฅํ๊ธฐ = [[${data}]]</li>
</ul>
์ฐธ๊ณ ๋ก ์์ ์์ ์์ ์ฌ์ฉ๋ [[ ... ]] ๋ thymeleaf ๋ณ์์์ ๋ํ๋ด๋ ๊ธฐ๋ฅ๋ ์์ง๋ง, < > ์ ๊ฐ์ HTML ์์ฝ๋ฌธ์๋ฅผ ๋ฌธ์์ด(<)๋ก ๋ง๋ค์ด์ฃผ๋ ์ญํ ๋ ํ๋ค. ๋ง์ฝ ์๋์ ์ผ๋ก ๋ฌธ์์ด์ด ์๋ HTML ํ๊ทธ๋ก ์ธ์๋๊ฒ ํ๊ณ ์ถ๋ค๋ฉด th:utext ๋๋ [(...)] ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
๊ทธ ์ธ ์ ๊ณตํ๋ ํํ์์ ์๋์ ๊ฐ์๋ฐ, ๊ตณ์ด ์ธ์ฐ์ง ์์๋ ์ฌ์ฉํ๋ค๋ณด๋ฉด ์์ฐ์ค๋ฝ๊ฒ ์๊ฒ๋๋ค.
//• ๊ฐ๋จํ ํํ:
โฆ ๋ณ์ ํํ์: ${...}
โฆ ์ ํ ๋ณ์ ํํ์: *{...}
โฆ ๋ฉ์์ง(์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ) ํํ์: #{...}
โฆ ๋งํฌ 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, ge, le)
โฆ ๋๋ฑ ์ฐ์ฐ: ==, != (eq, ne)
//• ์กฐ๊ฑด ์ฐ์ฐ:
โฆ If-then: (if) ? (then)
โฆ If-then-else: (if) ? (then) : (else)
โฆ Default: (value) ?: (defaultvalue)
//• ํน๋ณํ ํ ํฐ:
โฆ No-Operation: _
# ์ฃผ์ (Comment)
ํ์๋ฆฌํ ์ฃผ์์ ์ฌ์ฉํ๋ฉด ๋ ๋๋ง์ HTML ์์ฒด์์ ์ฃผ์์ ์ญ์ ํด๋ฒ๋ฆฐ๋ค.
- ํ๋กํ ํ์ ์ ์ฃผ์์ ์ํ ๊ธฐ๋ฅ์ ์๋๊ณ , ํ์๋ฆฌํ ๋ ๋๋ง ํ์๋ง ํด๋น ํ๊ทธ๋ฅผ ๋ณด์ด๋๋ก ๋ง๋ค์ด์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
<!-- 1. HTML ์ฃผ์ -->
<h1> 2. ํ์๋ฆฌํ Parser์ฉ ์ฃผ์. ์์ ์ฝ์ง์๊ณ ๋ ๋๋ง์ ์ญ์ ํด๋ฒ๋ฆผ </h1>
<!--/* Thymeleaf ์ฃผ์ */-->
<!--/*-->
<span th:text="${data}">์ฌ๋ฌ์ค Thymeleaf ์ฃผ์</span>
<!--*/-->
<h1>3. ํ์๋ฆฌํ ํ๋กํ ํ์
์ฃผ์. ๊ฑฐ์ ์ฌ์ฉํ ์ผ ์์</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->
# ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
์๋์ ๊ฐ์ Controller์ Domain์ด ์๋ค๊ณ ๊ฐ์ ํ์.
@Data // = @Get, @Set, @toStr, @Equals&Hashcode, @ReqArgs
static class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
@GetMapping("/variable")
public String variable(Model model) {
User userA = new User("UserA", 10);
User userB = new User("UserB", 20);
List<User> list = Arrays.asList(userA, userB);
Map<String, User> map = Map.of("userA", userA, "userB", userB); // Java9
model.addAttribute("user", userA);
model.addAttribute("userList", list);
model.addAttribute("userMap", map);
return "basic/variable";
}
์ด๋ ๊ฒ model์ ๋ด์ ๊ฐ์ฒด, ๊ฐ์ ์๋์ ๊ฐ์ด HTML ํ๊ทธ์ ์์ฑ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ฐธ๊ณ ๋ก thํ๊ทธ๋ฅผ ์ฌ์ฉํ์ ๋ ์์ ๋ด์ฉ์ ์๋ฒ์์ ๋ ๋๋ง ๋ ๋ ์์ด์ง๋ค.
<span th:text="${userMap['userA'].username}"> ๋์ฒด ํ
์คํธ(๋ ๋๋ง์ ์ญ์ ) </span>
<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> ๊ฐ: <span th:text="${userList[0].username}"></span></li>
<li> ๊ฐ: <span th:text="${userList[0]['username']}"></span></li>
<li> ๊ฐ: <span th:text="${userList[0].getUsername()}"></span></li>
</ul>
<ul> Map
<li> ๊ฐ: <span th:text="${userMap['userA'].username}"></span></li>
<li> ๊ฐ: <span th:text="${userMap['userA']['username']}"></span></li>
<li> ๊ฐ: <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
# ์น ๊ธฐ๋ณธ ๊ฐ์ฒด (req, resp, session, servletcontext, locale)
๋ชจ๋ธ ์ฌ์ฉ๋ฒ๊ณผ ๊ฐ๋ค. ์ฌ์ฉํ ๊ฐ์ฒด์ ${#name} ์ผ๋ก ํ์ํด์ฃผ๋ฉด ๋๋ค.
๋ค๋ง request๋ ์์ฃผ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, ๋งค๋ฒ HttpServletRequest ๊ฐ์ฒด๋ฅผ ์ฃผ๊ธฐ์ ์ฌ์ฉํ๊ธฐ ๋ถํธํ๋ค.
๊ทธ๋์ ํ์๋ฆฌํ์์๋ ์์ฒญ ํ๋ผ๋ฉํ ์ฉ๋๋ก param์ด๋ผ๋ ํธ์์ฉ ๊ฐ์ฒด๋ฅผ ์ถ๊ฐ๋ก ์ ๊ณตํ๋ค. param.name ์ผ๋ก ์ฌ์ฉ๊ฐ๋ฅ
<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>
์์ ์๋ ๋์์์ง๋ง, ${@springBean}์ผ๋ก ์คํ๋ง ๋น ๊ฐ์ฒด์ ์ ๊ทผํ ์๋ ์๋ค.
// <li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello " + data;
}
}
# ์ ํธ๋ฆฌํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ์ฒด
Tutorial: Using Thymeleaf ํ์ํ ๋ ์ฐพ์๋ณด๋ฉด ๋๋ค. ์ฌ์ฉ๋ฒ์ ์น ๊ธฐ๋ณธ๊ฐ์ฒด์ ๋์ผํ๋ค.
- Java8์ ์ถ๊ฐ๋ ๋ ์ง ๊ฐ์ฒด๋ ${#temporals} ๋ก ์ฌ์ฉํ ์ ์๋ค. ๋ ํผ๋ฐ์ค์ ์์ ํ์์ด์ ์ถ๊ฐ.
//<span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span> @GetMapping("/date") public String date(Model model) { model.addAttribute("localDateTime", LocalDateTime.now()); return "basic/date"; }โ
# URL ๋งํฌ
th:href ์ @{/link} ๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค. ( name = ${value} ) ๋ฅผ ์ด์ฉํ์ฌ ๊ฒฝ๋ก ๋ณ์๋ฅผ ์ง์ ํด ์ค ์ ์๋ค. ๋จ ์ ์ด๋๊ณ ์ฌ์ฉํ์ง ์์ ๋ณ์๋ ์๋์ผ๋ก ํ๋ผ๋ฉํ ?name=value ๋ก ์ถ๊ฐ๋๊ฒ ๋๋ค.
- ์ฐธ๊ณ ๋ก @{} ์ฐ์ง์๊ณ , HTML ํ๊ทธ๋ง๋ฅ ๋ฐ๋ก th:href="/" ๋ฐ์๋ ๋๋ค.
- ๋งํฌ์ @{} ์ฌ์ฉํ๊ฒ๋๋ฉด ์๋๊ฒฝ๋ก๋ฅผ ์ ์ฐํ๊ฒ ์ฌ์ฉํ ์ ์๋ค. ์๋ธ๋ฆฟ ์ปจํ ์คํธ(ํฐ์บฃ ์ธ์คํด์ค) ๊ธฐ์ค์ผ๋ก ์ฐพ์์ฃผ๊ธฐ ๋๋ฌธ์, ํ๋ก์ ํธ ๋ฃจํธ ๊ฒฝ๋ก๊ฐ ๋ฌ๋ผ์ก๋๋ผ๋ url ๋ณ๊ฒฝ์์ด ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ค.
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<!-- /hello?param1=data1¶m2=data2 -->
<li><a th:href="@{/hello(param1=${data1}, param2=${data2})}"> query param</a></li>
<!-- /hello/data1/data2 -->
<li><a th:href="@{/hello/{param1}/{param2} (param1=${data1}, param2=${data2})}">path variable</a></li>
<!-- /hello/data1?param2=data2 -->
<li><a th:href="@{/hello/{param1} (param1=${data1}, param2=${data2})}">path var + query param</a></li>
</ul>
# ๋ฆฌํฐ๋๊ณผ ์ฐ์ฐ์
10.5, true, false, null ๊ทธ๋ฅ ์ฌ์ฉํ๋ฉด ๋๊ธดํ์ง๋ง, ๋ช๊ฐ์ง ์ฃผ์์ฌํญ๋ง ์์๋ณด์.
- ๋ฌธ์ ๋ฆฌํฐ๋์ ๊ณต๋ฐฑ์ ๋ฃ๊ณ ์ถ๋ค๋ฉด ์์ ๋ฐ์ํ('~')๋ก ๊ฐ์ธ๋ฉด ๋๋ค. ๊ทธ๋ฅ ๊ณต๋ฐฑ์ ๋ฃ์ผ๋ฉด ์ค๋ฅ๊ฐ ๋๋ค.
'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span>
'hello world!' = <span th:text="'hello world!'"></span>
- ๋ฌธ์์ด์ ์ฝ๊ฒ ์์ฑํ๋ [๋ฆฌํฐ๋ ๋์ฒด ๋ฌธ๋ฒ]์ด ์กด์ฌํ๋ค. | ~ | ๋ฅผ ์ฌ์ฉํด์ ๋ฐ๋ก ์ฐ๋ฉด ๋๋ค. (Js, Kotlin๊ณผ ๋น์ท)
'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span>
๋ฆฌํฐ๋ด ๋์ฒด |hello ${data}| = <span th:text="|hello ${data}|"></span>
- ์ฐ์ ์ฐ์ฐ, ์กฐ๊ฑด์์ Java์ ์๋ฒฝํ๊ฒ ๋์ผํ๋ค. ๊ฒฐ๊ตญ Java๋ก ๋์๊ฐ๋ ๊ฑฐ๋๊น
10 + 2 = <span th:text="10 + 2"></span>
10 % 2 == 0 = <span th:text="10 % 2 == 0"></span>
(10 % 2 == 0)? '์ง์':'ํ์' = <span th:text="(10 % 2 == 0)? '์ง์':'ํ์'"></span>
- ๋จ, ๋น๊ต์ฐ์ฐ์ ๊ฒฝ์ฐ HTML ํ๊ทธ์ ๊ฒน์น๋ ๋ฌธ์ < > ๋ ์ ์ฉ ๊ธฐํธ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
1 > 10 = <span th:text="1 > 10"></span> <!-- ์์ HTML์์๋ > ์ด๋ฐ์์ผ๋ก ์ฌ์ฉ๋๋ค -->
1 gt 10 = <span th:text="1 gt 10"></span>
1 >= 10 = <span th:text="1 >= 10"></span>
1 ge 10 = <span th:text="1 ge 10"></span>
1 == 10 = <span th:text="1 == 10"></span>
# Elvis ์ฐ์ฐ์ ( null ์ฒ๋ฆฌ, ${data}?: )
- ์กฐ๊ฑด์์ ์ถ์ฝ๋ฒ์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๋ฐ์ดํฐ๊ฐ ์์ ๋ ๋ฐํํ ๋์ฒด ๊ฐ์ ์ง์ ํ ์ ์๋ค.
${data}? = <span th:text="${data}?: '๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.'"></span></li>
${nullData}? = <span th:text="${nullData}?: '๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.'"></span></li>
- ํ์ด์ฌ ์ฒ๋ผ _ ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. _ ๊ฐ ์ ํ๋ ๊ฒฝ์ฐ ํด๋น ํ๊ทธ๋ ๋ ๋๋ง ๋์ง ์๊ณ HTML ๊ทธ๋๋ก ์ถ๋ ฅ๋๋ค.
${data}? = <span th:text="${data}?: _">๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.</span>
${nullData}? = <span th:text="${nullData}?: _">๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.</span>
# ํ๊ทธ ์์ฑ ๊ฐ ์ค์
<tag th:์์ฑ="~"> ์ผ๋ก ์ ์ผ๋ฉด, ํ์๋ฆฌํ ๋ ๋๋ง ์ <tag ์์ฑ="~"> ์ผ๋ก ๋ฐ๊ฟ์ค๋ค.
- ๊ทธ๋ฅ ์์ฑ์ ์ฌ์ฉํด๋ ๋๋ค. ๋จ th:์์ฑ์ ์ด์ฉํ๋ฉด ์๋์ ํ๊ทธ๋ค์ ์ด์ฉํด ์์ฑ์ ๋ฐ๊ฟ ์ ์๋ค.
th:attrappend : ์์ฑ ๊ฐ์ ๋ค์ ๊ฐ์ ์ถ๊ฐํ๋ค.
th:attrprepend : ์์ฑ ๊ฐ์ ์์ ๊ฐ์ ์ถ๊ฐํ๋ค.
th:classappend : class ์์ฑ์ผ๋ก ํด๋น ๊ฐ์ ์ถ๊ฐํ๋ค.
- th:attrappend = <input type="text" class="text" th:attrappend="class='large'" /><br/>
- th:attrprepend = <input type="text" class="text" th:attrprepend="class='large'" /><br/>
- th:classappend = <input type="text" class="text" th:classappend="large" /><br/>
- ๋๋ ์๋์ ๊ฐ์ด ์ฒดํฌ๋ฐ์ค ์ฒ๋ฆฌ๋ฑ์ ํ ๋ th:checked๋ฅผ ์ฌ์ฉํ๋ฉด boolean ๋ก์ง์ ๋ง๋ค๊ธฐ ํธํ๋ค.
- checked o <input type="checkbox" name="active" th:checked="true" /><br/>
- checked x <input type="checkbox" name="active" th:checked="false" /><br/>
<!-- ๊ธฐ์กด์ HTML์ checked ์์ฑ์ด ์์ผ๋ฉด ๊ทธ๋ฅ ์ฒดํฌํด๋ฒ๋ฆผ checked=boolean ์ต์
์์ -->
- checked=false <input type="checkbox" name="active" checked="false" /><br/>
# ๋ฐ๋ณต๋ฌธ (th:each)
์๋์ ๊ฐ์ users ๊ฐ ์ฃผ์ด์ก์ ๋ th:each="user : ${users}"๋ก ์ํํ ์ ์๋ค. ์ด๋ ๊ฒ ์์ฑํ๋ฉด user๋ฅผ ์ฌ์ฉํ ํ๊ทธ๊ฐ ๊ฐ ๋ฐฐ์ด์ ๊ฐ์๋งํผ ๋ฐ๋ณต๋๋ค.
- Map์ ๊ฒฝ์ฐ ์ธ์๋ก Entry(key, value)๊ฐ ๋ด๊ธฐ๊ฒ ๋๋ค.
@GetMapping("/each")
public String each(Model model) {
addUsers(model);
return "basic/each";
}
private void 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);
}
<table border="1">
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
</table>
th:each๋ฅผ ์ฌ์ฉํ ๋, 2๋ฒ์งธ ์ธ์(ex. userStat)๋ฅผ ์ฃผ์ด์ ๋ฐ๋ณต ์ธ๋ฑ์ค, ์ ์ฒดํฌ๊ธฐ, ํธ์์ฉ ์กฐ๊ฑด์ฐ์ฐ์๋ฑ์ ์ฌ์ฉํ ์ ์๋ค.
<tr th:each="user, userStat : ${users}"> <!-- ์ด๋ฆ์ ๋ณ๊ฒฝํด๋ ๋๋ค. -->
index 0๋ถํฐ ์์ = <span th:text="${userStat.index}"></span>
count 1๋ถํฐ ์์ = <span th:text="${userStat.count}"></span>
size ๋ฐฐ์ด ํฌ๊ธฐ = <span th:text="${userStat.size}"></span>
even? ์ง์์ฌ๋ถ boolean = <span th:text="${userStat.even}"></span>
odd? ํ์์ฌ๋ถ boolean = <span th:text="${userStat.odd}"></span>
first? ์ฒ์์ฌ๋ถ boolean = <span th:text="${userStat.first}"></span>
last? = ๋ง์ง๋ง์ฌ๋ถ boolean = <span th:text="${userStat.last}"></span>
current = ํ์ฌ ๊ฐ์ฒด = <span th:text="${userStat.current}"></span>
# ์กฐ๊ฑด๋ฌธ ( th:if , th:unless , th:switch )
์กฐ๊ฑด์ ๋ง์กฑํ์ง ์์์ ๋, ํด๋น ํ๊ทธ๋ฅผ ์์ ๋ฒ๋ฆด ์ ์๋ค. ์ฐธ๊ณ ๋ก unless๋ if์ ๋ถ์ ๋ฌธ์ด๋ค.
- switch์์ default case๋ th:case="*" ๋ก ์ง์ ํ ์ ์๋ค.
<span th:text="${user.age}">0</span>
<span th:text="'20์ด'" th:if="${user.age == 20}"></span>
<span th:text="'20์ด์ด์'" th:if="${user.age ge 20}"></span>
<span th:text="'20์ด๋ฏธ๋ง'" th:unless="${user.age ge 20}"></span>
<td th:switch="${user.age}">
<span th:case="10">10์ด</span>
<span th:case="20">20์ด</span>
<span th:case="*">๊ธฐํ</span>
</td>
# ๋ธ๋ก <th:block>
ํ์๋ฆฌํ๋ HTML ๋ฌธ๋ฒ์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ค. ์ ์ผํ๊ฒ <th:block>๋ง ํ์๋ฆฌํ ์ ์ฉ ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด์ฐจํผ ๋ ๋๋ง๋๋ฉด ์ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ด๋ค.
- React์ <Fragment>์ ๋น์ทํ๋ค. ํน์ ํ๊ทธ๋ก ๊ฐ์ธ์ง ์๊ณ ๊ทธ๋๋ก ์ฌ์ฉํ๊ธฐ ์ํด์ ์ด๋ค.
<!-- th: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>
# ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ script th:inline="javascript"
ํ์๋ฆฌํ์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ <script>๋ฅผ ์ฌ์ฉํ๊ณ ์ํ๋ฉด, ๋ฐ๋์ <script th:inline="javascript"> ์ต์ ์ ์ฃผ์ด์ผ ํ๋ค. ์ด๋ ๊ธฐ์กด ํ๊ทธ๋ค์ th:tag๋ก ๋์ฒดํด์ ์ฌ์ฉํ๋ ์ด์ ์ ๋น์ทํ๋ค.
<!-- ์๋ฐ์คํฌ๋ฆฝํธ ์ธ๋ผ์ธ ์ฌ์ฉ ์ -->
<script>
var username = [[${user.username}]]; // ๊ทธ๋ฅ ํ์๋ฆฌํ ๋ฌธ๋ฒ์ ์ถ๊ฐํ ๊ฒฝ์ฐ
var age = [[${user.age}]];
//์ฃผ์์ ์ด์ฉํด ์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ง ์ฌ์ฉํ ์๋ ์๋ค. user.username ์ด ๋์ ๋ค์ด๊ฐ.
var username2 = /*[[${user.username}]]*/ "test username";
//๊ฐ์ฒด
var user = [[${user}]];
</script>
- ํด๋น ์คํฌ๋ฆฝํธ๋ฅผ HTML๋ก ๋ ๋๋งํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ๊ทธ ์ด์ ๋ ๊ฒฐ๊ณผ HTML์ ๋ณด๋ฉด ์ ์ ์๋ค.
- ์ฝ๋์์์ "" ๋ฅผ ์ถ๊ฐํ๋ฉด ๋ ๊น? ๋ ๋๋ง ๋ ์ง์ง "๋ฌธ์์ด"์ธ์ง ํ์๋ฆฌํ ๋ณ์์ธ์ง ์ด๋ป๊ฒ ๊ตฌ๋ถํ ์ ์์๊น?
// ์ด๊ฒ ํ์๋ฆฌํ ๋ณ์์ธ์ง, ๊ทธ๋ฅ "[[..]]" ๋ฌธ์์ด์ธ์ง ์ด๋ป๊ฒ ์ ์ ์์๊น?
var username = "[[${user.username}]]";
์ด๋ฐ ๊ฒฝ์ฐ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์, ํ์๋ฆฌํ์๋ <script th:inline = "javascript"> ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
<!-- th:inline = "javascript ์ฌ์ฉ์ ๋ ๋๋ง ๊ฒฐ๊ณผHTML -->
<script>
var username = "userA"; // ๋ฌธ์์ด์ ๋ฐ์ํ๋ก ๊ฐ์ธ์ค๋ค
var age = 10; // ์ซ์๋ ๊ทธ๋๋ก ์ฒ๋ฆฌ
var username2 = "userA"; // ์๋ฐ์คํฌ๋ฆฝํธ ํ์๋ฆฌํ ์ฒ๋ฆฌ๋ ๊น๋ํ๊ฒ ํด์ค.
var user = {"username":"userA","age":10}; // ๊ฐ์ฒด๋ ์ฝ๋๋ฅผ JSON{..} ๋ฌธ๋ฒ์ผ๋ก ๋ฐ๊ฟ์ค๋ค
</script>
# ์๋ฐ์คํฌ๋ฆฝํธ ์์์ ํ์๋ฆฌํ์ th:each ์ฌ์ฉ
[# th:each= "..." ] ~Javascript Code~ [/] ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
<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>
# ํ ํ๋ฆฟ ์กฐ๊ฐ (th:fragment)
๋จผ์ ์ฌ์ฌ์ฉํ ํ ํ๋ฆฟ์ ๋ง๋ค์ด๋์. ๋ณดํต fragment ํด๋๋ฅผ ๋ง๋ค์ด ๋ชจ์๋๊ณ ๋ ํ๋ค.
- ํ๊ทธ์ th:fragment ="name" ์ ์ด์ฉํ์ฌ ํ ํ๋ฆฟ ์ด๋ฆ๊ณผ ์ ๋ ฅ๋ฐ์ ํ๋ผ๋ฉํ๋ฅผ ์ง์ ํด์ค ์ ์๋ค.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!-- footer.html 1๋ฒ ํ
ํ๋ฆฟ -->
<footer th:fragment="copy">
ํธํฐ ์๋ฆฌ ์
๋๋ค.
</footer>
<!-- footer.html 2๋ฒ ํ
ํ๋ฆฟ -->
<footer th:fragment="copyParam (param1, param2)">
<p>ํ๋ผ๋ฏธํฐ ์๋ฆฌ ์
๋๋ค.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
</body>
</html>
์ด๋ ๊ฒ ๋ง๋ ํ ํ๋ฆฟ์ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค. (ํ๋ก์ ํธ resource/templates๋ฅผ ๋ฒ ์ด์ค ๊ฒฝ๋ก๋ก ํ์ํ๋ค.)
- insert๋ ํด๋น ํ๊ทธ ์ ๋ด์ฉ์ ๋ง๋ค๊ณ , replace๋ ํด๋น ํ๊ทธ๋ฅผ ์ญ์ ํ ํ ๋ง๋ ๋ค.
<h1>๋ถ๋ถ ํฌํจ insert</h1>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<!-- ํ๋ก์ ํธ๊ฒฝ๋ก/resource/templates/template/fragment/footer.html ์ ์๋ copy ํ๊ทธ -->
<h1>๋ถ๋ถ ํฌํจ replace</h1>
<div th:replace="~{template/fragment/footer::copy}"></div>
<h1>ํ๋ผ๋ฏธํฐ ์ฌ์ฉ</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('๋ฐ์ดํฐ1', '๋ฐ์ดํฐ2')}"></div>
๋ค๋ง ~{...} ๋ฅผ ์๋ตํด๋ ๊ฒฝ๋ก ์ธ์์ด ๊ฐ๋ฅํ ๊ฒฝ์ฐ, ์๋ตํด๋ ๋๊ฐ์ด ๋์ํ๋ค.
<!-- ~{}๋ ์๋ต๊ฐ๋ฅ -->
<div th:replace="template/fragment/footer :: copy"></div>
<div th:replace="template/fragment/footer :: copyParam ('๋ฐ์ดํฐ1', '๋ฐ์ดํฐ2')"></div>
# ์ฌ์ฌ์ฉ๊ฐ๋ฅํ ๋ ์ด์์ ๋ง๋ค๊ธฐ
์๋์ ๊ฐ์ด th:replace๋ฅผ ์ง์ ํด์ฃผ๋ฉด, ํด๋น ํ๊ทธ ์์ ์๋ ๋ด์ฉ์ด ์ญ์ ๋๊ณ ์ง์ ํ ํ ํ๋ฆฟ์ผ๋ก ๊ต์ฒด๋๋ค.
์ด๋, ํ ํ๋ฆฟ์์ [ํน์ ํ๊ทธ ์์ ๊ธฐ์กด ๋ด์ฉ] ์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
- ํ๋ผ๋ฉํ์ ~{::TagName} ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ด์ฉ์ ์ ํ์๋ ๋ชจ๋ ํด๋น ํ๊ทธ(TagName)๋ฅผ ํ๋ผ๋ฉํ๋ก ๋๊ธด๋ค.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!-- head ํ๊ทธ์ ๋ด์ฉ์ ์ญ์ ๋๊ณ , common_header๋ก ๊ต์ฒด๋จ -->
<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>
- th:replace๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ด์ฐจํผ ๊ต์ฒด๋ ๊ฑฐ๋ผ ๊ธฐ์กด ํ๊ทธ๋ฅผ ๋ฌด์์ ์ฌ์ฉํ๋ ์๊ด์๋ค. (๋์ถฉ th:block์ผ๋ก ํด๋๋จ)
ํ์ง๋ง ํ๊ทธ๋ฅผ ์๋ฏธ์๊ฒ ์ฌ์ฉํ๋๊ฒ, ํ ํ๋ฆฟ์ ๋ง๋ค๋๋ ํธํ๊ณ HTML์ ๊ฐ๋ ์ฑ๋ ์ฌ๋ผ๊ฐ๋ค.
์ฐธ๊ณ ๋ก ~{::tag} ๋ก ๋ฐ์๋๋ฐ, ๊ธฐ์กด์ฝ๋์ ํด๋นํ๊ทธ๊ฐ 3๊ฐ๋ผ๋ฉด, th:replace์์๋ ๋ฐ๋ณต๋ฌธ์ฒ๋ผ 3๊ฐ ์ฐ๋ฌ์ ๋ง๋ค์ด์ง๋ค.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!-- common_header ํ
ํ๋ฆฟ ์ ์, template/layout/base.html-->
<head th:fragment="common_header(myTitle, myLinks)">
<!-- ์ถ๊ฐ, ํ๋ผ๋ฉํ ์
๋ ฅ์ผ๋ก ๋ฐ์ ์๋งํผ title ํ๊ทธ๋ฅผ ๋ง๋ฌ. -->
<title th:replace="${myTitle}">๋ ์ด์์ ํ์ดํ</title>
<!-- ๊ณตํต, ๋ชจ๋ ๊ณณ์ ๊ณตํต ๋ ์ด์์์ ์ ์ฉ -->
<link media="all" rel="stylesheet" th:href="@{/css/awesomeapp.css}" type="text/css">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script th:src="@{/sh/scripts/codebase.js}" type="text/javascript"></script>
<!-- ์ถ๊ฐ, ํ๋ผ๋ฉํ ์
๋ ฅ์ผ๋ก ๋ฐ์ ์ ๋งํผ link ํ๊ทธ๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํจ. -->
<th:block th:replace="${myLinks}"/>
</head>
</html>
# ์ ์ฒด HTML์ ๊ณตํต ๋ ์ด์์ ์ ์ฉํ๊ธฐ (Extends)
<html> ํ๊ทธ์ ํ ํ๋ฆฟ์ ์ ์ฉํ๋ฉด, ํ์ด์ง์ ๊ณตํต์ ์ธ ๋ ์ด์์์ ์ ์ฉํ๊ณ ํ์ํ ๋ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: myLayout(~{::title}, ~{::section})}"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>๋ฉ์ธ ํ์ด์ง ํ์ดํ</title>
</head>
<body>
<section>
<p>๋ฉ์ธ ํ์ด์ง ์ปจํ
์ธ </p>
<div>๋ฉ์ธ ํ์ด์ง ํฌํจ ๋ด์ฉ</div>
</section>
</body>
</html>
- ์ ์ฒด HTML์ด ์๋ ํ
ํ๋ฆฟ์ ๊ฑฐ์ณ์ ๋ ๋๋ง ๋๋ค. ๊ด๋ฆฌํ๋ ํ์ด์ง๊ฐ ๋ง์ ๊ฒฝ์ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
์ด ๋ th:replace๋ฅผ ์ฌ์ฉํ ํ๊ทธ๋, ์ด์ฐจํผ ๋ด์ฉ์ด ์ญ์ ๋๋ฏ๋ก ์ฃผ์์ผ๋ก ์ฌ์ฉํ๋ฉด ์ ์ฉํ๋ค.
<!DOCTYPE html>
<!-- template/layoutExtend/layoutFile.html ์์ ์๋ layout(title, content) ํ
ํ๋ฆฟ -->
<html th:fragment="myLayout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">~ ์ ์ฉ๋ ํ์ดํ th:replace๋ผ ์ด ํ
์คํธ๋ ์ญ์ ๋จ~</title>
</head>
<body>
<h1>๋ ์ด์์ H1</h1>
<div th:replace="${content}">
<p>~ ์ ์ฉ๋ ์ปจํ
์ธ , th:replace๋ผ ์ด ํ
์คํธ๋ ์ญ์ ๋จ ~</p>
</div>
<footer>
๋ ์ด์์ ํธํฐ
</footer>
</body>
</html>
# ์ ํ๋ณ์ *{...} ์ th:object
์ ํ๋ณ์ *{...} ์ th:object ๋ฅผ ์ด์ฉํด์ ๊ฐ์ฒด์์ ๊ฐ์ ๊บผ๋ผ ๋ ์ค๋ณต๋๋ ์ฝ๋๋ฅผ ์ค์ผ ์ ์๋ค.
myClass.session.user ์ด๋ฐ ์ฝ๋๋ฅผ th:object= ${myClass.session}๋ก ์ ์ธํ๊ณ , ์ดํ *{user} ๋ก ํธํ๊ฒ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ.
- ๋น์ฐํ๊ฑฐ์ง๋ง, th:object๋ฅผ ์ ์ ํ๊ทธ ๋ด๋ถ(ํ์ ํ๊ทธ)์์๋ง ์ฌ์ฉํ ์ ์๋ค.
<!-- ๋ ๋๋ง ์ , ์ ํ ๋ณ์ *{...} ์ฌ์ฉ -->
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
<!-- ๋ ๋๋ง ์ดํ -->
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
# ๋ฉ์์ง ๊ธฐ๋ฅ #{...}
์์์ ์น ๊ธฐ๋ณธ ๊ฐ์ฒด๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ${#...} ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค๊ณ ํ๋ค.
๊ทธ๋ฅ ์ธ๋ถ ํ์ผ์ ๊ฐ์ ์ฝ์ด์ค๊ณ ์ถ์๊ฒฝ์ฐ #{...}์ ์ฌ์ฉํ๋ฉด ๋๋ค. thymeleaf engine ๊ฒฝ๋ก์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด์ ธ์๋ ํ๋กํผํฐ ํ์ผ(message properties)์ด ์กด์ฌํ๋ ๊ฒฝ์ฐ ์ธ๋ถ์์ ํด๋น ๊ฐ์ ๊ฐ์ ธ์ค๋ ๊ธฐ๋ฅ์ด๋ค.
์ด๋ ๋์ค์ ๊ตญ์ ํ & ๋ฉ์์ง ๊ธฐ๋ฅ์ ๋ฐฐ์ฐ๊ฒ๋๋ฉด ์ ์ด๋ฐ๊ฑธ ๋ฐ๋ก ์ ๊ณตํ๋์ง ์ ์ ์๊ฒ๋๋ค.
// message properties ํ์ผ
home.welcome = ์๋
ํ์ธ์? ๋ฐ๊ฐ์ต๋๋ค
home.param = ์ด๊ฑด {0} ํ๋ผ๋ฉํ {1}.
<p th:text="#{home.welcome}">์ธ์ฌ๋ง</p>
<p th:text="#{home.param(${name}, ${end})}">์ธ์ฌ๋ง(๊ณ ๊ฐ๋ช
)</p>
- ์คํ๋ง์์ ๋ฐ์ Model ์์ [ name= "๊ฐ์์ง", end= "์ ๋๋ค" ] ์ด๋ ๊ฒ ์ ์ฅ๋์ด์๋ค๋ฉด ์๋์ ๊ฐ์ด ๋ ๋๋ง ๋๋ค.
<p> ์๋
ํ์ธ์? ๋ฐ๊ฐ์ต๋๋ค </p>
<p> ์ด๊ฑด ๊ฐ์์ง ํ๋ผ๋ฉํ ์
๋๋ค. </p>
'๐ฑ Spring Framework > Spring MVC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring ๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ (0) | 2021.08.28 |
---|---|
Thymeleaf#2 Spring๊ณผ HTML Form ์ฒ๋ฆฌ (0) | 2021.08.28 |
#2 HTTP API ์์ฒญ ๋งคํ, ํค๋ ์กฐํ (0) | 2021.08.11 |
#1 HTTP ์์ฒญ ๋งคํ๊ณผ ๊ธฐ๋ณธ ๋ก๊น (0) | 2021.08.11 |
# HTTP ๋ฐ์ดํฐ ์ ์ก/์๋ต (0) | 2021.08.11 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev