πŸƒ νƒ€μž„λ¦¬ν”„ (Thymeleaf)

JeongHoHyunΒ·2025λ…„ 2μ›” 9일

Spring Boot

λͺ©λ‘ 보기
6/7

πŸƒ νƒ€μž„λ¦¬ν”„ (Thymeleaf)

νƒ€μž„λ¦¬ν”„ νŠΉμ§•

μ„œλ²„ μ‚¬μ΄λ“œ HTML λ Œλ”λ§ (SSR)

  • νƒ€μž„λ¦¬ν”„λŠ” λ°±μ—”λ“œ μ„œλ²„μ—μ„œ HTML을 λ™μ μœΌλ‘œ λ Œλ”λ§ν•˜λŠ” μš©λ„λ‘œ μ‚¬μš©λœλ‹€.

λ„€μΈ„λŸ΄ ν…œν”Œλ¦Ώ

  • JSP와 λ‹€λ₯΄κ²Œ νƒ€μž„λ¦¬ν”„λŠ” HTML을 μœ μ§€ν•œλ‹€.

μŠ€ν”„λ§ 톡합지원

  • νƒ€μž„λ¦¬ν”„λŠ” μŠ€ν”„λ§κ³Ό μžμ—°μŠ€λŸ½κ²Œ ν†΅ν•©λ˜κ³ , μŠ€ν”„λ§μ˜ λ‹€μ–‘ν•œ κΈ°λŠ₯을 νŽΈλ¦¬ν•˜κ²Œ μ‚¬μš©ν•  수 있게 μ§€μ›ν•œλ‹€.

⭐️ νƒ€μž„λ¦¬ν”„ κΈ°λŠ₯

πŸ“Œ ν…μŠ€νŠΈ - th:text, th:utext

  • νƒ€μž„λ¦¬ν”„μ˜ κ°€μž₯ κΈ°λ³Έ κΈ°λŠ₯인 ν…μŠ€νŠΈλ₯Ό 좜λ ₯ν•˜λŠ” κΈ°λŠ₯이닀.
  • νƒ€μž„λ¦¬ν”„λŠ” 기본적으둜 HTML νƒœκ·Έμ˜ 속성에 κΈ°λŠ₯을 μ •μ˜ν•΄μ„œ λ™μž‘ν•œλ‹€.
  • HTML의 μ½˜ν…μΈ (content)에 데이터λ₯Ό 좜λ ₯ν•  λ•ŒλŠ” λ‹€μŒκ³Ό 같이 'th:text'λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€
  • HTML νƒœκ·Έ 속성이 μ•„λ‹Œ HTML 컨텐츠 μ˜μ—­ μ•ˆμ—μ„œ 직접 데이터λ₯Ό 좜λ ₯ν•˜κ³  μ‹ΆμœΌλ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•œλ‹€.

μ˜ˆμ‹œ

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

Escape

  • ThymeleafλŠ” 기본적으둜 좜λ ₯λ˜λŠ” 값을 HTML μ—”ν‹°ν‹°λ‘œ λ³€ν™˜(escape)ν•˜μ—¬ λΈŒλΌμš°μ €κ°€ ν•΄λ‹Ή λ¬Έμžμ—΄μ„ μ½”λ“œλ‘œ ν•΄μ„ν•˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.
  • 이것은 XSS곡격 λ°©μ§€λ₯Ό μœ„ν•΄ 기본적으둜 μ μš©λœλ‹€.
  • νƒœκ·Έλ₯Ό μ—”ν‹°ν‹°λ‘œ λ³€ν™˜ν•˜μ—¬ 좜λ ₯, XSS λ°©μ§€(슀크립트 μ‹€ν–‰ μ•ˆλ¨), 일반적인 ν…μŠ€νŠΈ 좜λ ₯

μˆ˜λ™ Escape 처리

String escapedText = HtmlUtils.htmlEscape("<script>alert('XSS');</script>");
// escapedText = "&lt;script&gt;alert('XSS');&lt;/script&gt;"

Unescape

  • λ•Œλ•Œλ‘œ HTML νƒœκ·Έλ‚˜ JavaScript μ½”λ“œκ°€ μ‹€μ œλ‘œ μ‹€ν–‰λ˜λ„λ‘ ν•΄μ•Όν•  λ•Œκ°€ μžˆλ‹€.
    이 경우 'th:utext, [()]λ₯Ό μ‚¬μš©ν•˜μ—¬ escape없이 원본 HTML을 κ·ΈλŒ€λ‘œ 좜λ ₯ν•  수 μžˆλ‹€.
  • νƒœκ·Έλ₯Ό μ‹ μ œλ‘œ λ Œλ”λ§, 슀크립트 μ‹€ν–‰ κ°€λŠ₯, HTML이 ν¬ν•¨λœ λ¬Έμžμ—΄μ„ λ Œλ”λ§.

