Thymeleaf는 주로 서버 측 자바 웹 애플리케이션 개발에서 사용되는 템플릿 엔진이다. 즉, HTML을 생성하는 데 사용된다.
하지만 Thymeleaf는 서버 측에서만 사용되는 것은 아니며, 이메일 템플릿, PDF 생성, XML 처리 등 다양한 용도로도 활용될 수 있다. 또한 클라이언트 측 JavaScript 프레임워크와 함께 사용하여 동적으로 웹 페이지를 생성하고 업데이트하는 데에도 사용될 수 있다.
하지만 주로 서버 측 자바 웹 애플리케이션에서 사용되는 것이 가장 흔하며, 이를 위해 많은 서버 측 웹 프레임워크에서 Thymeleaf를 지원하고 있다.
유연한 문법: Thymeleaf의 문법은 자유롭게 사용할 수 있다. 따라서 기존의 HTML 코드에 추가하기가 쉬우며, 다른 템플릿 엔진과 마찬가지로 자바 코드와 결합하여 사용할 수 있다.
자바 객체와 바인딩: Thymeleaf는 자바 객체와 HTML을 쉽게 연결할 수 있다. 또한 자바 객체의 필드나 메서드를 사용하여 HTML 코드를 생성할 수 있다.
Spring Framework와의 연동: Thymeleaf는 Spring Framework와 통합되어 사용할 수 있습니다. Spring MVC와 함께 사용하면 자동화된 폼 처리, 메시지 다국어 처리, 검증 및 페이징과 같은 기능을 쉽게 구현할 수 있다.
간단한 템플릿 캐시: Thymeleaf는 템플릿의 변경을 감지하여 자동으로 캐시를 갱신합니다. 이렇게 하면 템플릿 변경 사항이 즉시 적용되어 애플리케이션의 성능이 향상된다.
다양한 템플릿 모드: Thymeleaf는 HTML을 생성하는 표준 모드 외에도 텍스트 및 XML 모드를 지원합니다. 이러한 모드는 다양한 종류의 템플릿을 지원한다.
일반적인 속성은 HTML, XML, JavaScript, CSS에서 사용할 수 있는 일반적인 속성으로, Thymeleaf가 해석하지 않는다.
(ex) id, class, title, style, src, href
Thymeleaf는 HTML, XML, JavaScript, CSS에서 사용할 수 있는 많은 특수 속성을 제공합니다. 이러한 속성은 Thymeleaf가 데이터를 바인딩하고 처리할 수 있도록 하는 데 사용됩니다. 다음은 Thymeleaf 특수 속성의 일부입니다.
th:text: HTML 태그의 텍스트를 수정합니다.
th:if: 지정된 조건이 참인 경우 HTML 요소를 표시합니다.
th:unless: 지정된 조건이 거짓인 경우 HTML 요소를 표시합니다.
th:switch: 지정된 조건에 따라 여러 HTML 요소 중 하나를 표시합니다.
th:case: th:switch와 함께 사용되어 해당 case의 조건이 참인 경우 해당 HTML 요소를 표시합니다.
th:each: 반복문을 실행하여 HTML 요소를 생성합니다.
th:object: HTML 태그에 객체를 바인딩합니다.
th:field: 폼 필드에 데이터를 바인딩합니다.
th:action: 폼의 액션 URL을 설정합니다.
th:method: 폼의 HTTP 메서드를 설정합니다.
유틸리티 속성은 Thymeleaf가 제공하는 다양한 유틸리티 기능에 사용됩니다. 다음은 유틸리티 속성의 예입니다.
th:attr: HTML 태그의 속성을 수정합니다.
th:replace: 지정된 템플릿을 현재 요소에 대체합니다.
th:remove: HTML 요소를 제거합니다.
th:with: 변수를 정의하고 값에 대한 참조를 제공합니다.
문법: ${...}
변수나 표현식을 출력할 때 사용합니다.
예시: ${name}
문법: {...}
객체나 리스트에서 값을 선택하여 출력할 때 사용합니다.
예시: `{user.name}`
문법: #{...}
다국어 지원을 위해 사용합니다.
예시: #{label.name}
문법: @{...}
URL 생성을 위해 사용합니다.
예시: @{/home}
문법: ?: (삼항 연산자)
조건에 따라 값을 출력할 때 사용합니다.
예시:${flag} ? '참' : '거짓'
문법: th:each
반복문을 사용하여 리스트나 배열의 값을 출력할 때 사용합니다.
예시: <tr th:each="item : ${list}">...</tr>
문법: th:fragment
레이아웃을 작성할 때 사용합니다.
예시: <div th:fragment="header">...</div>
문법: [[...]]
자바스크립트 코드를 작성할 때 사용합니다.
예시: var name = '[[${user.name}]]';
문법: th:include, th:replace, th:insert, th:with
레이아웃을 작성할 때 사용합니다.
예시: <div th:include="fragments/header :: header"></div>
thymeleaf의 템플릿 구조화 기능을 사용하면, 공통적인 레이아웃과 다양한 페이지를 분리하여 개별적으로 작성할 수 있습니다. 이를 통해 코드 재사용성과 유지보수성을 향상시킬 수 있습니다.
Thymeleaf에서 템플릿 구조화 기능을 사용하는 방법에는 크게 두 가지가 있습니다.
th:include 속성을 사용하여 공통적인 부분을 파일로 분리하여 include할 수 있습니다.
예를 들어, header, footer와 같은 공통적인 영역을 header.html, footer.html 파일로 분리한 후, th:include 속성을 사용하여 각각의 페이지에 include할 수 있습니다.
<!-- header.html -->
<header>
<nav>...</nav>
</header>
<!-- footer.html -->
<footer>
<div>...</div>
</footer>
<!-- page.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>...</head>
<body>
<div th:include="header :: header"></div>
<main>
<p>page content</p>
</main>
<div th:include="footer :: footer"></div>
</body>
</html>
Thymeleaf에서는 Layout Dialect라는 기능을 제공합니다. 이를 사용하면, th:insert, th:replace, th:fragment 등의 속성을 사용하여 페이지 내의 특정 영역을 다른 파일에서 가져올 수 있습니다.
<!-- layout.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
th:fragment="layout">
<head>...</head>
<body>
<header>
<nav>...</nav>
</header>
<main>
<div th:replace="~{content :: content}"></div>
</main>
<footer>
<div>...</div>
</footer>
</body>
</html>
<!-- content.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>...</head>
<body>
<div th:fragment="content">
<p>page content</p>
</div>
</body>
</html>
<!-- page.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>...</head>
<body th:replace="layout :: layout">
<div th:replace="~{content :: content}"></div>
</body>
</html>
Layout Dialect를 사용하면, th:replace를 사용하여 content.html 파일을 layout.html 파일의 main 영역에 삽입하였습니다. 이를 통해, 템플릿의 구조화를 보다 쉽고 효과적으로 할 수 있습니다.
Thymeleaf에서는 다양한 제어문과 반복문을 지원하여 템플릿 작성 시 동적인 데이터를 처리할 수 있습니다. 아래는 주요 제어문과 반복문에 대한 사용 방법입니다.
Thymeleaf에서의 if-else 문은 다음과 같은 형태를 가집니다.
<!-- if 문 -->
<div th:if="${조건}">
<!-- 조건이 참일 경우 출력할 내용 -->
</div>
<!-- if-else 문 -->
<div th:if="${조건}">
<!-- 조건이 참일 경우 출력할 내용 -->
</div>
<div th:unless="${조건}">
<!-- 조건이 거짓일 경우 출력할 내용 -->
</div>
<!-- if-else if-else 문 -->
<div th:if="${조건1}">
<!-- 조건1이 참일 경우 출력할 내용 -->
</div>
<div th:elseif="${조건2}">
<!-- 조건2가 참일 경우 출력할 내용 -->
</div>
<div th:else>
<!-- 조건1, 2 모두 거짓일 경우 출력할 내용 -->
</div>
Thymeleaf에서의 switch 문은 다음과 같은 형태를 가집니다.
<div th:switch="${변수}">
<p th:case="'값1'">변수가 값1일 경우 출력할 내용</p>
<p th:case="'값2'">변수가 값2일 경우 출력할 내용</p>
<p th:case="'값3'">변수가 값3일 경우 출력할 내용</p>
<p th:case="*">변수가 위의 값들 중 아닐 경우 출력할 내용</p>
</div>
Thymeleaf에서의 반복문은 다음과 같은 형태를 가집니다.
<!-- 리스트를 이용한 반복문 -->
<ul>
<li th:each="item : ${리스트}">
<span th:text="${item}">리스트 내용 출력</span>
</li>
</ul>
<!-- 맵을 이용한 반복문 -->
<ul>
<li th:each="key, value : ${맵}">
<span th:text="${key}">맵의 키 출력</span>: <span th:text="${value}">맵의 값 출력</span>
</li>
</ul>
Thymeleaf를 사용하여 특정 요소에 이벤트 리스너를 등록하려면 다음과 같은 구문을 사용합니다.
<button th:onclick="'myFunction(\'' + ${value} + '\')'">Click me</button>
<button th:onclick="${'alert(\'' + ${value} + '\')'}">Click me</button>
<button th:onclick="|alert('${value}')|">Click me</button>
❗ ${'alert(\'' + ${value} + '\')'}의 경우, 'alert(\''와 '\')'가 각각 문자열의 시작과 끝을 나타내고, ${value}는 변수 값으로 대체
<button>Click me</button>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>My Page</title>
<script th:src="@{/js/myScript.js}"></script>
</head>
<body>
...
</body>
</html>
fetch('/my-api/data')
.then(response => response.json())
.then(data => {
console.log(data);
// Thymeleaf의 속성 표현식을 사용하여 동적으로 값을 바인딩
const myValue = [[${myValue}]];
console.log(myValue);
});
1. 오브젝트가져오기
th:object="${articles}"
2. form이용할 때
th:action="@{/articles}"
th:method="get"
3.반복문
th:remove="all-but-first"
th:each="searchType : ${searchTypes}"
4.입력
th:value="${searchType.name}" -- input,textarea,select 값
th:text="${searchType.description}" -- 안에 값입력
th:value="${article?.hashtag} ?: _" -- 있으면 그값 아니면 _
5.true,false로 선텍여부(checked로 체크여부도 됨!)
th:selected="${param.searchType != null && (param.searchType.toString == searchType.name)}"
6. param (url의 파라메터가 객체로 가져와짐)
${param.searchValue}
7.링크
1)오름차순내림차순왔다갔다!
th:href="@{/articles(
page=${articles.number},
sort='title' + (*{sort.getOrderFor('title')} != null ? (*{sort.getOrderFor('title').direction.name} != 'DESC' ? ',desc' : '') : ''),
searchType=${param.searchType},
searchValue=${param.searchValue}
title/
2) 이동
th:href="@{'/articles/'+${article.id}}
th:href="@{'/buy/'+${product.data.idx}}"
th:href="@{/articles/form}"
th:href="@{/articles/search-hashtag(
page=${param.page},
sort=${param.sort},
searchValue=${hashtag}
)}"/>
8. 날짜
<attr sel="td.created-at/time" th:datetime="${article.createdAt}" th:text="${#temporals.format(article.createdAt, 'yyyy-MM-dd')}"/>
9.버튼 조건
<attr sel="li[0]/a" th:text="'previous'"
th:href="@{/articles(page=${articles.number - 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} <= 0 ? ' disabled':'')" />
<attr sel="a" th:text="${pageNumber + 1}" th:href="@{/articles(page=${pageNumber}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${pageNumber} == ${articles.number} ? ' disabled' : '')"/>
<attr sel="li[2]/a" th:text="'next'"
th:href="@{/articles(page=${articles.number + 1}, searchType=${param.searchType}, searchValue=${param.searchValue})}"
th:class="'page-link' + (${articles.number} >= ${articles.totalPages - 1} ? ' disabled':'')" />
10.action
th:action="${formStatus?.update} ? '/articles/' + ${article.id} + '/form' : '/articles/form'" th:method="post"
11.onclick
th:onclick="'history.back()'"