Skip to content

Latest commit

ย 

History

History
963 lines (745 loc) ยท 29 KB

thymeleaf.md

File metadata and controls

963 lines (745 loc) ยท 29 KB

7. ํƒ€์ž„๋ฆฌํ”„

์ข‹์€ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๋ ค๋ฉด, JSP๋“  ํƒ€์ž„๋ฆฌํ”„๋“  SSR ๋ Œ๋”๋ง ๊ธฐ์ˆ  ์ค‘ ํ•œ ๊ฐ€์ง€ ์ •๋„๋Š” ๋Šฅ์ˆ™ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๊ทธ ์ค‘์—์„œ๋„ ์Šคํ”„๋ง๊ณผ ํ†ตํ•ฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ง€์›ํ•˜๋Š” View ํ…œํ”Œ๋ฆฟ ํƒ€์ž„๋ฆฌํ”„์˜ ๊ฐœ๋…์„ ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณด๊ณ , ์‹ค์ œ ๋™์ž‘ํ•˜๋Š” ๊ธฐ๋Šฅ ์œ„์ฃผ๋กœ ๊ณต๋ถ€ํ•ด๋ณด์ž.

ํƒ€์ž„๋ฆฌํ”„ ์†Œ๊ฐœ

ํƒ€์ž„๋ฆฌํ”„ ํŠน์ง•

  • ์„œ๋ฒ„ ์‚ฌ์ด๋“œ HTML ๋ Œ๋”๋ง (SSR)
  • ๋„ค์ธ„๋Ÿด ํ…œํ”Œ๋ฆฟ
  • ์Šคํ”„๋ง ํ†ตํ•ฉ ์ง€์›

์„œ๋ฒ„ ์‚ฌ์ด๋“œ HTML ๋ Œ๋”๋ง

ํƒ€์ž„๋ฆฌํ”„๋Š” ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์—์„œ HTML์„ ๋™์ ์œผ๋กœ ๋ Œ๋”๋ง ํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋œ๋‹ค

๋„ค์ธ„๋Ÿด ํ…œํ”Œ๋ฆฟ

ํƒ€์ž„๋ฆฌํ”„๋Š” ์ˆœ์ˆ˜ HTML์„ ์ตœ๋Œ€ํ•œ ์œ ์ง€ํ•˜๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.

ํƒ€์ž„๋ฆฌํ”„๋กœ ์ž‘์„ฑํ•œ ํŒŒ์ผ์€ HTML์„ ์œ ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ํŒŒ์ผ์„ ์ง์ ‘ ์—ด์–ด๋„ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ๋ทฐ ํ…œํ”Œ๋ฆฟ์„ ๊ฑฐ์น˜๋ฉด ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

JSP๋ฅผ ํฌํ•จํ•œ ๋‹ค๋ฅธ ๋ทฐ ํ…œํ”Œ๋ฆฟ๋“ค์€ ํ•ด๋‹น ํŒŒ์ผ์„ ์—ด๋ฉด, JSP ํŒŒ์ผ ์ž์ฒด๋ฅผ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ทธ๋Œ€๋กœ ์—ด์–ด๋ณด๋ฉด JSP ์†Œ์Šค์ฝ”๋“œ์™€ HTML์ด ๋’ค์ฃฝ๋ฐ•์ฃฝ ์„ž์—ฌ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ •์ƒ์ ์ธ HTML ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†๋‹ค. ์˜ค์ง ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด์„œ JSP๊ฐ€ ๋ Œ๋”๋ง ๋˜๊ณ  HTML ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์•ผ ํ™”๋ฉด์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐ˜๋ฉด์— ํƒ€์ž„๋ฆฌํ”„๋กœ ์ž‘์„ฑ๋œ ํŒŒ์ผ์€ ํ•ด๋‹น ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์—ด์–ด๋„ ์ •์ƒ์ ์ธ HTML ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ์ด ๊ฒฝ์šฐ ๋™์ ์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋ Œ๋”๋ง ๋˜์ง€๋Š” ์•Š์ง€๋งŒ, ํŒŒ์ผ๋งŒ ์—ด์–ด๋„ HTML ๋งˆํฌ์—… ๊ฒฐ๊ณผ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ˆœ์ˆ˜ HTML์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ทฐ ํ…œํ”Œ๋ฆฟ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž„๋ฆฌํ”„์˜ ํŠน์ง•์„ ๋„ค์ธ„๋Ÿด ํ…œํ”Œ๋ฆฟ(natural templates)์ด๋ผ ํ•œ๋‹ค.

