Skip to content

Repository for studying View Template Engine : Thymeleaf + HTML

Notifications You must be signed in to change notification settings

jeus1998/Thymeleaf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Thymeleaf (SSR) + HTML

Thymeleaf HTML5

๋ทฐ ํ…œํ”Œ๋ฆฟ ์—”์ง„ ํƒ€์ž„๋ฆฌํ”„ ๊ณต๋ถ€์šฉ ๋ ˆํฌ์ง€ํ† ๋ฆฌ

์ž‘์„ฑ์ž : ๋ฐฐ์ œ์šฐ

Mail : baejeu@naver.com

โ–ถ Environment

Language : Java 17

IDE : Intellij

Framework : SpringBoot (3.2.5)

โ–ถ Reference data

โžก ํƒ€์ž„๋ฆฌํ”„ ๊ณต์‹ ์‚ฌ์ดํŠธ 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

Thymeleaf(ํƒ€์ž„๋ฆฌํ”„)?

ํƒ€์ž„๋ฆฌํ”„๋Š” View Templete Engine ์œผ๋กœ JSP, Freemarkerd, Velocity ์™€ ๊ฐ™์ด SSR(Server Side Rendering) ์ด๋‹ค.

์ฆ‰ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋™์ ์œผ๋กœ ์‘๋‹ตํ•  ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

Thymeleaf ์žฅ์ 

  1. ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฌธ๋ฒ• : ํƒ€์ž„๋ฆฌํ”„๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฌธ๋ฒ•์„ ์ œ๊ณตํ•˜์—ฌ HTML ํŒŒ์ผ์„ ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๊ฐœ๋ฐœ์ž๋“ค์€ ๋น ๋ฅด๊ฒŒ ํ…œํ”Œ๋ฆฟ์„ ์ž‘์„ฑํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  2. ํ†ตํ•ฉ ์šฉ์ด์„ฑ : Spring ์—์„œ๋„ Spring Boot์™€ Thymeleaf๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค. JSP ์‚ฌ์šฉ ์‹œ ํ˜ธํ™˜ ๋ฐ ํ™˜๊ฒฝ์„ค์ •์— ์–ด๋ ค์›€์ด ๋งŽ๋‹ค. ๋ฐ˜๋Œ€๋กœ ํƒ€์ž„๋ฆฌํ”„๋Š” ๊ฐ„ํŽธํ•˜๊ฒŒ Depedency ์ถ”๊ฐ€ ์ž‘์—…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  3. View ์ง‘์ค‘ : JSP์™€ ๋‹ฌ๋ฆฌ Servlet Code๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๋ถ„๋ฆฌ๋˜์–ด View์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

  4. ์‚ฌ์šฉ์ž ํŽธ์˜ : ์„œ๋ฒ„์ƒ์—์„œ ๋™์ž‘ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ๋™์ž‘ ์—†์ด ํ™”๋ฉด์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋•Œ๋ฌธ์— ๋”๋ฏธ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  ํ™”๋ฉด ๋””์ž์ธ ๋ฐ‘ ํ…Œ์ŠคํŠธ์— ์šฉ์ดํ•˜๋‹ค.

  5. ์ตœ์ข… ์‚ฌ์šฉ์ž ์นœํ™”์  : ํƒ€์ž„๋ฆฌํ”„๋Š” ์ตœ์ข… ์‚ฌ์šฉ์ž๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ์— ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์ตœ์ข… ์ถœ๋ ฅ๋ฌผ์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋””์ž์ด๋„ˆ์™€ ํ˜‘์—…ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ ๋‹ค.

  6. ํ…œํ”Œ๋ฆฟ ์บ์‹ฑ: ํƒ€์ž„๋ฆฌํ”„๋Š” ํ…œํ”Œ๋ฆฟ ์บ์‹ฑ์„ ์ง€์›ํ•˜์—ฌ ํšจ์œจ์ ์ธ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํ…œํ”Œ๋ฆฟ์„ ๋ฏธ๋ฆฌ ์ปดํŒŒ์ผํ•˜๊ณ  ์บ์‹œํ•˜์—ฌ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋น ๋ฅด๋‹ค.

Thymeleaf ์„ค์ • ๐Ÿ› ๏ธ

  1. ์˜์กด์„ฑ ์ถ”๊ฐ€

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'
  1. application.properties ์—ฌ๋Ÿฌ ์„ค์ •
