- Thymeleaf 는 서버 사이드 렌더링을 하지 않고 브라우저에 띄워도 정상적인 화면을 볼 수 있다.
- thymeleaf-layout-dialect 를 이용하면 하나의 레이아웃을 미리 만들어놓고 현재 작성 중인 페이지만 레이아웃에 끼워넣으면 된다.
- Thymeleaf docs : Documentation - Thymeleaf
- thymeleaf-layout-dialect Github : ultraq/thymeleaf-layout-dialect: A dialect for Thymeleaf that lets you build layouts and reusable templates in order to improve code reuse (github.com)
- thymeleaf-layout-dialect Maven : Maven Repository: nz.net.ultraq.thymeleaf » thymeleaf-layout-dialect (mvnrepository.com)
필요한 라이브러리
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.0.0</version>
</dependency>
layout1.html
- 여기서 body안에있는 header, content, footer 등은 여러 페이지에 공통적으로 쓰이는부분이라 파일을 분할되었다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link th:href="@{/css/layout1.css}" rel="stylesheet">
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<th:block layout:fragment="script"></th:block>
<th:block layout:fragment="css"></th:block>
</head>
<body>
<div th:replace="fragments/header::header"></div>
<div layout:fragment="content" class="content">
</div>
<div th:replace="fragments/footer::footer"></div>
</body>
</html>
header.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<div th:fragment="header">
<nav class="navbar navbar-expand navbar-dark bg-dark" aria-label="Second navbar example">
<div class="container-fluid">
<a class="navbar-brand" href="/">Shop</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample02" aria-controls="navbarsExample02" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExample02">
<ul class="navbar-nav me-auto">
<li class="nav-item" sec:authorize="hasAnyAuthority('ROLE_ADMIN')">
<a class="nav-link active" aria-current="page" href="/admin/item/new">상품등록</a>
</li>
<li class="nav-item" sec:authorize="hasAnyAuthority('ROLE_ADMIN')">
<a class="nav-link" href="/admin/items">상품관리</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/cart">장바구니</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/orders">구매이력</a>
</li>
<li class="nav-item" sec:authorize="isAnonymous()">
<a class="nav-link" href="/members/login">로그인</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" href="/members/logout">로그아웃</a>
</li>
</ul>
<form class="d-flex" th:action="@{/}" method="get">
<input name="searchQuery" class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
</div>
</html>
footer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="footer" class="footer">
<footer class="page-footer font-small cyan darken-3">
<div class="footer-copyright text-center py-3">
2022 Shopping Mall Example WebSite
</div>
</footer>
</div>
</html>
main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{laouts/layout1}">
<div layout:fragment="content">
<h1>메인페이지입니다.</h1>
</div>
</html>
또는
memberForm.html
- th:block css 부분은 layout1.html 안에서의 head 안에 css 부분에 대체된다.
- th:block script부분은 layout1.html 안에서의 head 안에 script 부분에 대체된다.
- content 부분은 layout.html 안에서의 body 안에 content 부분에 대체된다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{laouts/layout1}">
<th:block layout:fragment="css">
<style>
.fieldError{
color: #bd2130;
}
</style>
</th:block>
<th:block layout:fragment="script">
<script>
$(document).ready(function (){
var errorMessage = [[${errorMessage}]];
if(errorMessage != null){
alert(errorMessage);
}
});
</script>
</th:block>
<div layout:fragment="content">
<form action="/members/new" role="form" method="post" th:object="${memberFormDto}">
<div class="row g-3">
<div class="col-12">
<label th:for="name" class="form-label">이름</label>
<input type="text" th:field="*{name}" class="form-control" placeholder="이름을 입력해주세요">
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="fieldError">Incorrect data</p>
</div>
<div class="col-12">
<label th:for="email" class="form-label">이메일주소</label>
<input type="email" th:field="*{email}" class="form-control" placeholder="이메일을 입력해주세요">
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="fieldError">Incorrect data</p>
</div>
<div class="col-12">
<label th:for="password" class="form-label">비밀번호</label>
<input type="password" th:field="*{password}" class="form-control" placeholder="비밀번호 입력">
<p th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="fieldError">Incorrect data</p>
</div>
<div class="col-12">
<label th:for="address" class="form-label">주소</label>
<input type="text" th:field="*{address}" class="form-control" placeholder="주소를 입력해주세요">
<p th:if="${#fields.hasErrors('address')}" th:errors="*{address}" class="fieldError">Incorrect data</p>
</div>
<div style="text-align: center">
<button type="submit" class="btn btn-primary" style="">Submit</button>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
</div>
</form>
</div>
</html>
layout1.html 안에서의 부트스트랩 적용
5.0 이상 버전에서 변경 부분에대해서는 아래링크 참고
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link th:href="@{/css/layout1.css}" rel="stylesheet">
<!-- Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
html 수정시 자동 컴파일 할수있게 도와주는 라이브러리
참고 사이트 : Spring Boot Devtools 사용법 (tistory.com)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.6.2</version>
</dependency>
- Recompile 진행하면된다
'2022 > JPA입문(完)' 카테고리의 다른 글
부트스트랩(bootstrap) 5.1 적용 (0) | 2022.01.24 |
---|---|
스프링 시큐리티 - 1 (0) | 2022.01.23 |
Spring Data JPA - 2 (0) | 2022.01.20 |
Spring Data JPA - 1 (0) | 2022.01.18 |
JPA - 1 (0) | 2022.01.10 |