์Šคํ”„๋ง ํ†ตํ•ฉ ์ง€์›

ํƒ€์ž„๋ฆฌํ”„๋Š” ์Šคํ”„๋ง๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋˜๊ณ , ์Šคํ”„๋ง์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ง€์›ํ•œ๋‹ค.


ํƒ€์ž„๋ฆฌํ”„ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ

  • ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ ์„ ์–ธ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
<html xmlns:th ="http://www.thymeleaf.org">

๊ธฐ๋ณธ ํ‘œํ˜„์‹: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#standard-expression-syntax


ํ…์ŠคํŠธ - text, utext

ํƒ€์ž„๋ฆฌํ”„์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ์ธ ํ…์ŠคํŠธ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

ํƒ€์ž„๋ฆฌํ”„๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HTML ํƒœ๊ทธ์˜ ์†์„ฑ์— ๊ธฐ๋Šฅ์„ ์ •์˜ํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค.
HTML์˜ ์ฝ˜ํ…์ธ ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด th:text๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

<span th:text="${data}">

HTML ํƒœ๊ทธ์˜ ์†์„ฑ์ด ์•„๋‹ˆ๋ผ HTML ์ฝ˜ํ…์ธ  ์˜์—ญ์•ˆ์—์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด [[...]]๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

package hello.thymeleaf.basic;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/basic")
public class BasicController {

    @GetMapping("text-basic")
    public String textBasic(Model model) {
        model.addAttribute("data", "Hello Spring!");
        return "basic/text-basic";
    }
}
<!DOCTYPE html>
<html 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>

๊ฐ„๋‹จํ•˜๊ฒŒ Hello Spring ๋ฌธ์žฅ์„ thymeleaf๋ฅผ ์ด์šฉํ•ด์„œ ์ถœ๋ ฅ์‹œ์ผœ๋ณด์•˜๋‹ค.

Escape

HTML ๋ฌธ์„œ๋Š” <,> ๊ฐ™์€ ํŠน์ˆ˜ ๋ฌธ์ž๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ •์˜๋œ๋‹ค.
๋”ฐ๋ผ์„œ ๋ทฐ ํ…œํ”Œ๋ฆฟ์œผ๋กœ HTML ํ™”๋ฉด์„ ์ƒ์„ฑํ•  ๋•Œ๋Š” ์ถœ๋ ฅํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์ด๋Ÿฌํ•œ ํŠน์ˆ˜ ๋ฌธ์ž๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

model.addAttribute("data", "Hello <b>Spring!</b>");

์œ„ Hello Spring! ๋ฌธ์žฅ ์ค‘ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ Spring! ์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ์ง„ํ•˜๊ฒŒ ๋‚˜์˜ค๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณด์•˜๋‹ค.

๋‚ด๊ฐ€ ๊ธฐ๋Œ€ํ–ˆ๋˜ ๊ฒฐ๊ณผ๋Š” Spring! ๋‹จ์–ด๊ฐ€ ์ง„ํ•˜๊ฒŒ ๋‚˜์˜ค๋Š” ๊ฒƒ์ด์—ˆ์ง€๋งŒ, ํƒœ๊ทธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณด์ด๋Š” ์†Œ์Šค์ฝ”๋“œ๋„ ์ด์ƒํ•œ ๋ฌธ์ž๋“ค์ด ์„ž์—ฌ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

HTML ์—”ํ‹ฐํ‹ฐ

  • ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” < ๋ฅผ HTML ํƒœ๊ทธ์˜ ์‹œ์ž‘์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.
  • ๋”ฐ๋ผ์„œ < ๋ฅผ ํƒœ๊ทธ์˜ ์‹œ์ž‘์ด ์•„๋‹ˆ๋ผ ๋ฌธ์ž๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•œ๋ฐ ์ด ๊ฒƒ์„ HTML ์—”ํ‹ฐํ‹ฐ๋ผ ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ HTML์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ˆ˜๋ฌธ์ž๋ฅผ HTML ์—”ํ‹ฐํ‹ฐ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์ด์Šค์ผ€์ดํ”„(escape)๋ผ ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ œ๊ณตํ•˜๋Š” th:text, [[...]] ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด์Šค์ผ€์ดํ”„(escape)๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • < -> &lt
  • < -> &gt
  • ๊ธฐํƒ€ ์ˆ˜ ๋งŽ์€ HTML ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์žˆ๋‹ค.