# Thymeleaf ์‚ฌ์šฉ
spring.thymeleaf.enabled=true
# cache ์‚ฌ์šฉ /์‚ฌ์šฉ ์‹œ ์ปดํŒŒ์ผํ•œ ํŒŒ์ผ๋งŒ ์‚ฌ์šฉ 
spring.thymeleaf.cache=false
# ํ…œํ”Œ๋ฆฟ ์œ„์น˜ / View ํŒŒ์ผ ์œ„์น˜ 
spring.thymeleaf.prefix=classpath:templates/
# View ํŒŒ์ผ ๊ธฐ๋ณธ ํ™•์žฅ์ž
spring.thymeleaf.suffix=.html
  1. ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์ ์šฉํ•˜๋Š” 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>

Thymeleaf basic expression (๊ธฐ๋ณธ ํ‘œํ˜„์‹)

๐Ÿ“Œ ๊ฐ„๋‹จํ•œ ํ‘œํ˜„

๋ณ€์ˆ˜ ํ‘œํ˜„์‹ ${โ€ฆ}

์„ ํƒ ๋ณ€์ˆ˜ ํ‘œํ˜„์‹ *{โ€ฆ}

๋ฉ”์„ธ์ง€ ํ‘œํ˜„์‹ #{โ€ฆ}

๋งํฌ 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 Entity & Escape vs Unescape ๊ฐœ๋…

  • ๊ฐœ๋… : ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” < ๋ฅผ HTML ํƒœ๊ทธ๋กœ ์ธ์‹ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ < ๋ฅผ ํƒœ๊ทธ์˜ ์‹œ์ž‘์ด ์•„๋‹ˆ๋ผ ๋ฌธ์ž๋กœ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•œ๋ฐ ์ด๊ฒƒ์„ HTML ์—”ํ‹ฐํ‹ฐ๋ผ ํ•œ๋‹ค.
  • escape : HTML์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ˆ˜ ๋ฌธ์ž๋ฅผ HTML Entity๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ
  • unescape : escape ๋ฐ˜๋Œ€ (๊ทธ๋Œ€๋กœ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉ)

๐Ÿ“Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ด์Šค์ผ€์ดํ”„๋œ ๋ฌธ์ž๋“ค

  • <๋ฅผ &lt;๋กœ ์ด์Šค์ผ€์ดํ”„
  • >๋ฅผ &gt;๋กœ ์ด์Šค์ผ€์ดํ”„
  • &๋ฅผ &amp;๋กœ ์ด์Šค์ผ€์ดํ”„
  • "๋ฅผ &quot;๋กœ ์ด์Šค์ผ€์ดํ”„
  • '๋ฅผ &#39;๋กœ ์ด์Šค์ผ€์ดํ”„

HTML ํƒœ๊ทธ ์ •๋ฆฌ

๐Ÿ“Œ html ํƒœ๊ทธ

<!DOCTYPE html>
<html>
<head>
    <title>๋ฌธ์„œ ์ œ๋ชฉ</title>
</head>
<body>
    <!-- ๋‚ด์šฉ ์ž‘์„ฑ -->
</body>
</html>

html : HTML ๋ฌธ์„œ์˜ ์‹œ์ž‘๊ณผ ๋์„ ๋‚˜ํƒ€๋‚ด๋Š” ํƒœ๊ทธ์ž…๋‹ˆ๋‹ค.

๐Ÿ“Œ head ํƒœ๊ทธ

<head>
    <title>๋ฌธ์„œ ์ œ๋ชฉ</title>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="styles.css">
</head>

head : ๋ฌธ์„œ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ถ€๋ถ„์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ title ํƒœ๊ทธ

<title>๋‚˜์˜ ์›น ํŽ˜์ด์ง€</title>

title : ๋ฌธ์„œ์˜ ์ œ๋ชฉ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค

๐Ÿ“Œ body ํƒœ๊ทธ

body : ๋ฌธ์„œ์˜ ๋ณธ๋ฌธ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถ€๋ถ„์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ h1 ~ h6 ์ œ๋ชฉ ํƒœ๊ทธ

<h1>์ฒซ ๋ฒˆ์งธ ์ œ๋ชฉ</h1>
<h2>๋‘ ๋ฒˆ์งธ ์ œ๋ชฉ</h2>

h? : ์ œ๋ชฉ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค h1์ด ๊ฐ€์žฅ ํฌ๊ณ  h6๊ฐ€ ๊ฐ€์žฅ ์ž‘์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ p ํƒœ๊ทธ

