2019. 10. 30. 17:45ㆍSpring/Spring Boot Tutorial
Thymeleaf Layout Dialect
이전 게시글
[Spring Boot Tutorial] 5. Thymeleaf 템플릿 엔진 이용하기
공통 구성요소들(header, nav, footer)를 공유하고, 컨텐츠 관련요소(section)만 변경하고 싶을 때 layout을 사용하면 중복 코드를 최소화 할 수 있습니다.
Thymeleaf를 이용하여 layout을 설정하는 단계는 아래와 같습니다.
layout 설정하는 단계
- layout 템플릿 만들고 재정의할 컨텐츠 요소는
layout:fragment
로 정의 - layout 템플릿을
layout:decorator
를 이용하여 상속받고, 컨텐츠 요소만 override - 중복적으로 추가할 블럭이나, link, script 태그와 같은 요소를
th:insert
로 삽입하기
단계를 진행하기에 앞서, thymeleaf 템플릿엔진에서 layout표현법(Layout Dialect)을 사용하기 위해 pom.xml에 아래의 의존 라이브러리를 추가합니다.
<dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency>
1. layout.html 레이아웃 파일 만들기
이전 과정에서 만들었던 레이아웃에서,
<header>, <nav>, <footer>
요소를 레이아웃 파일에 정의하고,
<section>
태그는 layout:decorator로 상속받는 파일에서 override 할 것입니다.
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <head> <meta name="description" content="Welcome to jiniworld!"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>demo</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/> <link href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:300,400,500,700,900&subset=korean" rel="stylesheet"> <link rel="stylesheet" type="text/css" th:href="@{/static/css/common.css}"/> </head> <body> <header> <h1> <a th:href="@{/v}"><img th:src="@{/static/img/like.png}" width="50" height="auto" alt="demo page" id="btn_home" /></a> </h1> <ul> <li sec:authorize="isAuthenticated()"> <span sec:authentication="principal.username"></span> 님 반가워요! </li> <li><form id="logoutFrm" th:action="@{/logout}" method="post" style="display: inline-block;"> <a href="#" onclick="document.getElementById('logoutFrm').submit()" data-toggle="tooltip" data-placement="logout" title="Logout"><i class="fa fa-power-off"></i></a> </form></li> </ul> </header> <nav class="navbar navbar-expand-lg navbar-light" id="nav_area"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#nav_menu_area" style="margin: 3px;"> <span class="navbar-toggler-icon" style="width: 1em; height: auto;"></span> </button> <div class="collapse navbar-collapse" id="nav_menu_area"> <ul class="navbar-nav" id="nav_menu" style="margin: 0 auto; width: 1140px;"> <li class='nav-item' th:classappend="${currentPage eq 'home'} ? 'active' : ''" th:attr="data-href=@{/v}">홈</li> <li sec:authorize="hasRole('ROLE_ADMIN')" class='nav-item' th:classappend="${currentPage eq 'user'} ? 'active' : ''" th:attr="data-href=@{/v/users}">회원</li> </ul> </div> </nav> <section layout:fragment="f-content"></section> <footer> <p> JINI WORLD - DEMO </p> </footer> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script> <script> $("#nav_menu_area #nav_menu li").on("click", function() { $("#nav_menu_area #nav_menu li").removeClass("active"); $(this).addClass("active"); location.href = $(this).data('href'); }); </script> </body> </html>
main.html
과 user.html
의 <section>
영역을 제외한 나머지 공통 요소를 layout에 작성했습니다.
기존의 코드와의 차이점은, html 태그에 layout xml 네임스페이스를 추가한 것과(ln 4), <section>
태그에 layout:fragment 속성을 추가하여 재정의할 수 있는 구역(ln 44)을 지정할 수 있다.
참고 ※ 위의 코드에서는 <section>
태그 내부가 비어있지만, 만약 내부에 내용을 작성할 경우 이 레이아웃을 이용하는 다른 페이지에서 재정의를 따로 하지 않으면 layout에서 작성된 내용이 그대로 출력됩니다.
2. layout:decorator로 레이아웃 상속받고, layout:fragment override
페이지를 렌더링할 때 이용할 레이아웃 템플릿은 html 태그의 layout:decorator 속성에 설정하고
layout:fragment 속성이 설정되어있는 override할 요소 내부에 코드를 작성한다.
<!DOCTYPE html> <html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="cmm/layout"> <section layout:fragment="f-content"> <div class="container"> <div class="d-title"> <h2>thymeleaf tutorial 과정</h2> </div> <div class="d-content"> <div class="row"> <ul> <li>thymeleaf 관련 configuration 설정하기</li> <li>thymeleaf로 웹 페이지 구성하기</li> <li>JSP vs thymeleaf 비교하기</li> <li>thymeleaf layout 구성하기</li> </ul> </div> </div> </div> </section>
html 태그의 layout:decorator
속성에 사용하고자 하는 레이아웃 파일 경로를 작성합니다. (ln 6)
이때, 경로위치는 thymeleaf 템플릿 prefix로 설정한 경로를 기준으로 합니다.
이전과정에서 우리는 prefix를 classpath:/templates/
로 설정했었죠.
layout파일을 cmm 폴더에 위치하기 때문에 'cmm/layout'이라고 작성합니다.
그리고, override할 section영역을 작성합니다. (ln 8~24)
3. 중복으로 이용될 코드블럭 정의
th:fragment 사용 예
- 레이아웃 영역 중 override 가능한 영역을 설정
- 여러 페이지에서 공통적으로 사용할 코드영역 설정
th:fragment는 여러 페이지에서 필요에 따라 포함시킬 코드영역을 정의할 때에도 사용할 수 있다.
layout 템플릿에 작성했던 css, js import 부분을 th:fragment
로 정의해보자.
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <th:block th:fragment="f-header"> <meta name="description" content="Welcome to jiniworld!"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/> <link href="https://fonts.googleapis.com/css?family=Noto+Sans+KR:300,400,500,700,900&subset=korean" rel="stylesheet"> <link rel="stylesheet" type="text/css" th:href="@{/static/css/common.css}"/> </th:block> <th:block th:fragment="f-footer"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script> <script> $("#nav_menu_area #nav_menu li").on("click", function() { $("#nav_menu_area #nav_menu li").removeClass("active"); $(this).addClass("active"); location.href = $(this).data('href'); }); </script> </th:block> </html>
cmm/block 위치에 f-header, f-footer 라는 이름으로 fragment를 생성했습니다.
<head> <title>demo</title> <th:block th:insert="cmm/block :: f-header"></th:block> </head> <body> ... <th:block th:insert="cmm/block :: f-footer"></th:block> </body>
layout에서 공통으로 사용할 meta, link, script 태그들을 제거하고
th:insert
속성을 설정한 <th:block>
요소를 삽입합니다.
cmm/block 파일에 있는 f-header라는 fragment를 head 태그 내에 insert 하고(ln 3)
body태그 맨 마지막 줄에 f-footer fragment를 insert 합니다.(ln 9)
++ tip
th:fragment를 잘 활용하면, 중복 코드를 효율적으로 줄일 수 있다.
1) <head>
내의 <title>
만 재정의 가능!
위의 layout 페이지의 <head>
태그를 아래와 같이 변경한다.
<head> <title> <th:block layout:fragment="f-title">demo</th:block> </title> <th:block th:insert="cmm/block :: f-header"></th:block> </head>
f-title fragment만 재정의 함으로써, 페이지의 title을 변경할 수 있다.
<!DOCTYPE html> <html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="cmm/layout"> <th:block layout:fragment="f-title">main</th:block> <section layout:fragment="f-content"> <div class="container"> <div class="d-title"> <h2>thymeleaf tutorial 과정</h2> </div> <div class="d-content"> <div class="row"> <ul> <li>thymeleaf 관련 configuration 설정하기</li> <li>thymeleaf로 웹 페이지 구성하기</li> <li>JSP vs thymeleaf 비교하기</li> <li>thymeleaf layout 구성하기</li> </ul> </div> </div> </div> </section>
<head>
태그 영역중 <title>
만 재정의한 main.html 코드이다.
ln 8 과같이 f-title fragment를 override하는 것으로 타이틀을 변경할 수 있다.
이 원리를 잘 파악한다면, 타임리프를 이용하여 문서구조를 만들 때 중복코드를 대량 단축할 수 있을것입니다.
2) 페이지별 개별적 script 작성 팁
만일, main.html나 user.html에서 jquery를 이용하려면 사전에 jquery라이브러리를 import해야합니다.
보통 javascript import태그는 body태그 맨 하단에 작성하죠.
그렇다면, section 영역보다 아래에 작성되는 부분입니다.
이런 경우, layout에 f-footer fragment를 insert 했던 부분 아래에 frgment를 하나 더 삽입하면 매우 깔끔하게 처리가 됩니다.
<th:block th:insert="cmm/block :: f-footer"></th:block> <th:block layout:fragment="f-script"></th:block>
만일, 특정 페이지에서만 이용될 javascript를 작성하고자 할 때에는 f-script fragment를 overide하여 그 태그내의 <script>
태그에 javascript코드를 작성하면 됩니다.
※ GitHub에서 demo 프로젝트를 다운받아 볼 수 있습니다.
'Spring > Spring Boot Tutorial' 카테고리의 다른 글
[Spring Boot Tutorial] 8. AccessDeniedHandler 구현클래스로 인증&인가 Exception 핸들링 (0) | 2019.12.12 |
---|---|
[Spring Boot Tutorial] 7. JavaConfig 설정으로 Spring Security 커스터마이징 (2) | 2019.11.29 |
[Spring Boot Tutorial] 5. Thymeleaf 템플릿 엔진 이용하기 (0) | 2019.10.21 |
[Spring Boot Tutorial] 4. Spring security 맛보기 (0) | 2019.10.04 |
[Spring Boot Tutorial] 3. JPA CRUD (6) | 2019.09.16 |