๋ณธ๋ฌธ์œผ๋กœ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋ฐ˜์‘ํ˜•

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

ํƒ€์ž„๋ฆฌํ”„(Thymeleaf)๋Š” Spring MVC + ๋ทฐ ํ…œํ”Œ๋ฆฟ ์—”์ง„์œผ๋กœ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ์ด๋‹ค.

๋ณดํ†ต ๊ธฐ๋ณธ ํ‹€์ด๋ผ๊ณ  ํ•˜๋ฉด, HTML ์•ˆ์—์„œ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์ ์šฉํ•˜๋Š” ๊ณต์‹ ๊ตฌ์กฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 


1.HTML ๋ฌธ์„œ ๊ธฐ๋ณธ ๊ตฌ์กฐ

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ํƒ€์ž„๋ฆฌํ”„ ๊ธฐ๋ณธ ํ‹€</title>
</head>
<body>

    <h1 th:text="${title}">๊ธฐ๋ณธ ์ œ๋ชฉ</h1>

    <!-- ๋ณ€์ˆ˜ ์ถœ๋ ฅ -->
    <p th:text="${message}">๊ธฐ๋ณธ ๋ฉ”์‹œ์ง€</p>

    <!-- ์กฐ๊ฑด๋ฌธ -->
    <div th:if="${isLogin}">
        ๋กœ๊ทธ์ธ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.
    </div>
    <div th:unless="${isLogin}">
        ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    </div>

    <!-- ๋ฐ˜๋ณต๋ฌธ -->
    <ul>
        <li th:each="item : ${items}" th:text="${item}">์•„์ดํ…œ</li>
    </ul>

    <!-- ๋งํฌ -->
    <a th:href="@{/member/list}">ํšŒ์›๋ชฉ๋ก</a>

</body>
</html>

 

2. ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ์‹œ (Spring mvc)

@Controller
public class ThymeleafController {

    @RequestMapping("/example")
    public String example(Model model) {
        model.addAttribute("title", "ํƒ€์ž„๋ฆฌํ”„ ์˜ˆ์ œ");
        model.addAttribute("message", "Hello Thymeleaf!");
        model.addAttribute("isLogin", true);
        model.addAttribute("items", Arrays.asList("์‚ฌ๊ณผ", "๋ฐ”๋‚˜๋‚˜", "ํฌ๋„"));
        return "example"; // resources/templates/example.html
    }
}

3. ๋™์ž‘ ์›๋ฆฌ

  • Controller์—์„œ Model์— ๋ฐ์ดํ„ฐ ๋‹ด์Œ
  • return "example" → example.html ์ฐพ์•„๊ฐ
  • ${๋ณ€์ˆ˜๋ช…} → Model์˜ ๊ฐ’์œผ๋กœ ์น˜ํ™˜

๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐํ™”

JSP ์‹œ์ ˆ์— <%@ include file="header.jsp" %> ์ด๋Ÿฐ ๊ฑฐ ํ•˜๋˜ ๊ฑธ, ํƒ€์ž„๋ฆฌํ”„์—์„œ๋Š” fragment๋กœ ๊น”๋”ํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

1.header.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title th:text="${title}">๊ณตํ†ต ํƒ€์ดํ‹€</title>
</head>
<body>
<!-- header fragment -->
<div th:fragment="header">
    <header>
        <h1>๊ณตํ†ต ํ—ค๋”</h1>
        <nav>
            <a th:href="@{/}">ํ™ˆ</a>
            <a th:href="@{/member/list}">ํšŒ์›๋ชฉ๋ก</a>
        </nav>
    </header>
</div>

 

2. footer.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!-- footer fragment -->
<div th:fragment="footer">
    <footer>
        <p>โ“’ 2025 MyCompany</p>
    </footer>
</div>
</body>
</html>

 

๋ณธ๋ฌธ์—์„œ include ํ•˜๊ธฐ

3. example.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ํƒ€์ž„๋ฆฌํ”„ Fragment ์˜ˆ์ œ</title>
</head>
<body>

<!-- ํ—ค๋” ์‚ฝ์ž… -->
<div th:replace="fragments/header :: header"></div>

<!-- ๋ณธ๋ฌธ -->
<main>
    <h2 th:text="${title}">๊ธฐ๋ณธ ์ œ๋ชฉ</h2>
    <p th:text="${message}">๋ณธ๋ฌธ ๋‚ด์šฉ</p>
</main>

<!-- ํ‘ธํ„ฐ ์‚ฝ์ž… -->
<div th:replace="fragments/footer :: footer"></div>

</body>
</html>

๋™์ž‘ ์›๋ฆฌ

  • fragments/header :: header → header.html ํŒŒ์ผ ์•ˆ์— th:fragment="header" ์ง€์ •๋œ ๋ถ€๋ถ„์„ ๊ฐ€์ ธ์˜ด
  • fragments/footer :: footer → footer.html ์•ˆ์˜ footer fragment ๊ฐ€์ ธ์˜ด
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ—ค๋”/ํ‘ธํ„ฐ๋ฅผ ๋ชจ๋“  ํŽ˜์ด์ง€์— ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ

replace vs insert vs include ์ฐจ์ด

  • th:replace → ๊ฐ€์ ธ์˜จ fragment๊ฐ€ ์ž๊ธฐ ์ž์‹ ์„ ํ†ต์งธ๋กœ ๋Œ€์ฒด
  • th:insert → fragment ๋‚ด์šฉ์„ ํ˜„์žฌ ํƒœ๊ทธ ์•ˆ์— ์‚ฝ์ž…
  • th:include → ์˜›๋‚  ๋ฐฉ์‹ (๊ฑฐ์˜ ์•ˆ ์”€)

์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต replace ๋งŽ์ด ์“ด๋‹ค.


์ฆ‰, header/footer๋ฅผ fragment๋กœ ๋‚˜๋ˆ ๋†“๊ณ 

ํ•„์š”ํ•  ๋•Œ th:replace๋กœ ๊ฐ€์ ธ์˜ค๋ฉด JSP include์ฒ˜๋Ÿผ ๊ณตํ†ต ๋ ˆ์ด์•„์›ƒ ๊ด€๋ฆฌ๊ฐ€ ๊น”๋”ํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

 

๋ฐ˜์‘ํ˜•