Unescape

ํƒ€์ž„๋ฆฌํ”„๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด์Šค์ผ€์ดํ”„๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋Šฅ์„ ์›ํ•˜์ง€ ์•Š์„๋•Œ๋Š”

  • th:text -> th:utext
  • [[...]] -> [(...)]

๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค.

<!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>

  • ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด escape๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์„œ HTML์ด ์ •์ƒ ๋ Œ๋”๋ง ๋˜์ง€ ์•Š๋Š” ์ˆ˜๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • escape๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ํ•˜๊ณ , ๊ผญ ํ•„์š”ํ•  ๋•Œ๋งŒ unescape๋ฅผ ์‚ฌ์šฉํ•˜์ž!

๋ณ€์ˆ˜ - SpringEL

ํƒ€์ž„๋ฆฌํ”„์—์„œ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ณ€์ˆ˜ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๋ณ€์ˆ˜ ํ‘œํ˜„์‹: ${...}

์ด ๋ณ€์ˆ˜ ํ‘œํ˜„์‹์—๋Š” ์Šคํ”„๋ง EL์ด๋ผ๋Š” ์Šคํ”„๋ง์ด ์ œ๊ณตํ•˜๋Š” ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<!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>
</body>
</html>

์ง€์—ญ ๋ณ€์ˆ˜ ์„ ์–ธ

th:with๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ง€์—ญ ๋ณ€์ˆ˜๋Š” ์„ ์–ธํ•œ ํƒœ๊ทธ ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

<h1>์ง€์—ญ ๋ณ€์ˆ˜ - (th:with)</h1>
<div th:with="first=${users[0]}">
    <p>์ฒ˜์Œ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„์€ <span th:text="${first.username}"></span></p>
</div>

๊ธฐ๋ณธ ๊ฐ์ฒด๋“ค

ํƒ€์ž„ ๋ฆฌํ”„๋Š” ๊ธฐ๋ณธ ๊ฐ์ฒด๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

  • ${#request}
  • ${#response}
  • ${#session}
  • ${#servletContext}
  • ${#locale}

๊ทธ๋Ÿฐ๋ฐ #request๋Š” HttpServletRequest ๊ฐ์ฒด๊ฐ€ ๊ทธ๋Œ€๋กœ ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋ ค๋ฉด
request.getParameter("data")์ฒ˜๋Ÿผ ๋ถˆํŽธํ•˜๊ฒŒ ์ ‘๊ทผํ•ด์•ผ ํ•œ๋‹ค.

์ด๋Ÿฐ ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํŽธ์˜ ๊ฐ์ฒด๋„ ์ œ๊ณตํ•œ๋‹ค.

-HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ์ ‘๊ทผ: param -ex) ${param.paramData} -HTTP ์„ธ์…˜ ์ ‘๊ทผ: session -ex) ${session.sessionData} -์Šคํ”„๋ง ๋นˆ ์ ‘๊ทผ: @ -ex) ${@helloBean.hello('Spring!')}

@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;
        }
    }
<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>


์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด์™€ ๋‚ ์งœ

ํƒ€์ž„๋ฆฌํ”„๋Š” ๋ฌธ์ž, ์ˆซ์ž, ๋‚ ์งœ URI๋“ฑ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋‹ค๋ฃจ๋Š” ๋‹ค์–‘ํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

ํƒ€์ž„๋ฆฌํ”„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด๋“ค

  • #message : ๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™” ์ฒ˜๋ฆฌ
  • #uris : URI ์ด์Šค์ผ€์ดํ”„ ์ง€์›
  • #dates : java.util.Date ์„œ์‹ ์ง€์›
  • #calendars : java.util.Calendar ์„œ์‹ ์ง€์›
  • #temporals : ์ž๋ฐ”8 ๋‚ ์งœ ์„œ์‹ ์ง€์›
  • #numbers : ์ˆซ์ž ์„œ์‹ ์ง€์›
  • #strings : ๋ฌธ์ž ๊ด€๋ จ ํŽธ์˜ ๊ธฐ๋Šฅ
  • #objects : ๊ฐ์ฒด ๊ด€๋ จ ๊ธฐ๋Šฅ ์ œ๊ณต
  • #bools : boolean ๊ด€๋ จ ๊ธฐ๋Šฅ ์ œ๊ณต
  • #arrays : ๋ฐฐ์—ด ๊ด€๋ จ ๊ธฐ๋Šฅ ์ œ๊ณต
  • #lists , #sets , #maps : ์ปฌ๋ ‰์…˜ ๊ด€๋ จ ๊ธฐ๋Šฅ ์ œ๊ณต
  • #ids : ์•„์ด๋”” ์ฒ˜๋ฆฌ ๊ด€๋ จ ๊ธฐ๋Šฅ ์ œ๊ณต, ๋’ค์—์„œ ์„ค๋ช…

