이전 포스팅(Thymeleaf layout dialect)에서 타임리프를 사용하면서 공통 사용되는 레이아웃을 나누는
방법을 사용했었는데 이전 방법보다 조금더 간단하게 Fragment로 레이아웃을 나누는 방법을 알아보자.
Fragment로 레이아웃을 나누고 각 페이지에서 공통으로 사용되는 Fragment로 파라미터를 넘길수도 있다.
Thymeleaf Fragment - 1 : Fragment를 나누기
Thymeleaf Fragment - 2 : 파라미터 전달, 사용하기
Thymeleaf Fragment 나누기
개발환경은 Springboot + Thymeleaf를 사용하여 프로젝트를 만들었고
프로젝트에서 Thymeleaf를 사용하기위해 Thymeleaf 의존성 주입,
Web을 사용하기 위해 Spring-boot-starter-web 의존성을 주입받았다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Fragment 구조는 아래와 같이 나누어 생성하였다.
-
header (html 헤더 영역)
-
nav (상단의 메뉴 영역)
-
footer (하단의 footer 영역)
각 공통으로 사용되는 페이지는 fragments 디렉토리에 생성하고
다른 페이지에서 해당 fragment(공통부분)를 불러와서 사용 할 수 있도록 설정할것이다.
먼저 header.html에서는 페이지의 head 부분에서 해당하는 코드를 작성했다.
그리고 Thymeleaf를 사용한다는 선언으로 html 태그에 xmlns:th="http://www.thymeleaf.org"를 추가한다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="fragment-header">
<title>Thymeleaf Fragment</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</head>
</html>
head 부분을 fragment로 선언하려면 head태그에 th:fragment="fragment명"을 작성해준다.
선언한 fragment명으로 다른 페이지에서 해당 fragment를 가져올 수 있다.
th:fragment는 해당 부분을 fragment로 선언한다는 의미
다른 페이지 화면에서는 이 선언한 fragment를 가져와보자.
index.html 에서 fragment-header를 가져오려면 th:replace로 fragment를 불러온다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="/fragments/header.html :: fragment-header"></div>
</head>
<body>
Index Page
</body>
</html>
Index 페이지에서 공통으로 사용하기 위해 fragment로 선언해놓은 fragment-header를 가져오려면
head 부분에 th:replace="fragment 위치 :: fragment명" 을 호출하여 fragment를 불러올수 있다.
th:replace는 해당 태그에 fragment로 선언한 코드로 바꾼다는 의미
위 2가지 코드를 작성 후 실행한뒤 개발자 도구를 열어서 확인해보면 head부분에 fragment-header에서 작성한 코드를 가져온걸 확인 할 수 있다.
head와 같은 방식으로 footer와 nav도 작성해보자.
footer.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<footer th:fragment="fragment-footer" class="footer">
<div class="row justify-content-center">
<img class="mb-2" alt="" width="20" src="data:image/png;base64,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">
<small class="d-block mb-3 ml-3 text-muted">©GHSONG 2020</small>
</div>
</footer>
</html>
nav.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div th:fragment="fragment-nav">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="#">상단메뉴</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-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 mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#" th:href="@{/index}">메인 <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{/index2}">설정</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
</div>
</html>
공통으로 사용될 fragment를 선언 후 페이지에서 fragmet를 불러온다.
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="/fragments/header.html :: fragment-header"></div>
</head>
<body>
<div th:replace="/fragments/nav.html :: fragment-nav"></div>
<!-- 페이지 소스 -->
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Index</h1>
<p class="lead">ghsong tistory</p>
</div>
</div>
<!-- 페이지 소스 -->
<footer th:replace="/fragments/footer.html :: fragment-footer"></footer>
</body>
</html>
fragment로 화면을 구성하고 각 페이지의 소스를 작성하면 레이아웃을 나눌수 있다.
다른 페이지도 작성하여 비교해보자.
index2.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="/fragments/header.html :: fragment-header"></div>
<style>
.jumbotron {background-color: aqua !important;}
</style>
</head>
<body>
<div th:replace="/fragments/nav.html :: fragment-nav"></div>
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Index2</h1>
<p class="lead">ghsong tistory</p>
</div>
</div>
<div th:replace="/fragments/footer.html :: fragment-footer"></div>
</body>
</html>
index2 페이지에서는 공통 head 부분 말고도 이 페이지에서만 사용할 코드를 넣었는데
head 부분에 fragment 선언부분 아래에 <style> 태그를 작성하면 해당 페이지에서만 사용할 style을 추가 할 수 있다.
이 style 태그는 index 페이지에서는 적용되지 않는다.
index.html 화면
index2.html 화면
이처럼 Thymeleaf를 사용할때 fragment를 사용하여 th:fragmet로 선언, th:replace로 호출하여 레이아웃을
보다 쉽게 나눌 수 있다.
git source : https://github.com/sgoho01/Thymeleaf-Fragments/tree/fragment
'IT > Template Engine' 카테고리의 다른 글
Thymeleaf Fragment - 2 (Fragment 에 변수 넘기기) (2) | 2020.06.08 |
---|---|
타임리프 레이아웃 (thymeleaf layout dialect) (9) | 2019.11.20 |
댓글