μˆ˜λ™ Unescape 처리

String unescapedText = HtmlUtils.htmlUnescape("&lt;b&gt;Hello&lt;/b&gt;");
// unescapedText = "<b>Hello</b>"

⚠️ 주의

  • μ‹€μ œ μ„œλΉ„μŠ€λ₯Ό κ°œλ°œν•˜λ‹€ 보면 escapeλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„μ„œ HTML이 정상 λ Œλ”λ§ λ˜μ§€ μ•ŠλŠ” 수 λ§Žμ€ λ¬Έμ œκ°€ λ°œμƒν•œλ‹€.
  • escapeλ₯Ό 기본으둜 ν•˜κ³ , κΌ­ ν•„μš”ν•  λ•Œλ§Œ unescapeλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

πŸ“Œ λ³€μˆ˜ - SpringEL

  • νƒ€μž„λ¦¬ν”„μ—μ„œ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” λ³€μˆ˜ ν‘œν˜„μ‹μ„ μ‚¬μš©ν•œλ‹€.

λ³€μˆ˜ ν‘œν˜„μ‹ : '${...}'

  • 이 λ³€μˆ˜ ν‘œν˜„μ‹μ—λŠ” SpringEL μ΄λΌλŠ” μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” ν‘œν˜„μ‹μ„ μ‚¬μš©ν•  수 μžˆλ‹€.

λ‹€μ–‘ν•œ ν‘œν˜„μ‹ μ‚¬μš© 예제

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

πŸ“Œ μ§€μ—­λ³€μˆ˜ μ„ μ–Έ (th:with)

  • 'th:with'λ₯Ό μ‚¬μš©ν•˜λ©΄ μ§€μ—­ λ³€μˆ˜λ₯Ό μ„ μ–Έν•΄μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.
  • μ§€μ—­λ³€μˆ˜λŠ” μ„ μ–Έν•œ νƒœκ·Έ μ•ˆμ—μ„œλ§Œ μ‚¬μš©ν•  수 μžˆλ‹€.
<h1>μ§€μ—­ λ³€μˆ˜ - (th:with)</h1>
<div th:with="first=${users[0]}">
    <p>처음 μ‚¬λžŒμ˜ 이름은 <span th:text="${first.username}"></span></p>
</div>