ํƒ€์ž„๋ฆฌํ”„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด

์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด ์˜ˆ์‹œ

์ด๋Ÿฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด๋“ค์€ ๋Œ€๋žต ์ด๋Ÿฐ ๊ฒƒ์ด ์žˆ๋‹ค ์•Œ์•„๋‘๊ณ , ํ•„์š”ํ•  ๋•Œ ์ฐพ์•„์„œ ์‚ฌ์šฉํ•˜์ž!

์ž๋ฐ”8 ๋‚ ์งœ

ํƒ€์ž„๋ฆฌํ”„์—์„œ ์ž๋ฐ”8 ๋‚ ์งœ์ธ LocalDate, LocalDateTime, Instant๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ถ”๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ๋ฐ,
์Šคํ”„๋ง ๋ถ€ํŠธ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜๊ณ  ํ†ตํ•ฉ๋œ๋‹ค.

ํƒ€์ž„๋ฆฌํ”„ ์ž๋ฐ”8 ๋‚ ์งœ ์ง€์› ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
thymeleaf-extras-java8time

์ž๋ฐ”8 ๋‚ ์งœ์šฉ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด
#temporals

@GetMapping("/date")
    public String data(Model model) {
        model.addAttribute("localDateTime", LocalDateTime.now());
        return "basic/date";
    }
<span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span>

URL ๋งํฌ

ํƒ€์ž„๋ฆฌํ”„์—์„œ URL์„ ์ƒ์„ฑํ•  ๋•Œ๋Š” @{...} ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@GetMapping("/link")
    public String link(Model model) {
        model.addAttribute("param1", "data1");
        model.addAttribute("param2", "data2");
        return "basic/link";
    }

๋‹จ์ˆœํ•œ URL

<li><a th:href="@{/hello}">basic url</a></li> 
<!--http://localhost:8080/hello-->

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ

<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li> 
<!--http://localhost:8080/hello?param1=data1&param2=data2-->

๊ฒฝ๋กœ ๋ณ€์ˆ˜

<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<!--http://localhost:8080/hello/data1/data2-->

๊ฒฝ๋กœ ๋ณ€์ˆ˜ + ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ

<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
<!--http://localhost:8080/hello/data1?param2=data2-->

๋ฆฌํ„ฐ๋Ÿด

Literals

๋ฆฌํ„ฐ๋Ÿด์€ ์†Œ์Šค ์ฝ”๋“œ์ƒ์— ๊ณ ์ •๋œ ๊ฐ’์„ ๋งํ•˜๋Š” ์šฉ์–ด์ด๋‹ค.

ํƒ€์ž„๋ฆฌํ”„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฆฌํ„ฐ๋Ÿด์ด ์žˆ๋‹ค.

  • ๋ฌธ์ž: 'hello'
  • ์ˆซ์ž: 10
  • ๋ถˆ๋ฆฐ: true, false
  • null: null

ํƒ€์ž„๋ฆฌํ”„์—์„œ ๋ฌธ์ž ๋ฆฌํ„ฐ๋Ÿด์€ ํ•ญ์ƒ ์ž‘์€ ๋”ฐ์˜ดํ‘œ๋กœ ๊ฐ์‹ธ์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋ฌธ์ž๋ฅผ ํ•ญ์ƒ '๋กœ ๊ฐ์‹ธ๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ๊ท€์ฐฎ์€ ์ผ์ด๋‹ค.
๊ณต๋ฐฑ ์—†์ด ์ญ‰ ์ด์–ด์ง„๋‹ค๋ฉด ํ•˜๋‚˜์˜ ์˜๋ฏธ์žˆ๋Š” ํ† ํฐ์œผ๋กœ ์ธ์ง€ํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์€ ๋”ฐ์˜ดํ‘œ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.
๋ฃฐ: A-Z, a-z, 0-9, [], ., -, _

