Thymeleaf 페이지 레이아웃

진크·2022년 2월 21일
1
post-thumbnail

1. Thymeleaf Layout Dialect dependency 추가하기

Thymeleaf Layout Dialect를 이용하면 하나의 레이아웃을 여러 페이지에 똑같이 적용할 수 있습니다. 공통적으로 적용되는 레이아웃을 미리 만들어놓고 현재 작성 중인 페이지만 레이아웃을 끼워넣으면 됩니다.

//build.gradle

dependencies {
	....
	// Thymeleaf Layout Dialect 추가
	implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.0.0'
	....
}

2. Thymeleaf 페이지 컨트롤러 클래스 작성하기

위에서 만든 페이지를 확인하기 위해 web이라는 패키지를 만들고 ThymeleafExController라는 컨트롤러 클래스를 생성하겠습니다.

package me.jincrates.gobook.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping(value = "/thymeleaf")
@Controller
public class ThymeleafExController {
    @GetMapping(value = "/ex")
    public String thymeleafExample() {
        return "thymeleafEx";
    }
}

애플리케이션을 실행하면 다음 화면을 볼 수 있습니다. thymeleafEx.html 파일에는 따로 header 영역과 footer 영역을 지정하지 않았지만 작성한 내용이 default.html 파일에 포함돼 출력됩니다. 이렇게 공통 영역은 레이아웃으로 만들어 놓고 작성하는 페이지의 content만 변경하면 공통 내용들을 쉽게 관리할 수 있습니다.

thymeleaf-layout-dialect 라이브러리 설치가 완료됐다면 templates 아래 fragments 폴더 생성 후 footer.html, header.html 파일을 생성합니다. 마찬가지로 templates 아래 layouts 폴더를 만들고 default.html 파일을 생성합니다. 마지막으로 thymeleafEx.html 파일을 생성합니다.

다른 페이지에 포함시킬 영역을 th:fragment로 선언해줍니다. thymeleafEx.html에 작성된 내용은 default.html<div layout:fragment=”content”> 영역에 들어갑니다.

<!--resources/templates/fragments/footer.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

    <div th:fragment="footer">
        footer 영역입니다.
    </div>
</html>
<!--resources/templates/fragments/header.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

    <div th:fragment="header">
        header 영역입니다.
    </div>
</html>
<!--resources/templates/layouts/default.html-->

<!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>

    <!-- 각 페이지의 script가 작성될 위치 -->
    <th:block layout:fragment="script"></th:block>
    <!-- 각 페이지의 css가 작성될 위치 -->
    <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>
<!--resources/templates/thymeleafEx.html-->

<!DOCTYPE html>
<html xmlns:th="http//www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/default}">

    <div layout:fragment="content">
        본문 영역입니다.
    </div>

</html>

3. 원하는 템플릿 선정

저는 페이지를 예쁘게 꾸밀 재주가 없습니다. 시간도 넉넉하지 않고요. 그렇다고 흰바탕에 글씨만 놓고 보여주기도 싫습니다.(PPT 발표도 마찬가지죠..) 구글에 html free template 이라고 검색하면 정말 많은 템플릿이 나오며, React, 부트스트랩 등의 다양한 프레임워크로 만들어진 템플릿도 1~2만원 선에서도 퀄리티 좋은 템플릿을 구매할 수 있습니다.

저는 Start Bootstrap이라는 사이트에서 Shop Homepage 무료 템플릿을 사용할 생각입니다. https://startbootstrap.com/template/shop-homepage

템플릿을 그대로 적용하면 재미가 없으니까 저는 레트로 감성을 추가하여 폰트를 둥근모꼴체로 변경했습니다. 웹폰트 적용에 대해서는 검색하시면 많이 나오니 원하시는 폰트로 웹을 바꿔보는 것도 권해드립니다. 자기만족.

<!--resources/templates/fragments/footer.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <div th:fragment="footer">
        <!-- Footer-->
        <footer class="py-5 bg-dark">
            <div class="container"><p class="m-0 text-center text-white">Copyright &copy; Jincrates 2022</p></div>
        </footer>
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
        <!-- Core theme JS-->
        <script src="/js/scripts.js"></script>
    </div>
</html>
<!--resources/templates/fragments/header.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <div th:fragment="header">
        <!-- Navigation-->
        <nav class="navbar navbar-expand-lg navbar-light bg-light">
            <div class="container px-4 px-lg-5">
                <span>
                    <a class="navbar-brand" href="#!">
                        <img src="/assets/img/pixel-squirtle.png" style="width: 28px; padding-bottom: 4px;">
                        <b style="font-size: 28px; color:#60BFB6; text-shadow: -1px 0 #0D0D0D, 0 1px #0D0D0D, 1px 0 #0D0D0D, 0 -1px #0D0D0D; margin-right: -8px">고북</b>
                        <b style="font-size: 28px; color:#F2D22E; text-shadow: -1px 0 #0D0D0D, 0 1px #0D0D0D, 1px 0 #0D0D0D, 0 -1px #0D0D0D;">고북</b>
                    </a>
                </span>
                <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 ms-lg-4">
                        <li class="nav-item"><a class="nav-link active" aria-current="page" href="#!">상품등록</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">상품관리</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">장바구니</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">구매이력</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">로그인</a></li>
                        <li class="nav-item"><a class="nav-link" href="#!">회원가입</a></li>

                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" id="navbarDropdown" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">내 정보</a>
                            <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
                                <li><a class="dropdown-item" href="#!">마이페이지</a></li>
                                <li><hr class="dropdown-divider" /></li>
                                <li><a class="dropdown-item" href="#!">로그아웃</a></li>
                            </ul>
                        </li>
                    </ul>
                    <form class="d-flex">
                        <button class="btn btn-outline-dark" type="submit">
                            <i class="bi-cart-fill me-1"></i>
                            Cart
                            <span class="badge bg-dark text-white ms-1 rounded-pill">0</span>
                        </button>
                    </form>
                </div>
            </div>
        </nav>

        <!-- Header-->
        <header class="bg-dark py-5">
            <div class="container px-4 px-lg-5 my-5">
                <div class="text-center text-white">
                    <h1 class="display-4 fw-bolder">Let's Go-Book</h1>
                    <p class="lead fw-normal text-white-50 mb-0">With this shop hompeage template</p>
                </div>
            </div>
        </header>
    </div>
</html>
<!--resources/templates/layouts/default.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <title>고북고북</title>
    <!-- Favicon-->
    <link rel="icon" type="image/x-icon" href="/assets/favicon.ico" />
    <!-- Bootstrap icons-->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet" />
    <!-- Core theme CSS (includes Bootstrap)-->
    <link href="/css/styles.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <!-- JS, Popper.js, and jQuery -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    <style>
        @font-face {
            font-family: 'DungGeunMo-Regular';
            src: url('https://cdn.jsdelivr.net/gh/wooin21/web/fonts/etc/DungGeunMo-Regular.woff');
            font-weight: normal;
            font-style: normal;
        }

        body {
            font-family: 'DungGeunMo-Regular';
        }
        .item .card:hover {border: #0D0D0D 1px solid;}
    </style>

    <!-- 각 페이지의 script가 작성될 위치 -->
    <th:block layout:fragment="script"></th:block>
    <!-- 각 페이지의 css가 작성될 위치 -->
    <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>

profile
철학있는 개발자 - 내가 무지하다는 것을 인정할 때 비로소 배움이 시작된다.

0개의 댓글