<p>์ด๊ฒƒ์€ ๋ฌธ๋‹จ์ž…๋‹ˆ๋‹ค.</p>

p : ๋ฌธ๋‹จ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค

๐Ÿ“Œ a ํƒœ๊ทธ

<a href="https://www.example.com">example ์‚ฌ์ดํŠธ</a>

a : ํ•˜์ดํผ๋งํฌ๋ฅผ ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ img ํƒœ๊ทธ

<img src="์ด๋ฏธ์ง€์ฃผ์†Œ.jpg" alt="example ์ด๋ฏธ์ง€">

img : ์ด๋ฏธ์ง€๋ฅผ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ div ํƒœ๊ทธ

<div id="content">
    <!-- ๋‚ด์šฉ -->
</div>

div : ๊ตฌํš์„ ์ •์˜ํ•˜๋ฉฐ ์ฃผ๋กœ CSS ์Šคํƒ€์ผ๋ง์ด๋‚˜ JavaScript๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ ul, ol, li ํƒœ๊ทธ

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์€ ํ•ญ๋ชฉ ๊ฐ„์˜ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๐Ÿ“Œ span ํƒœ๊ทธ

<p>์ด ๋ฌธ์žฅ์€ <span style="color: blue;">ํŒŒ๋ž€์ƒ‰</span>์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</p>

span : ํ…์ŠคํŠธ์˜ ์ผ๋ถ€๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜๊ฑฐ๋‚˜ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ b ํƒœ๊ทธ

<p>This is <b>bold</b> text.</p>
  • b ํƒœ๊ทธ๋Š” ํ…์ŠคํŠธ๋ฅผ ๊ตต๊ฒŒ(bold)ํ‘œ์‹œํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์˜๋ฏธ์ ์ธ ์ค‘์š”์„ฑ์„ ๊ฐ–์ง€ ์•Š๊ณ , ์‹œ๊ฐ์ ์œผ๋กœ ํ…์ŠคํŠธ๋ฅผ ๊ฐ•์กฐํ•˜๊ณ ์ž ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์œ„ ์˜ˆ์ œ์—์„œ๋Š” "bold"๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ๊ตต๊ฒŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ br ํƒœ๊ทธ

<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"์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค

Thymeleaf ๋ฌธ๋ฒ• ์ •๋ฆฌ

๐Ÿ“Œ ์ฃผ์„

1 ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋žœ๋”๋ง ๋˜์ง€ ์•Š๋Š” ์ฃผ์„ (ํƒ€์ž„๋ฆฌํ”„ ์—”์ง„์ด ํ…œํ”Œ๋ฆฟ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋ฌด์‹œ)

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

1-1 ๋‹จ์ผ ๋ผ์ธ ์ฃผ์„ (Single-line Comment)

<!--/* Comment here */-->

1-2 ๋‹ค์ค‘ ๋ผ์ธ ์ฃผ์„ (Multi-line Comment)

<!--/* 
  ์—ฌ๋Ÿฌ ์ค„ ์ฃผ์„ 
*/-->

2 ํƒ€์ž„๋ฆฌํ”„ ํŒŒ์„œ ์ฃผ์„

  • ๋ Œ๋”๋ง์—์„œ ์ฃผ์„ ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•œ๋‹ค.
<!--/* [[${data}]] */-->

3 ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋žœ๋”๋ง ๋˜๋Š” ์ฃผ์„ (ํƒ€์ž„๋ฆฌํ”„ ํ”„๋กœํ† ํƒ€์ž… ์ฃผ์„)

<!--/*/ <span>ํ‘œ์ค€ HTML ์ฃผ์„</span> /*/-->

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

๐Ÿ“Œ ๋ณ€์ˆ˜ ํ‘œํ˜„์‹ - text, utext

๐Ÿ’ก ๋ฌธ๋ฒ•: 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 ํƒœ๊ทธ๋กœ ๋‹จ์–ด๊ฐ€ ๊ตต๊ฒŒ ํ‘œ์‹œ๋œ๋‹ค.

๐Ÿ“Œ SpringEL ํ‘œํ˜„์‹

  • 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, .๋ณ€์ˆ˜์ด๋ฆ„ ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.

๐Ÿ“Œ Safe Navigation Operator

 <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์ด ์ œ๊ณตํ•˜๋Š” ๋ฌธ๋ฒ•์ด๋‹ค.

