화면 단에서 사용하는 헤더 혹은 푸터와 같은 화면에서 공통적으로 사용하는 부분을 모든 HTML이나 JSP 화면에 적용하게 되면, 수정이 이뤄지면 매번 고쳐야 하는 문제가 있다. 즉 비효율적이다.
필자는 JSP에서는 Include와 같은 것을 사용해 공통. jsp를 만들고 해당 jsp 부분을 가져오는 방식으로 개발했었다.
하지만 타임리프에서는 이런 번거로운 과정을 줄여주기 위해 레이아웃 기능을 제공해 준다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
위 Dependencies를 build.gradle에 추가한다.
- 첫 번째는 타임리프 사용을 위함이고
- 두 번째는 레이아웃 기능을 사용하기 위함이다.
Layout 구성하기
<!DOCTYPE html>
<html lang="ko"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title th:fragment="title">Title</title>
<!-- CSS 파일 -->
<link rel="stylesheet" th:href="@{/css/main.css}"/>
</head>
<body>
<div class="layout">
<!-- 사이드바 -->
<div th:replace="~{fragments/sidebar :: sidebar}"></div>
<!-- 메인 콘텐츠 영역 -->
<main class="main-content">
<div class="container">
<div th:replace="~{fragments/header :: header}"></div>
<th:block layout:fragment="content"></th:block>
</div>
</main>
<div th:replace="~{fragments/scripts :: scripts}"></div>
</div>
</body>
</html>
필자는 위 코드와 같이 레이아웃을 구현하였다.
해당 레이아웃은 사이드바와 헤더, 그리고 스크립트 부분을 공통으로 쓰기 위해서 구성한 것이고, content 부분만 변경이 된다.
하나씩 설명을 하자면
<title th:fragment="title">title</title>
위와 같이 th:fragment="title"이 레이아웃으로 설정되어 있다.
content 부분에도 title이 사용되는데, content 안에서 따로 title을 설정해주지 않으면 레이아웃에서 설정한 태그 안에 있는 title이라는 문구가 title로 설정될 것이다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<aside class="sidebar" id="sidebar" th:fragment="sidebar">
<div class="sidebar-section">
<h2 class="sidebar-title">내 정보</h2>
<ul class="category-list">
<li><a href="#">Categories</a></li>
<li><a href="#">Latest Posts</a></li>
<li><a href="#">Contact</a></li>
<li><a href="#">... (other items) ...</a></li>
</ul>
</div>
</aside>
</body>
</html>
사이드 바에 구성된 화면이다.
<aside class="sidebar" id="sidebar" th:fragment="sidebar">
th:fragment = "sidebar"로 지정된 것은 layout에 sidebar로 지정된 곳에 적용된다.
마지막으로, 고정으로 사용하지 않는 content 부분이다.
<!DOCTYPE html>
<html lang="ko"
layout:decorate="~{common/layout}"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
>
<head>
<title>Home</title>
</head>
<body>
<div class="post-list" layout:fragment="content">
<div class="post-list" th:each="card : ${posts}">
<a th:href="@{'/houdini/post/view/' + ${card.BOARD_ID}}">
<div class="post-card">
<h2 class="post-title" th:text="${card.TITLE}">2024년 웹 디자인 트렌드 총정리</h2>
<div class="post-meta">
<span th:text="'작성자: ' + ${card.INST_ID}">작성자: 디자인마스터</span>
<span th:text="${card.CUR_DTM}">2024.03.15</span>
<span class="post-comments">댓글 42</span>
</div>
</div>
</a>
</div>
<div class="pagination">
<ul>
<li th:if="${currentPage > 0}">
<a th:href="@{/houdini/index?page=0}">««</a>
</li>
<li th:if="${currentPage > 0}">
<a th:href="@{/houdini/index(page=${currentPage - 1})}">«</a>
</li>
<li th:classappend="${page == currentPage} ? 'active'"
th:each="page : ${#numbers.sequence(currentPage - 2 > 0 ? currentPage - 2 : 0, currentPage + 2 < totalPages - 1 ? currentPage + 2 : totalPages - 1)}">
<a th:href="@{/houdini/index(page=${page})}"
th:text="${page + 1}">1</a>
</li>
<li th:if="${currentPage < totalPages - 1}">
<a th:href="@{/houdini/index(page=${currentPage + 1})}">»</a>
</li>
<li th:if="${currentPage < totalPages - 1}">
<a th:href="@{/houdini/index(page=${totalPages - 1})}">»»</a>
</li>
</ul>
</div>
</div>
</body>
</html>
<head>
<title>Home</title>
</head>
content의 내용이며, title이 Home으로 지정되어 Layout에 설정된 title이 아닌 Home 문구를 사용하게 된다.
<div class="post-list" layout:fragment="content">
layout:fragment="content"를 통해 layout의 content에 Import 되는 것을 확인할 수 있다.
구현화면
첫 번째 사진은 메인화면이고, 두 번째 사진은 상세화면이다.
위와 같이 내용만 변경되고, 사이드바와 헤더는 동일한 것을 확인할 수 있다.
'Backend > Spring || SpringBoot' 카테고리의 다른 글
[Spring || SpringBoot] Controller와 RestController 란? (2) | 2024.12.29 |
---|---|
[Spring || SpringBoot] MVC 패턴이란? 스프링 MVC와 Counter 앱 예제 (0) | 2024.11.03 |
[Spring || SpringBoot] IPv4 설정 (0) | 2024.09.09 |
[Spring || SpringBoot] Spring에서 @Value로 Properties 값 가져오기 (0) | 2024.09.09 |
[Spring || SpringBoot] Rest API 공통 Response 포맷 구현 (1) | 2024.03.25 |