타임리프(Thymeleaf) 간단 정리
2025. 5. 9. 19:18ㆍSpring/MVC
1. 타임리프 선언
<!-- HTML 최상단에 타임리프 네임스페이스 선언이 필요 -->
<html xmlns:th="http://www.thymeleaf.org">
2. 타임리프 핵심
- 핵심은 th:xxx 가 붙은 부분은 서버사이드에서 렌더링 되고, 기존 속성을 대체한다.
- HTML 파일을 브라우저에서 직접 열면 th: 속성은 무시되고 원래 HTML만 보여짐 → 순수 HTML처럼 보임
- 서버를 통해 렌더링하면 th: 속성이 적용된 최종 결과가 보여짐
- JSP는 서버 렌더링 없이 브라우저에서 직접 열면 깨지지만, 타임리프는 안 깨짐
- 이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿이라 한다.
3. 속성 변경 - th:xxx
<!-- 정적 리소스 경로 변경 -->
<a href="/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}">
<!-- 클릭 이벤트 속성 변경 -->
<button onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|">등록</button>
- 기존 HTML 속성을 동적으로 바꾸고 싶을 때 사용한다.
- th:href, th:onclick, th:src 등 대부분의 속성에 th:를 붙여서 사용 가능
- th:onclick 같이 JS 코드가 필요한 경우는 리터럴 대체 문법 (|...|) 을 사용하면 편리하게 작성할 수 있다
4. 내용 변경 - th:text
<td th:text="${item.price}">10000</td>
- th:text는 내부 텍스트를 ${...} 표현식 값으로 바꿔줌
- 원래 있는 10000은 무시되고, 서버에서 렌더링한 값으로 대체됨
5. 변수 표현식 - ${...}
item.getPrice() → ${item.price}
- 모델(Model)에 담긴 데이터나 타임리프 변수로 선언한 값을 출력할 때 사용
- 자바의 getter 방식으로 접근
6. URL 링크 표현식 - @{...}
<!-- 기본 사용 -->
<a th:href="@{/basic/items}">목록</a>
<!-- 경로 변수 포함 -->
<a th:href="@{/basic/items/{id}(id=${item.id})}">상세보기</a>
<!-- 리터럴 대체 -->
<a th:href="@{|/basic/items/${item.id}|}">상세보기</a>
<!-- 쿼리 파라미터 포함 -->
<a th:href="@{/basic/items/{id}(id=${item.id}, query='test')}">상세보기</a>
- URL 링크 표현식 @{...}을 사용하면 컨텍스트 경로를 자동으로 포함해준다
- 컨텍스트 경로: 브라우저에서 접속할 때, 웹 앱이 시작되는 하위 경로
- 개발자가 application.properties (또는 yml) 같은 설정파일에서 지정할 수 있다
- 기본 컨텍스트 경로는 / 루트 (localhost:8080/)
7. 문자열 리터럴 대체 문법 - |...|
- 타임리프에서 문자와 표현식 등은 분리되어 있기 때문에 + 기호를 통해 각각 더해서 사용해야 한다.
- 하지만 리터럴 대체 문법을 사용하면, 더하기 없이 편리하게 사용할 수 있다.
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
→ <span th:text="|Welcome to our application, ${user.name}!|">
<button th:onclick="'location.href=' + '\'' + @{/basic/items/add} + '\''">
→ <button th:onclick="|location.href='@{/basic/items/add}'|">
8. 반복 출력 - th:each
<tr th:each="item : ${items}">
<td th:text="${item.name}"></td>
</tr>
- items는 Model에 담긴 리스트
- 반복문을 돌면서 item 객체를 하나씩 꺼내 사용
- HTML 태그를 반복해서 렌더링할 수 있음
9. 예시 코드
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../../static/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>상품 목록</h2> </div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">상품 등록</button>
</div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>상품명</th>
<th>가격</th>
<th>수량</th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${items}">
<td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
<td><a href="item.html" th:href="@{|/basic/items/${item.id}|}" th:text="${item.itemName}">상품명</a></td>
<td th:text="${item.price}">10000</td>
<td th:text="${item.quantity}">10</td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>