์˜ค๋ฅ˜

<span th:text="hello world!"></span>

๋ฌธ์ž ๋ฆฌํ„ฐ๋Ÿด์€ ์›์น™์ƒ '๋กœ ๊ฐ์‹ธ์•ผ ํ•œ๋‹ค. ์ค‘๊ฐ„์— ๊ณต๋ฐฑ์ด ์žˆ์–ด์„œ ํ•˜๋‚˜์˜ ํ† ํฐ์œผ๋กœ๋„ ์ธ์‹๋˜์ง€ ์•Š๋Š”๋‹ค.

์ˆ˜์ •

<span th:text="'hello world!'"></span>
<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-->

๋ฆฌํ„ฐ๋Ÿด ๋Œ€์ฒด(Literal substitutions)

<span th:text="|hello $(data)|"> <!--hello Spring-->

์—ฐ์‚ฐ

ํƒ€์ž„๋ฆฌํ”„ ์—ฐ์‚ฐ์€ ์ž๋ฐ”์™€ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค. HTML ์•ˆ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— HTML ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ์ฃผ์˜ํ•˜์ž

  • ๋น„๊ต์—ฐ์‚ฐ: HTML ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์„ ์ฃผ์˜ํ•˜์ž,
    • > (gt), < (lt), >= (ge), <= (le), ! (not), == (eq), != (neq, ne)
<li>1 > 10 = <span th:text="1 &gt; 10"></span></li>
<li>1 gt 10 = <span th:text="1 gt 10"></span></li>
<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
<li>1 ge 10 = <span th:text="1 ge 10"></span></li>
<li>1 == 10 = <span th:text="1 == 10"></span></li>
<li>1 != 10 = <span th:text="1 != 10"></span></li>

  • ์กฐ๊ฑด์‹: ์ž๋ฐ”์˜ ์กฐ๊ฑด์‹๊ณ  ์œ ์‚ฌํ•˜๋‹ค.
<li>(10 % 2 == 0)? '์ง์ˆ˜':'ํ™€์ˆ˜' = <span th:text="(10 % 2 == 0)? '์ง์ˆ˜':'ํ™€์ˆ˜'"></span></li>

  • Elvis ์—ฐ์‚ฐ์ž: ์กฐ๊ฑด์‹์˜ ํŽธ์˜ ๋ฒ„์ „
<li>${data}?: '๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.' = <span th:text="${data}?: '๋ฐ์ดํ„ฐ๊ฐ€์—†์Šต๋‹ˆ๋‹ค.'"></span></li> <!--Spring -->
<li>${nullData}?: '๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.' = <span th:text="${nullData}?: '๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.'"></span></li> <!--๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค-->

  • No- Operation: _ ์ธ ๊ฒฝ์šฐ ๋งˆ์น˜ ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค.

์ด ๊ฒƒ์„ ์ž˜ ์‚ฌ์šฉํ•˜๋ฉด HTML์˜ ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์ง€๋ง‰ ์˜ˆ๋ฅผ ๋ณด๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ถ€๋ถ„์ด ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ๋œ๋‹ค.

<li>${data}?: _ = <span th:text="${data}?: _">๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</span></li> <!--Spring-->
<li>${nullData}?: _ = <span th:text="${nullData}?: _">๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</span></li> <!--๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค-->

๋ฐ˜๋ณต

ํƒ€์ž„๋ฆฌํ”„์—์„œ ๋ฐ˜๋ณต์€ th:each๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

@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);
    }

<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>

๋ฐ˜๋ณต ์ƒํƒœ ์œ ์ง€

<tr th:each="user, userStat : ${user}">

๋ฐ˜๋ณต์˜ ๋‘๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ค์ •ํ•ด์„œ ๋ฐ˜๋ณต์˜ ์ƒํƒœ๋ฅผ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ์ƒ๋žต ๊ฐ€๋Šฅํ•œ๋ฐ, ์ƒ๋žตํ•˜๋ฉด ์ง€์ •ํ•œ ๋ณ€์ˆ˜๋ช… (user) + Stat์ด ๋œ๋‹ค.

<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}">
        <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>
            count = <span th:text="${userStat.count}"></span>
            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>


์กฐ๊ฑด๋ถ€ ํ‰๊ฐ€

