JiwonDev

Thymeleaf#1 ๊ธฐ๋ณธ๊ธฐ๋Šฅ

by JiwonDev

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

 

# ๊ณต์‹ ๋ ˆํผ๋Ÿฐ์Šค

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

 

 

Tutorial: Thymeleaf + Spring

Preface This tutorial explains how Thymeleaf can be integrated with the Spring Framework, especially (but not only) Spring MVC. Note that Thymeleaf has integrations for both versions 3.x and 4.x of the Spring Framework, provided by two separate libraries c

www.thymeleaf.org

 

 

* ์ฐธ๊ณ ๋กœ 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>

th:text๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด &amp;lt;tag&amp;gt;๋‚ด์šฉ&amp;lt;/tag&amp;gt;์€ ํƒ€์ž„๋ฆฌํ”„ ๋ Œ๋”๋ง์‹œ ์—†์–ด์ง€๊ฒŒ ๋œ๋‹ค.

์ฐธ๊ณ ๋กœ ์œ„์˜ ์˜ˆ์ œ์—์„œ ์‚ฌ์šฉ๋œ [[ ... ]] ๋Š” thymeleaf ๋ณ€์ˆ˜์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ธฐ๋Šฅ๋„ ์žˆ์ง€๋งŒ, < > ์™€ ๊ฐ™์€ HTML ์˜ˆ์•ฝ๋ฌธ์ž๋ฅผ ๋ฌธ์ž์—ด(&lt)๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์—ญํ• ๋„ ํ•œ๋‹ค. ๋งŒ์•ฝ ์˜๋„์ ์œผ๋กœ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ 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>

 

https://path/mypage?paramData=HelloParam

์˜ˆ์ œ์—๋„ ๋‚˜์™€์žˆ์ง€๋งŒ, ${@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";
    }โ€‹

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expression-utility-objects

 


# 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&param2=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 &gt; 10"></span> <!-- ์ˆœ์ˆ˜ HTML์—์„œ๋Š” &gt; ์ด๋Ÿฐ์‹์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค -->
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์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋‹ค.

"userA"๋Š” ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ํƒ€์ž„๋ฆฌํ”„์—์„œ ๋ Œ๋”๋งํ•œ ๋ฌธ์ž์—ด ๊ฐ’์ด๋‹ค. ๋”ฐ์Œํ‘œ(")๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

 

  • ์ฝ”๋“œ์ƒ์—์„œ "" ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ ๊นŒ? ๋ Œ๋”๋ง ๋•Œ ์ง„์งœ "๋ฌธ์ž์—ด"์ธ์ง€ ํƒ€์ž„๋ฆฌํ”„ ๋ณ€์ˆ˜์ธ์ง€ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์„๊นŒ?
// ์ด๊ฒŒ ํƒ€์ž„๋ฆฌํ”„ ๋ณ€์ˆ˜์ธ์ง€, ๊ทธ๋ƒฅ "[[..]]" ๋ฌธ์ž์—ด์ธ์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ?
var username = "[[${user.username}]]";

 

์ด๋Ÿฐ ๊ฒฝ์šฐ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ, ํƒ€์ž„๋ฆฌํ”„์—๋Š” <script th:inline = "javascript"> ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

&amp;lt;script th:inline = "javascript"&amp;gt; ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ”๋€Œ๋Š” ๊ฒƒ

<!-- 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)๋ฅผ ํŒŒ๋ผ๋ฉ”ํƒ€๋กœ ๋„˜๊ธด๋‹ค.

๊ต์ฒด๋˜๊ธฐ ์ „ &amp;lt;head&amp;gt; ํƒœ๊ทธ์•ˆ์— ์žˆ๋˜ ๋‚ด์šฉ์„ ํŒŒ๋ผ๋ฉ”ํƒ€๋กœ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

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

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>

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