์ฐธ๊ณ  : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressionsoperator-safe-navigation

๐Ÿ“Œ ์ง€์—ญ ๋ณ€์ˆ˜

๐Ÿ’ก ๋ฌธ๋ฒ•: 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&param2=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 &gt; 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 &lt; 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๋ฉด ์œ ์ง€

๐Ÿ“Œ ๋ฐ˜๋ณต๋ฌธ each

๐Ÿ‘‰ ๋ฐ˜๋ณต ๊ธฐ๋Šฅ

<tr th:each="item : ${items}">
<td th:text="${item.id}"></td>
<td th:text="${item.itemName}"></td>
<td th:text="${item.price}"></td>
</tr>

๐Ÿ‘‰ ๋ฐ˜๋ณต ์ƒํƒœ ์œ ์ง€

Thymeleaf each Table

<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 : ํ˜„์žฌ ๊ฐ์ฒด

๐Ÿ“Œ switch case

<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 ๊ฐ’

๐Ÿ“Œ ์กฐ๊ฑด์‹ if, unless(if์˜ ๋ฐ˜๋Œ€)

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

๐Ÿ“Œ th:classappend

          <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

๐Ÿ“Œ ๊ฒ€์ฆ(validation)

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” 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>

Thymeleaf ๊ฐ์ฒด ์ •๋ฆฌ

๐Ÿ“Œ ์Šคํ”„๋ง ๋ถ€ํŠธ v 3.0 ๋ฏธ๋งŒ (๊ธฐ๋ณธ ๊ฐ์ฒด)

  • ๊ธฐ๋ณธ ์ œ๊ณต ๊ฐ์ฒด : request, response, session, servletContext, locale
  • ์‚ฌ์šฉ๋ฒ• : ${#request}, ${#response}, ${#session}, ${#servletContext}, ${#locale}

๐Ÿ“Œ ์Šคํ”„๋ง ๋ถ€ํŠธ v 3.0 ์ด์ƒ (๊ธฐ๋ณธ ๊ฐ์ฒด)

  • ๊ธฐ๋ณธ ์ œ๊ณต ๊ฐ์ฒด : 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)์— ์ง์ ‘ ๋„ฃ์–ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ“Œ Thymeleaf ํŽธ์˜ ๊ฐ์ฒด

  • HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ์ ‘๊ทผ : param
    • ์˜ˆ) ${param.paramData}
  • HTTP ์„ธ์…˜ ์ ‘๊ทผ : session
    • ์˜ˆ) ${session.sessionData}
    • sessionData๋Š” ์„œ๋ฒ„์—์„œ ๋„ฃ์–ด์ค€ ๋ฐ์ดํ„ฐ(session.setAttribute) key
  • ์Šคํ”„๋ง ๋นˆ ์ ‘๊ทผ: @
    • ์˜ˆ) ${@helloBean.hello('Spring!')}
    • ๋นˆ ์ด๋ฆ„(helloBean) ์œผ๋กœ ์ ‘๊ทผ hello๋Š” ํ•ด๋‹น ๋นˆ์˜ ๋ฉ”์„œ๋“œ

๐Ÿ“Œ Thymeleaf ์œ ํ‹ธ๋ฆฌํ‹ฐ ๊ฐ์ฒด

  • ํƒ€์ž„๋ฆฌํ”„๋Š” ๋ฌธ์ž, ์ˆซ์ž, ๋‚ ์งœ, 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

Thymeleaf Practice1

  • project๋ฅผ ๋‹ค์šด๋ฐ›๊ณ  localhost:8181๋กœ ์ ‘์†์„ ํ•˜๋ฉด ํ…Œ์Šฌ๋ผ ์ฐจ ํŒ๋งค๋ผ๋Š” ๋งํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋“ค์–ด๊ฐ€์„œ ๋ณด๋ฉด ํƒ€์ž„๋ฆฌํ”„๋ฅผ ํ™œ์šฉํ•œ ์ƒํ’ˆ ๋“ฑ๋ก, ์ƒํ’ˆ ์ˆ˜์ •, ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • README์— ์„ค๋ช… ์•ˆ๋œ ๋ฌธ๋ฒ•๋“ค th:object, th:field, select, checkbox, radio, ${#ids.prev('...')}

Back-End Package โžก Thymleaf/study/pratice1/*

SSR Package โžก resources/templates/practice1/*

About

Repository for studying View Template Engine : Thymeleaf + HTML

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published