ํƒ€์ž„๋ฆฌํ”„์˜ ์กฐ๊ฑด์‹ if, unless

if, unless

ํƒ€์ž„๋ฆฌํ”„๋Š” ํ•ด๋‹น ์กฐ๊ฑด์ด ๋งž์ง€ ์•Š์œผ๋ฉด ํƒœ๊ทธ ์ž์ฒด๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š”๋‹ค.

<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>

switch

* ์€ ๋งŒ์กฑํ•˜๋Š” ์กฐ๊ฑด์ด ์—†์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋””ํดํŠธ์ด๋‹ค.

<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>


์ฃผ์„

@GetMapping("/comments")
    public String comments(Model model) {
        model.addAttribute("data", "Spring!");
        return "basic/comments";
    }
<h1>์˜ˆ์‹œ</h1>
<span th:text="${data}">html data</span>
<h1>1. ํ‘œ์ค€ 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>
/*/-->

1. ํ‘œ์ค€ HTML ์ฃผ์„
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ‘œ์ค€ HTML ์ฃผ์„์€ ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ๋ Œ๋”๋ง ํ•˜์ง€ ์•Š๊ณ , ๊ทธ๋Œ€๋กœ ๋‚จ๊ฒจ๋‘”๋‹ค.

2. ํƒ€์ž„๋ฆฌํ”„ ํŒŒ์„œ ์ฃผ์„
ํƒ€์ž„๋ฆฌํ”„ ํŒŒ์„œ ์ฃผ์„์€ ํƒ€์ž„๋ฆฌํ”„์˜ ์ง„์งœ ์ฃผ์„์ด๋‹ค. ๋ Œ๋”๋ง์—์„œ ์ฃผ์„ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.

3. ํƒ€์ž„๋ฆฌํ”„ ํ”„๋กœํ† ํƒ€์ž… ์ฃผ์„
ํƒ€์ž„๋ฆฌํ”„ ํ”„๋กœํ† ํƒ€์ž…์€ ์•ฝ๊ฐ„ ํŠน์ดํ•œ๋ฐ, HTML ์ฃผ์„์— ์•ฝ๊ฐ„์˜ ๊ตฌ๋ฌธ์„ ๋”ํ–ˆ๋‹ค.
HTML ํŒŒ์ผ์„ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ทธ๋Œ€๋กœ ์—ด์–ด๋ณด๋ฉด HTML ์ฃผ์„์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ถ€๋ถ„์ด ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ Œ๋”๋ง ํ•˜์ง€ ์•Š๋Š”๋‹ค.
ํƒ€์ž„๋ฆฌํ”„ ๋ Œ๋”๋ง์„ ๊ฑฐ์น˜๋ฉด ์ด ๋ถ€๋ถ„์ด ์ •์ƒ ๋ Œ๋”๋ง ๋œ๋‹ค.
์‰ฝ๊ฒŒ ์ด์•ผ๊ธฐํ•ด์„œ HTML ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ์—ด์–ด๋ณด๋ฉด ์ฃผ์„์ฒ˜๋ฆฌ๊ฐ€ ๋˜์ง€๋งŒ, ํƒ€์ž„๋ฆฌํ”„๋ฅผ ๋ Œ๋”๋ง ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋ณด์ด๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

ํƒ€์ž„๋ฆฌํ”„ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ

HTML ํŒŒ์ผ


๋ธ”๋ก

th:block์€ HTML ํƒœ๊ทธ๊ฐ€ ์•„๋‹Œ ํƒ€์ž„๋ฆฌํ”„์˜ ์œ ์ผํ•œ ์ž์ฒด ํƒœ๊ทธ๋‹ค.

<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">

@GetMapping("/javascript")
public String javascript(Model model) {
 model.addAttribute("user", new User("userA", 10));
 addUsers(model);
 return "basic/javascript";
}
<!-- ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ -->
<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 = [[${user.username}]];
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ -> var username = userA;
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ํ›„ -> var username = "userA";

์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , "userA" ๋Œ€์‹  userA ๋ผ๋Š” ๋ณ€์ˆ˜ ์ด๋ฆ„์ด ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๋‹ค.
๋‹ค์Œ ์œผ๋กœ ๋‚˜์˜ค๋Š” ์ˆซ์ž age์˜ ๊ฒฝ์šฐ์—๋Š” "๊ฐ€ ํ•„์š” ์—†๊ธฐ๋•Œ๋ฌธ์— ์ •์ƒ ๋ Œ๋”๋ง ๋œ๋‹ค.

์ธ๋ผ์ธ ์‚ฌ์šฉ ํ›„ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ๋ฌธ์ž ํƒ€์ž…์ธ ๊ฒฝ์šฐ " ๋ฅผ ํฌํ•จํ•ด์ค€๋‹ค.
์ถ”๊ฐ€๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ์ด์Šค์ผ€์ดํ”„ ์ฒ˜๋ฆฌ๋„ ํ•ด์ค€๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋‚ด์ถ”๋Ÿด ํ…œํ”Œ๋ฆฟ

ํƒ€์ž„๋ฆฌํ”„๋Š” HTML ํŒŒ์ผ์„ ์ง์ ‘ ์—ด์–ด๋„ ๋™์ž‘ํ•˜๋Š” ๋‚ด์ถ”๋Ÿด ํ…œํ”Œ๋ฆฟ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฃผ์„์„ ํ™œ์šฉํ•ด์„œ ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • var username2 = /*[[${user.username}]]*/ "test username";
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ -> var username2 = /*userA*/ "test username";
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ํ›„ -> var username2 = "userA";

