[Spring || SpringBoot] Thymeleaf Layout - 타임리프 레이아웃 적용 하기

반응형
SMALL

 

화면 단에서 사용하는 헤더 혹은 푸터와 같은 화면에서 공통적으로 사용하는 부분을 모든 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}">&laquo;&laquo;</a>
            </li>
            <li th:if="${currentPage > 0}">
                <a th:href="@{/houdini/index(page=${currentPage - 1})}">&laquo;</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})}">&raquo;</a>
            </li>
            <li th:if="${currentPage < totalPages - 1}">
                <a th:href="@{/houdini/index(page=${totalPages - 1})}">&raquo;&raquo;</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 되는 것을 확인할 수 있다.

 

구현화면

 

첫 번째 사진은 메인화면이고, 두 번째 사진은 상세화면이다.

위와 같이 내용만 변경되고, 사이드바와 헤더는 동일한 것을 확인할 수 있다.

반응형
LIST