πŸ“Œ νƒ€μž„λ¦¬ν”„κ°€ μ œκ³΅ν•˜λŠ” κΈ°λ³Έ 객체

  • ${#locale}

⚠️ 주의

κΈ°μ‘΄μ—λŠ” ${#request}, ${#response}, ${#session}, ${#servletContext} λ₯Ό 지원 ν•˜μ˜€μ§€λ§Œ μŠ€ν”„λ§ λΆ€νŠΈ 3.0 이후 λΆ€ν„°λŠ” μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.
λ”°λΌμ„œ model에 직접 λ‹΄μ•„μ„œ μ‚¬μš©ν•΄μ•Όν•œλ‹€.

@GetMapping("/basic-objects")
public String basicObjects(Model model, HttpServletRequest request,
HttpServletResponse response, HttpSession session) {
session.setAttribute("sessionData", "Hello Session");
model.addAttribute("request", request);
model.addAttribute("response", response);
model.addAttribute("servletContext", request.getServletContext());
return "basic/basic-objects";
}

πŸ“Œ 편의 객체

  • ${param.paramData}
    • URL의 μΏΌλ¦¬μŠ€νŠΈλ§μ„ λ³„λ„μ˜ μ„ μ–Έ 없이 μ‚¬μš©ν•  수 μžˆλ‹€.
  • ${session.sessionData}
    • session.setAttributeν•œ 값을 μ‚¬μš©ν•  수 μžˆλ‹€.
  • ${@helloBean.hello('Spring!')
    • @μŠ€ν”„λ§λΉˆμ΄λ¦„.λ©”μ„œλ“œλͺ…을 ν˜ΈμΆœν•˜λ©΄ μŠ€ν”„λ§ λΉˆμ— 직접 μ ‘κ·Όν•˜μ—¬ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€.

πŸ“Œ μœ ν‹Έλ¦¬ν‹° 객체와 λ‚ μ§œ

  • νƒ€μž„λ¦¬ν”„λŠ” 문자, 숫자, λ‚ μ§œ, URI등을 νŽΈλ¦¬ν•˜κ²Œ λ‹€λ£¨λŠ” λ‹€μ–‘ν•œ μœ ν‹Έλ¦¬ν‹° 객체듀을 μ œκ³΅ν•œλ‹€.

νƒ€μž„λ¦¬ν”„ μœ ν‹Έλ¦¬ν‹° 객체듀

μœ ν‹Έλ¦¬ν‹° 객체λ₯Ό μ•Œμ•„λ‘κ³ , λ‚˜μ€‘μ— ν•„μš”ν• λ•Œ 메뉴얼을 μ°Ύμ•„μ„œ μ‚¬μš©ν•˜λ©΄ λœλ‹€.

  • #message : λ©”μ‹œμ§€, κ΅­μ œν™” 처리
  • #uris : URI μ΄μŠ€μΌ€μ΄ν”„ 지원
  • #dates : java.util.Date 지원
  • #calendars : java.util.Calendar 지원
  • #temporals : μžλ°”8 λ‚ μ§œ μ„œμ‹ 지원
  • #numbers : 숫자 μ„œμ‹ 지원
  • #string : 문자 κ΄€λ ¨ 편의 κΈ°λŠ₯
  • #objects : 객체 κ΄€λ ¨ κΈ°λŠ₯ 제곡
  • #bools : boolean κ΄€λ ¨ κΈ°λŠ₯ 제곡
  • #arrays : λ°°μ—΄ κ΄€λ ¨ κΈ°λŠ₯ 제곡
  • #lists, #sets, #maps : μ»¬λ ‰μ…˜ κ΄€λ ¨ κΈ°λŠ₯ 제곡
  • #ids : 아이디 처리 κ΄€λ ¨ κΈ°λŠ₯ 제곡

temporalsκ΄€λ ¨ μ˜ˆμ‹œ

<body>

<h1>LocalDateTime</h1>
<ul>
    <li>default = <span th:text="${localDateTime}"></span></li>
    <li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span></li>
</ul>

<h1>LocalDateTime - Utils</h1>
<ul>
    <li>${#temporals.day(localDateTime)} = <span th:text="${#temporals.day(localDateTime)}"></span></li>
    <li>${#temporals.month(localDateTime)} = <span th:text="${#temporals.month(localDateTime)}"></span></li>
    <li>${#temporals.monthName(localDateTime)} = <span th:text="${#temporals.monthName(localDateTime)}"></span></li>
    <li>${#temporals.monthNameShort(localDateTime)} = <span th:text="${#temporals.monthNameShort(localDateTime)}"></span></li>
    <li>${#temporals.year(localDateTime)} = <span th:text="${#temporals.year(localDateTime)}"></span></li>
    <li>${#temporals.dayOfWeek(localDateTime)} = <span th:text="${#temporals.dayOfWeek(localDateTime)}"></span></li>
    <li>${#temporals.dayOfWeekName(localDateTime)} = <span th:text="${#temporals.dayOfWeekName(localDateTime)}"></span></li>
    <li>${#temporals.dayOfWeekNameShort(localDateTime)} = <span th:text="${#temporals.dayOfWeekNameShort(localDateTime)}"></span></li>
    <li>${#temporals.hour(localDateTime)} = <span th:text="${#temporals.hour(localDateTime)}"></span></li>
    <li>${#temporals.minute(localDateTime)} = <span th:text="${#temporals.minute(localDateTime)}"></span></li>
    <li>${#temporals.second(localDateTime)} = <span th:text="${#temporals.second(localDateTime)}"></span></li>
    <li>${#temporals.nanosecond(localDateTime)} = <span th:text="${#temporals.nanosecond(localDateTime)}"></span></li>
</ul>

</body>

πŸ“Œ URL 링크 (@{...})

νƒ€μž„ λ¦¬ν”„μ—μ„œ URL을 생성할 λ•ŒλŠ” '@{...}' 문법을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.

λ‹¨μˆœν•œ URL

  • @{/hello} : /hello

쿼리 νŒŒλΌλ―Έν„°

  • @{/hello(param1=param1,param2={param1}, param2={param2})}
    • -> /hello?param1=data1ΒΆm2=data2
    • '()' μ—μžˆλŠ” 뢀뢄은 쿼리 νŒŒλΌλ―Έν„°λ‘œ μ²˜λ¦¬λœλ‹€.

경둜 λ³€μˆ˜ (pathVariable)

  • @{/hello/{param1}/{param2}(param1=param1,param2={param1}, param2={param2})}
    • -> /hello/data1/data2
    • URL κ²½λ‘œμƒμ— λ³€μˆ˜κ°€ 있으면 '()' 뢀뢄은 경둜 λ³€μˆ˜λ‘œ μ²˜λ¦¬λœλ‹€.

κ²½λ‘œλ³€μˆ˜ + 쿼리 νŒŒλΌλ―Έν„°

  • @{/hello/{param1}(param1=param1,param2={param1}, param2={param2})}
    • -> /hello/data1?param2=data2
    • 경둜 λ³€μˆ˜μ™€ 쿼리 νŒŒλΌλ―Έν„°λ₯Ό ν•¨κ»˜ μ‚¬μš©ν•  수 μžˆλ‹€.

μ ˆλŒ€κ²½λ‘œ, μƒλŒ€κ²½λ‘œ, ν”„λ‘œν† μ½œ κΈ°μ€€

  • μ ˆλŒ€κ²½λ‘œ : /hello
  • μƒλŒ€κ²½λ‘œ : hello
  • ν”„λ‘œν† μ½œ 기쀀도 ν‘œν˜„ν•  수 μžˆλ‹€ (//) - 메뉴얼 μ°Έκ³ 

πŸ“Œ λ¦¬ν„°λŸ΄ (Literals, |...|)

λ¦¬ν„°λŸ΄μ€ μ†ŒμŠ€ μ½”λ“œμƒμ— κ³ μ •λœ 값을 λ§ν•˜λŠ” μš©μ–΄μ΄λ‹€.

νƒ€μž„λ¦¬ν”„ λ¦¬ν„°λŸ΄

  • 문자 : ex) 'hello'
    • νƒ€μž„λ¦¬ν”„μ—μ„œ 문자 λ¦¬ν„°λŸ΄μ€ 항상 ''(μž‘μ€ λ”°μ˜΄ν‘œ)둜 감싸야 ν•œλ‹€.
      • (th:text="'hello world!'")
    • 곡백없이 μ­‰ 이어진닀면 ν•˜λ‚˜μ˜ μ˜λ―ΈμžˆλŠ” ν† ν°μœΌλ‘œ μΈμ§€ν•΄μ„œ λ‹€μŒκ³Ό 같이 ''(μž‘μ€λ”°μ˜΄ν‘œ)λ₯Ό μƒλž΅ν•  수 μžˆλ‹€. (A-Z, a-z, 0-9, [], . , -, _)
      • (th:text="hello")
  • 숫자 : ex) 10
  • boolean : ex) true, false
  • null : ex) null

λ¦¬ν„°λŸ΄ λŒ€μ²΄ 문법 - |hello ${data}|

  • λ¦¬ν„°λŸ΄ λŒ€μ²΄ 문법은 || μ•ˆμ— μž‘μ„±ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆλ‹€.
    • ex) th:text="|hello ${data}}|"
  • ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ νŽΈλ¦¬ν•˜λ‹€.

μ˜ˆμ‹œ

<body>
<h1>λ¦¬ν„°λŸ΄</h1>
<ul>
    <!--주의! λ‹€μŒ 주석을 ν’€λ©΄ μ˜ˆμ™Έκ°€ λ°œμƒν•¨-->
    <!--    <li>"hello world!" = <span th:text="hello world!"></span></li>-->
    <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>
</ul>

</body>

πŸ“Œ μ—°μ‚° (<, >, ==, !=, !, >=, <=)

νƒ€μž„λ¦¬ν”„ 연산은 μžλ°”μ™€ 크게 λ‹€λ₯΄μ§€ μ•Šλ‹€.
HTMLμ•ˆμ—μ„œ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— HTML μ—”ν‹°ν‹°λ₯Ό μ‚¬μš©ν•˜λŠ” λΆ€λΆ„λ§Œ 주의 ν•˜λ©΄ λœλ‹€.

비ꡐ연산

  • > (gt)
  • < (lt)
  • >= (ge)
  • <= (le)
  • ! (not)
  • == (eq)
  • != (neq, ne)

쑰건식

  • μžλ°”μ˜ 쑰건식과 μœ μ‚¬ν•˜λ‹€
    • th:text="(10 % 2 == 0)? '짝수':'ν™€μˆ˜'"

Evis μ—°μ‚°μž

  • μ‘°κ±΄μ‹μ˜ 편의 버전

No-Operation

  • '_'인 경우 마치 νƒ€μž„λ¦¬ν”„κ°€ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ” 것 처럼 λ™μž‘.
  • 잘 μ‚¬μš©ν•˜λ©΄ HTML의 λ‚΄μš©μ„ κ·ΈλŒ€λ‘œ ν™œμš©ν•  수 μžˆλ‹€.

μ˜ˆμ‹œ

<body>

<ul>
    <li>μ‚°μˆ  μ—°μ‚°
        <ul>
            <li>10 + 2 = <span th:text="10 + 2"></span></li>
            <li>10 % 2 == 0 = <span th:text="10 % 2 == 0"></span></li>
        </ul>
    </li>
    <li>비ꡐ μ—°μ‚°
        <ul>
            <li>1 > 10 = <span th:text="${1 > 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>
        </ul>

    </li>
    <li>쑰건식
        <ul>
            <li>(10 % 2 == 0)? '짝수':'ν™€μˆ˜' = <span th:text="(10 % 2 == 0)? '짝수':'ν™€μˆ˜'"></span></li>
        </ul>
    </li>
    <li>Elvis μ—°μ‚°μž
        <ul>
            <li>${data}?: '데이터가 μ—†μŠ΅λ‹ˆλ‹€.' = <span th:text="${data}?: '데이터가 μ—†μŠ΅λ‹ˆλ‹€.'"></span></li>
            <li>${nullData}?: '데이터가 μ—†μŠ΅λ‹ˆλ‹€.' = <span th:text="${nullData}?: '데이터가 μ—†μŠ΅λ‹ˆλ‹€.'"></span></li>
        </ul>
    </li>
    <li>No-Operation
        <ul>
            <li>${data}?: _ = <span th:text="${data}?: _">데이터가 μ—†μŠ΅λ‹ˆλ‹€.</span></li>
            <li>${nullData}?: _ = <span th:text="${nullData}?: _">데이터가 μ—†μŠ΅λ‹ˆλ‹€.</span></li>
        </ul>
    </li>
</ul>

</body>

πŸ“Œ 속성 κ°’ μ„€μ •

νƒ€μž„λ¦¬ν”„λŠ” 주둜 HTML νƒœκ·Έμ— 'th:*' 속성을 μ§€μ •ν•˜λŠ” λ°©μ‹μœΌλ‘œ λ™μž‘ν•œλ‹€.
'th:*'둜 속성을 μ§€μ •ν•˜λ©΄ κΈ°μ‘΄ 속성을 λŒ€μ²΄ν•œλ‹€.
κΈ°μ‘΄ 속성이 μ—†μœΌλ©΄ μƒˆλ‘œ λ§Œλ“ λ‹€.

속성 μ„€μ •

  • 'th:*' 속성을 μ§€μ •ν•˜λ©΄ νƒ€μž„λ¦¬ν”„λŠ” κΈ°μ‘΄ 속성을 'th:*'둜 μ§€μ •ν•œ μ†μ„±μœΌλ‘œ λŒ€μ²΄ν•œλ‹€.
    • ex) <input type="text" name="mock" th:name="userA"/>
      -> <input type="text" name="userA"/>
  • κΈ°μ‘΄ 속성이 μ—†λ‹€λ©΄ μƒˆλ‘œ λ§Œλ“ λ‹€.

속성 μΆ”κ°€

  • th:attrappend = 속성 κ°’μ˜ 값에 값을 μΆ”κ°€ν•œλ‹€.
  • th:attrprepend = 속성 κ°’μ˜ 뒀에 값을 μΆ”κ°€ν•œλ‹€.
  • th:classappend = class 속성에 μžμ—°μŠ€λŸ½κ²Œ μΆ”κ°€ν•œλ‹€.

checked 처리

  • HTMLμ—μ„œλŠ” checed="false" 이 κ²½μš°μ—λ„ checked 속성이 있기 λ•Œλ¬Έμ— checkedμ²˜λ¦¬κ°€ λœλ‹€.
  • HTMLμ—μ„œ checked속성은 checkedμ†μ„±μ˜ κ°’κ³Ό 상관없이 checkedλΌλŠ” μ†μ„±λ§Œ μžˆμ–΄λ„ 체크가 λœλ‹€.
  • νƒ€μž„λ¦¬ν”„μ˜ th:checkedλŠ” 값이 false인 경우 checked속성 자체λ₯Ό μ œκ±°ν•œλ‹€.

예제

<body>

<h1>속성 μ„€μ •</h1>
<input type="text" name="mock" th:name="userA" />

<h1>속성 μΆ”κ°€</h1>
- 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/>

<h1>checked 처리</h1>
- checked o <input type="checkbox" name="active" th:checked="true" /><br/>
- checked x <input type="checkbox" name="active" th:checked="false" /><br/>
- checked=false <input type="checkbox" name="active" checked="false" /><br/>

</body>

πŸ“Œ 반볡 (th:each)

νƒ€μž„λ¦¬ν”„μ—μ„œ λ°˜λ³΅μ€ 'th:each'λ₯Ό μ‚¬μš©ν•œλ‹€. μΆ”κ°€λ‘œ λ°˜λ³΅ν•΄μ„œ μ‚¬μš©ν•  수 μžˆλŠ” μ—¬λŸ¬ μƒνƒœ 값을 μ§€μ›ν•œλ‹€.

반볡 κΈ°λŠ₯

<tr th:each="user : ${users}">
  • λ°˜λ³΅μ‹œ 였λ₯Έμͺ½ μ»¬λ ‰μ…˜ '${users}'의 값을 ν•˜λ‚˜μ”© κΊΌλ‚΄μ„œ μ™Όμͺ½ λ³€μˆ˜('user')에 λ‹΄μ•„μ„œ νƒœκ·Έλ₯Ό 반볡 μ‹€ν–‰ ν•œλ‹€.
  • 'th:each'λŠ” List뿐만 μ•„λ‹ˆλΌ λ°°μ—΄, java.util.Iterable, java.util.Enumeratioin을 κ΅¬ν˜„ν•œ λͺ¨λ“  객체λ₯Ό λ°˜λ³΅μ— μ‚¬μš©ν•  수 μžˆλ‹€.
  • Map도 μ‚¬μš©ν•  수 μžˆλŠ”λ°, 이 경우 λ³€μˆ˜μ— λ‹΄κΈ°λŠ” 값은 Map.Entry 이닀.

반볡 μƒνƒœ μœ μ§€

<tr th:each="user, userStat : ${users}">
  • 반볡의 λ‘λ²ˆμ§Έ νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •ν•΄μ„œ 반볡의 μƒνƒœλ₯Ό 확인할 수 μžˆλ‹€.
  • λ‘λ²ˆμ§Έ νŒŒλΌλ―Έν„°λŠ” μƒλž΅μ΄ κ°€λŠ₯ν•˜λ‹€. (μƒλž΅μ‹œ μ§€μ •ν•œ λ³€μˆ˜λͺ…('user') + 'Stat'이 λœλ‹€.)
  • μœ„μ˜ κ²½μš°λŠ” μƒλž΅ κ°€λŠ₯ν•˜λ‹€.

반볡 μƒνƒœ μœ μ§€ κΈ°λŠ₯

  • index : 0λΆ€ν„° μ‹œμž‘ν•˜λŠ” κ°’
  • count : 1λΆ€ν„° μ‹œμž‘ν•˜λŠ” κ°’
  • size : 전체 μ‚¬μ΄μ¦ˆ
  • even, odd : ν™€μˆ˜, 짝수 μ—¬λΆ€ (boolean)
  • first, last : 처음, λ§ˆμ§€λ§‰ μ—¬λΆ€ (boolean)
  • current : ν˜„μž¬ 객체

예제

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

<h1>반볡 μƒνƒœ μœ μ§€</h1>

<table border="1">
  <tr>
    <th>count</th>
    <th>username</th>
    <th>age</th>
    <th>etc</th>
  </tr>
  <tr th:each="user : ${users}">
    <td th:text="${userStat.count}">username</td>
    <td th:text="${user.username}">username</td>
    <td th:text="${user.age}">0</td>
    <td>
      index = <span th:text="${userStat.index}"></span>
      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>
</body>

πŸ“Œ 쑰건뢀 평가 (if, unless)

νƒ€μž„λ¦¬ν”„μ˜ 쑰건식 if, unless(if의 λ°˜λŒ€)

if, unless

  • νƒ€μž„λ¦¬ν”„λŠ” ν•΄λ‹Ή 쑰건이 λ§žμ§€ μ•ŠμœΌλ©΄ νƒœκ·Έ 자체λ₯Ό λ Œλ”λ§ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • λ§Œμ•½ λ‹€μŒ 쑰건이 false인 경우 spanνƒœκ·ΈλΆ€λΆ„ μžμ²΄κ°€ λ Œλ”λ§ λ˜μ§€ μ•Šκ³  사라진닀.
<span th:text="'λ―Έμ„±λ…„μž'" th:if="${user.age lt 20}"></span>

switch

  • *은 λ§Œμ‘±ν•˜λŠ” 쑰건이 없을 λ•Œ μ‚¬μš©ν•˜λŠ” λ””ν΄λ“œμ΄λ‹€

예제

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

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

</body>

πŸ“Œ 주석

ν‘œμ€€ HTML 주석

<!--
<span th:text="${data}">html data</span>
-->

νƒ€μž„λ¦¬ν”„ νŒŒμ„œ 주석

<!--/* [[${data}]] */-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->

νƒ€μž„λ¦¬ν”„ ν”„λ‘œν† νƒ€μž… 주석

<!--/*/
<span th:text="${data}">html data</span>
/*/-->
  • νƒ€μž„λ¦¬ν”„λ‘œ λ Œλ”λ§λœ κ²½μš°μ—λ§Œ λ³΄μ—¬μ§€λŠ” 주석이닀.
  • νŒŒμΌμ„ κ·ΈλŒ€λ‘œ μ—΄λ©΄ μ£Όμ„μ²˜λ¦¬κ°€ λ˜μ„œ 아무것도 보이지 μ•ŠλŠ”λ‹€.

πŸ“Œ 블둝

'th:block'은 HTML νƒœκ·Έκ°€ μ•„λ‹Œ νƒ€μž„λ¦¬ν”„μ˜ μœ μΌν•œ 자체 νƒœκ·Έμ΄λ‹€.

  • νƒ€μž„λ¦¬ν”„μ˜ νŠΉμ„±μƒ HTMLνƒœκ·Έ μ•ˆμ— μ†μ„±μœΌλ‘œ κΈ°λŠ₯을 μ •μ˜ν•΄μ„œ μ‚¬μš©ν•˜λŠ”λ°, μ•„λž˜ 예제처럼 μ‚¬μš©ν•˜κΈ° μ• λ§€ν•œ κ²½μš°μ— μ‚¬μš©ν•˜λ©΄ λœλ‹€.
  • th:block은 λ Œλ”λ§μ‹œ μ œκ±°λœλ‹€.
  • μ•ˆμ“°λŠ”κ²ƒμ΄ μ’‹μ§€λ§Œ μ–΄μ©” 수 없이 μ¨μ•Όν• λ•Œ μ‚¬μš©ν•΄μ•Όν•œλ‹€.

예제

<body>

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

</body>

πŸ“Œ μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인

νƒ€μž„λ¦¬ν”„λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ νƒ€μž„λ¦¬ν”„λ₯Ό νŽΈλ¦¬ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.
μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 κΈ°λŠ₯은 λ‹€μŒκ³Ό 같이 μ μš©ν•˜λ©΄λœλ‹€.

<script th:inline="javascript">

ν…μŠ€νŠΈ λ Œλ”λ§

  • 인라인을 μ‚¬μš©ν•˜λ©΄ λ Œλ”λ§ 결과에 λ¬Έμžνƒ€μž…μ€ ""둜 감싸주고, μˆ«μžνƒ€μž…μ€ μ²˜λ¦¬ν•˜μ§€μ•Šμ•„ 정상 λ Œλ”λ§λ˜κ²Œ 도와쀀닀.
  • μΆ”κ°€λ‘œ μžλ°” μŠ€ν¬λ¦½νŠΈμ—μ„œ λ¬Έμ œκ°€ λ μˆ˜μžˆλŠ” λ¬Έμžκ°€ ν¬ν•¨λ˜μ–΄μžˆμœΌλ©΄ μ΄μŠ€μΌ€μ΄ν”„ μ²˜λ¦¬λ„ ν•΄μ€€λ‹€.
    • ex) " -> \"

μžλ°”μŠ€ν¬λ¦½νŠΈ λ‚΄μΆ”λŸ΄ ν…œν”Œλ¦Ώ

  • νƒ€μž„λ¦¬ν”„λŠ” HTML νŒŒμΌμ„ 직접 열어도 λ™μž‘ν•˜λŠ” λ‚΄μΆ”λŸ΄ ν…œν”Œλ¦Ώ κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.
  • μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 μ‚¬μš©ν•˜λ©΄ 주석을 ν™œμš©ν•΄μ„œ 이 κΈ°λŠ₯을 μ‚¬μš©ν•  수 μžˆλ‹€.

객체

  • νƒ€μž„λ¦¬ν”„μ˜ μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 κΈ°λŠ₯을 μ‚¬μš©ν•˜λ©΄ 객체λ₯Ό JSON으둜 μžλ™μœΌλ‘œ λ³€ν™˜ν•΄μ€€λ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 each

  • μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인은 eachλ₯Ό μ§€μ›ν•œλ‹€.
  • μ•„λž˜μ™€ 같이 μ‚¬μš©ν•˜λ©΄λœλ‹€.
<!-- μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 each -->
<script th:inline="javascript">

    [# th:each="user, stat : ${users}"]
    var user[[${stat.count}]] = [[${user}]];
    [/]

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

<!-- μžλ°”μŠ€ν¬λ¦½νŠΈ 인라인 each -->
<script th:inline="javascript">

    [# th:each="user, stat : ${users}"]
    var user[[${stat.count}]] = [[${user}]];
    [/]

</script>

πŸ“Œ ν…œν”Œλ¦Ώ 쑰각

νƒ€μž„λ¦¬ν”„λŠ” ν…œν”Œλ¦Ώ 쑰각과 λ ˆμ΄μ•„μ›ƒ κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.

th:fragment

  • th:fragmentκ°€ μžˆλŠ” νƒœκ·ΈλŠ” λ‹€λ₯Έκ³³μ— ν¬ν•¨λ˜λŠ” μ½”λ“œμ‘°κ°μœΌλ‘œ μ΄ν•΄ν•˜λ©΄ λœλ‹€.

λΆ€λΆ„ 포함 th:insert

<div th:insert="~{template/fragment/footer :: copy}"></div>
  • th:insertλ₯Ό μ‚¬μš©ν•˜λ©΄ ν˜„μž¬ νƒœκ·Έ(div) 내뢀에 μΆ”κ°€ν•œλ‹€.

λΆ€λΆ„ 포함 th:replace

<div th:replace="~{template/fragment/footer :: copy}"></div>
  • th:replaceλ₯Ό μ‚¬μš©ν•˜λ©΄ ν˜„μž¬ νƒœκ·Έ(div)λ₯Ό λŒ€μ²΄ ν•œλ‹€.

λΆ€λΆ„ 포함 λ‹¨μˆœ ν‘œν˜„μ‹

<div th:replace="template/fragment/footer :: copy"></div>
  • ~{...}λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ›μΉ™μ΄μ§€λ§Œ ν…œν”Œλ¦Ώ 쑰각을 μ‚¬μš©ν•˜λŠ” μ½”λ“œκ°€ λ‹¨μˆœν•˜λ©΄ 이 뢀뢄을 μƒλž΅ν•  수 μžˆλ‹€.

νŒŒλΌλ―Έν„° μ‚¬μš©

<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
  • μœ„μ™€ 같이 νŒŒλΌλ―Έν„°λ₯Ό μ „λ‹¬ν•΄μ„œ λ™μ μœΌλ‘œ 쑰각을 λ Œλ”λ§ ν•  μˆ˜λ„ μžˆλ‹€.

th:fragment μ„ μ–Έ μ˜ˆμ‹œ

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

th:fragment μ‚¬μš© μ˜ˆμ‹œ

<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>
  • template/fragment/footer :: copy = template/fragment/footer.html ν…œν”Œλ¦Ώμ— μžˆλŠ” th:fragment="copy"λΌλŠ” 뢀뢄을 ν…œν”Œλ¦Ώ 쑰각으둜 κ°€μ Έμ™€μ„œ μ‚¬μš©ν•œλ‹€λŠ” μ˜λ―Έμ΄λ‹€.

πŸ“Œ ν…œν”Œλ¦Ώ λ ˆμ΄μ•„μ›ƒ

μ½”λ“œ 쑰각을 λ ˆμ΄μ•„μ›ƒμ— λ„˜κ²¨μ„œ μ‚¬μš©ν•˜λŠ” 방법이닀.
예λ₯Ό λ“€μ–΄ <header>에 κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜λŠ” css, javascript 같은 정보듀이 μžˆλŠ”λ°, μ΄λŸ¬ν•œ 곡톡 정보듀을 ν•œ 곳에 λͺ¨μ•„두고, κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•˜μ§€λ§Œ, 각 νŽ˜μ΄μ§€λ§ˆλ‹€ ν•„μš”ν•œ 정보λ₯Ό 더 μΆ”κ°€ν•΄μ„œ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•˜λ©΄ λœλ‹€.

곡톡 λ ˆμ΄μ•„μ›ƒ μ˜ˆμ‹œ

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

메인 파일

<!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>
  • ::title은 ν˜„μž¬ νŽ˜μ΄μ§€μ˜ title νƒœκ·Έλ“€μ„ μ „λ‹¬ν•œλ‹€.
  • ::linkλŠ” ν˜„μž¬ νŽ˜μ΄μ§€μ˜ link νƒœκ·Έλ“€μ„ μ „λ‹¬ν•œλ‹€.

ν…œν”Œλ › λ ˆμ΄μ•„μ›ƒ ν™•μž₯

ν…œν”Œλ › λ ˆμ΄μ•„μ›ƒμ€ <header>μ •λ„μ—λ§Œ μ μš©ν•˜λŠ”κ²Œ μ•„λ‹ˆλΌ <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>

메인 파일

<!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>
  • λ©”μΈνŒŒμΌμ˜ htmlνƒœκ·Έμ— th:replaceκ°€ 있기 λ•Œλ¬Έμ— html전체λ₯Ό layoutFile파일둜 λŒ€μ²΄ν•œλ‹€.
  • ν•˜μ§€λ§Œ ::titleκ³Ό ::sectioin을 λ„˜κ²¨μ£ΌκΈ° λ•Œλ¬Έμ— layoutFileνŒŒμΌμ—μ„œ ν•΄λ‹Ή 뢀뢄은 λŒ€μΉ˜λ˜μ„œ λ Œλ”λ§μ΄ λœλ‹€.
    • ν˜„μž¬ ν•„μš”ν•œ λ‚΄μš©μ„ μ „λ‹¬ν•˜λ©΄μ„œ html자체λ₯Ό layoutFile.html둜 λ³€κ²½ν•œ 것이닀.
profile
Java Back-End 2022.11.01 πŸ’»~ing

0개의 λŒ“κΈ€