์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ •๋ง ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ธ๋ผ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฃผ์„ ๋ถ€๋ถ„์€ ์ œ๊ฑฐ๋˜๊ณ , ๊ธฐ๋Œ€ํ–ˆ๋˜๋Œ€๋กœ "userA"๋งŒ ์ •ํ™•ํžˆ ์ถœ๋ ฅ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ฐ์ฒด

ํƒ€์ž„๋ฆฌํ”„์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ธ๋ผ์ธ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค.

  • var user = [[${user}]];
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „ -> var user = BasicController.User(username=userA, age=10);
    • ์ธ๋ผ์ธ ์‚ฌ์šฉ ํ›„ -> var user = {"username":"userA", "age":10};

์ธ๋ผ์ธ ์‚ฌ์šฉ ์ „์€ ๊ฐ์ฒด์˜ toString()์ด ํ˜ธ์ถœ๋œ ๊ฐ’์ด๋‹ค.
์ธ๋ผ์ธ ์‚ฌ์šฉ ํ›„๋Š” ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ ํ•ด์ค€๋‹ค.


ํ…œํ”Œ๋ฆฟ ์กฐ๊ฐ

์›น ํŽ˜์ด์ง€๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๊ณตํ†ต ์˜์—ญ์ด ๋งŽ์ด ์žˆ๋‹ค. ์ด๋Ÿฐ ์˜์—ญ๋“ค์„ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ณ€๊ฒฝ์‹œ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ
๋‹ค ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ƒ๋‹นํžˆ ๋น„ํšจ์œจ ์ ์ด๋‹ค. ํƒ€์ž„๋ฆฌํ”„๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ…œํ”Œ๋ฆฟ ์กฐ๊ฐ๊ณผ ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•œ๋‹ค.

@Controller
@RequestMapping("/template")
public class TemplateController {

    @GetMapping("/fragment")
    public String template() {
        return "template/fragment/fragmentMain";
    }

}

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>

fragment.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>

ํ…œํ”Œ๋ฆฟ ์กฐ๊ฐ์€ ๋ฌด์—‡๋ณด๋‹ค๋„ ์ง์ ‘ ์‹คํ–‰์„ ํ•ด๋ด์•ผ ์–ด๋–ป๊ฒŒ ๋˜๋Š”๊ฑด์ง€ ์ดํ•ด๊ฐ€ ๋œ๋‹ค.

์šฐ์„  GetMapping์˜ ๋ฆฌํ„ด์€ fragmentMain.html๋กœ ์‹œ์ผœ์ฃผ๋Š”๋ฐ,

footer.html ํ…œํ”Œ๋ฆฟ์— ์žˆ๋Š” th:fragment="copy" ๋ถ€๋ถ„์ด
fragment.html ~{template/fragment/footer :: copy} ๋กœ ์ด๋™ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ์ค‘์—์„œ๋„ th:insert ๋ฅผ ์‚ฌ์šฉํ•œ ๋ถ€๋ถ„์€ ํ˜„์žฌ ํƒœ๊ทธ์ธ div ๋‚ด๋ถ€์— footer ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๊ณ .
th:replace๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ˜„์žฌ ํƒœ๊ทธ์ธ div๋ฅผ ๋Œ€์ฒดํ•œ๋‹ค.

