자바 라이브러리이며, 웹과 웹이 아닌 환경 양쪽에서 텍스트, HTML, XML, Javascript, CSS 그리고 텍스트를 생성할 수 있는 템플릿 엔진
<input type="text" value="test" th:value="${item}">
input 태그는 th:value를 통해 item이라는 변수에 값이 존재하면 해당 값을 세팅해줌.
item이 존재하지 않으면 value="test"를 통해 "test"라는 문자열 세팅
th:xxx가 붙으 부분은 서버 사이드에서 렌더링 되어 기존의 것을 대체하고, th:xxx가 없으면 xxx 속성이 그대로 사용됨.
pom.xml 디펜던시 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
.yaml 파일 설정
spring:
thymeleaf:
prefix: classpath:templates/
suffix: .html
cache: false
html 상단에 선언
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
Fragment로 레이아웃을 나누고 각 페이지에서 공통으로 사용되는 Fragment로 파라미터를 넘길 수 있다.
templates/fragments생성
footer.html
<html xmlns:th="http://www.thymeleaf.org">
<!-- fragment 설정 -->
<div th:fragment="defaultFooter" class="card mt-5">
<h5 class="card-header">Footer</h5>
<div class="card-body">
<h5 class="card-title">Copyright 2022.</h5>
</div>
</div>
</html>
<html xmlns:th="http://www.thymeleaf.org">
<!-- fragment 설정 -->
<nav th:fragment="defaultHeader" class="navbar navbar-expand-lg bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/index.jsp">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/board/list">게시물 목록</a>
</li>
</ul>
<form class="d-flex" role="search" method="get" action="/board/list.jsp">
<input class="form-control mr-sm-2" type="search" name="query" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</div>
</nav>
</html>
default-layout.html 생성
프로젝트를 하다보면 layout 구성이 다양해지는 경우가 많다.
그렇기때문에 디폴트를 만드는 것, 다른 레이아웃 구조가 생긴다면 다른 파일명으로 생성 후 사용하면 됨.
전체적으로 이 레이아웃으로 사용하겠다는 의미임
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title>Spring Boot</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<!-- 페이지에 선언된 css 영역을 가져옴 -->
<th:block layout:fragment="css"></th:block>
</head>
<body>
<!-- 헤더사용시 헤더경로와 그 이름 -->
<th:block th:replace="fragments/header :: defaultHeader"></th:block>
<!-- 페이지 컨텐츠 가져오기 -->
<th:block layout:fragment="content"></th:block>
<!-- 푸터 가져오기 -->
<th:block th:replace="fragments/footer :: defaultFooter"></th:block>
<!-- 공통 스크립트 선언 -->
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
<!-- 페이지에 선언된 js 영역을 가져옴 -->
<th:block layout:fragment="script"></th:block>
</body>
</html>
- th:replace="frament경로 :: fragment이름" 속성은 해당 영역을 fragment로 치환하겠다는 의미입니다.
- layout:fragment="content"는 해당 layout을 사용하는 content의 내용을 불러오겠다는 의미입니다.
layout:fragment=”css”
: 호출된페이지에 css 이름으로 설정된 html 가져와서 붙여줌
th:replace=”fragments/header :: defaultHeader”
: header.html 내에 defaultHeader 이름으로 설정된 html 가져와서 붙여줌
layout:fragment=”content”
: 호출된 페이지에 content 이름으로 설정된 html 가져와서 붙여줌
th:replace=”fragments/footer :: defaultFooter”
: footer.html 내에 defaultFooter 이름으로 설정된 html 가져와서붙여줌
layout:fragment=”script”
: 호출된 페이지에 script 이름으로 설정된 html 가져와서 붙여줌
BoardList.html
layout:decorate=””
: 사용하고싶은 레이아웃경로를 설정
layout:fragment=”css”
: 현재 페이지에 필요한 css를 작성하면 레이아웃에 자동으로 붙여짐
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default-layout}">
<!-- 레이아웃에 선언된 content 부분 -->
<th:block layout:fragment="content">
<div class="container">
<h2>게시물 목록</h2>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>번호</th>
<th>종류</th>
<th>제목</th>
<!-- <th>작성자</th> -->
<th>등록일자</th>
</tr>
</thead>
<tbody>
<tr th:each="board : ${boardList}">
<td th:text="${board.boardSeq}">1</td>
<td th:text="${board.boardType}">공지사항</td>
<td>
<a th:href="@{/board/{boardSeq}(boardSeq=${board.boardSeq})}" th:text="${board.title}">
안녕하세요
</a>
</td>
<!-- <td th:text="${board.userName}">작성자</td> -->
<td th:text="${board.regDate}">2011-10-15</td>
</tr>
</tbody>
</table>
<a href="/board/form" class="btn btn-primary">등록</a>
</div>
</th:block>
</body>
</html>