~{...}๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์›์น™์ด์ง€๋งŒ ํ…œํ”Œ๋ฆฟ ์กฐ๊ฐ์„ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋‹จ์ˆœํ•˜๋ฉด ์ด ๋ถ€๋ถ„์„ ์ƒ๋žต ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‹น์—ฐํžˆ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ๋™์ ์œผ๋กœ ์กฐ๊ฐ์„ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.


ํ…œํ”Œ๋ฆฟ ๋ ˆ์ด์•„์›ƒ

์ฝ”๋“œ ์กฐ๊ฐ์„ ๋ ˆ์ด์•„์›ƒ์— ๋„˜๊ฒจ์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

์˜ˆ๋ฅผ ๋“ค์–ด <head>์— ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” css, javascript ๊ฐ™์€ ์ •๋ณด๋“ค์ด ์žˆ๋Š”๋ฐ ์ด๋Ÿฐ ๊ณตํ†ต ์ •๋ณด๋“ค์„ ํ•œ ๊ณณ์— ๋ชจ์•„๋‘๊ณ , ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€๋งŒ
๊ฐ ํŽ˜์ด์ง€๋งˆ๋‹ค ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋” ์ถ”๊ฐ€ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@GetMapping("layout")
public String layout() {
    return "template/layout/layoutMain";
}

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>

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>
  • common_header({::title},{::link}) ์ค‘์—
    • ::title์€ ํ˜„์žฌ ํŽ˜์ด์ง€์˜ title ํƒœ๊ทธ๋“ค์„ ์ „๋‹ฌํ•œ๋‹ค.
    • ::link๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€์˜ link ํƒœ๊ทธ๋“ค์„ ์ „๋‹ฌํ•œ๋‹ค.

๋ ˆ์ด์•„์›ƒ ๊ฐœ๋…์„ ๋‘๊ณ , ๊ทธ ๋ ˆ์ด์•„์›ƒ์— ํ•„์š”ํ•œ ์ฝ”๋“œ ์กฐ๊ฐ์„ ์ „๋‹ฌํ•ด์„œ ์™„์„ฑํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.


ํ…œํ”Œ๋ฆฟ ๋ ˆ์ด์•„์›ƒ2

๋ ˆ์ด์•„์›ƒ ๊ฐœ๋…์„ <html> ์ „์ฒด์— ์ ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์‚ฌ์ดํŠธ 100ํŽ˜์ด์ง€์˜ ๋ชจ์–‘์ด ๋‹ค ๋˜‘๊ฐ™์•„์•ผํ•˜๊ณ , title๊ณผ replace๋งŒ ๋ฐ”๊พธ๊ณ  ์‹ถ์œผ๋ฉด?

 @GetMapping("/layoutExtend")
    public String layoutExtend() {
        return "template/layoutExtend/layoutExtendMain";
    }

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>

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>

GetMapping์˜ ๋ฆฌํ„ด๊ฐ’์œผ๋กœ layoutExtendMain์ด ์‹คํ–‰์ด ๋˜๊ณ  layoutExtendMain์€ html์ž์ฒด๋ฅผ layoutFile๋กœ th:replace ์‹œํ‚จ๋‹ค.
layoutFile.html ์„ ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ์ด ๋˜๊ณ  title๊ณผ section ํƒœ๊ทธ๊ฐ€ ๋„˜์–ด๊ฐ„๋‹ค.

๋ ˆ์ด์•„์›ƒ์„ ์“ฐ๊ฑฐ๋‚˜ ์‚ฌ์ดํŠธ๊ฐ€ ์ž‘์œผ๋ฉด ์กฐ๊ฐ์กฐ๊ฐ ๋„ฃ๊ฑฐ๋‚˜ ๋ณดํ†ต ๋‘ ๊ฐ€์ง€ ์ค‘์— ์„ ํƒ์„ ํ•œ๋‹ค.
ํŽ˜์ด์ง€๊ฐ€ ๋งŽ์ด ์—†์„๋•Œ๋Š” ๋ ˆ์ด์•„์›ƒ ๋Œ€์‹  fragment๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ๊ดœ์ฐฎ๋‹ค.
ํ•˜์ง€๋งŒ ํŽ˜์ด์ง€๊ฐ€ ๋งŽ์•„์ง€๊ณ  ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•  ๊ฒƒ ๊ฐ™์œผ๋ฉด layout์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.


Reference