티스토리 뷰

Overview

angularjs, reactjs, viewjs 등이 웹 회면에서 많이 사용되면서부터 Server-side template language의 역활이 많이 감소하였습니다. 지금 프로젝트에서도 주 역할은 화면 구성 정도 입니다. 기존에 화면 구성은 Tiles, Sitemesh 등을 많이 사용하였는데 요즘은 개인적으로 Thymeleaf을 많이 선호하는 편입니다. Thymeleaf이 layout 구성하는데 쉽고 명시적이기 때문입니다. 


개발 환경 : spring boot 2.1.2, gradle, spring 5.x, java 8, Thymeleaf 3.x

Dependency (gradle) 추가


implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect')


1
2
3
4
5
6
7
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect')
}
cs

예제 코드

fragments/commonHead.html

1
2
3
4
5
6
7
8
9
10
11
12
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 
    <!-- vendor CSS -->
    <link th:src="@{/assets/vendor/bootstrap/bootstrap.min.js}" rel="stylesheet">
 
    <!-- 컨텐츠 페이지 CSS 삽입 -->
    <th:block layout:fragment="pageCustomCss"></th:block>
 
    <script th:src="@{/assets/vendor/jquery/jquery.js}"></script>
    <script th:src="@{/assets/vendor/bootstrap/bootstrap.min.js}"></script>
cs


fragments/commonFooter.html

1
2
3
4
5
6
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <footer th:fragment="commonFooter">
        Layout Common Footer
    </footer>
</html>
cs


defaultLayout.html 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <title layout:title-pattern="$LAYOUT_TITLE : $CONTENT_TITLE">Eblo</title>
    <!-- 공통 css, js, meta tag -->
    <th:block th:replace="fragments/commonHead"></th:block>
</head>
<body>
    <h1>기본 레이아웃</h1>
    <!-- page content body 구성 -->
    <th:block layout:fragment="content"></th:block>
    <!-- 공통 footer -->
    <footer th:replace="fragments/commonFooter :: commonFooter"></footer>
    <!-- 공통 javascript -->
    <th:block th:replace="fragments/commonScript"></th:block>
    <!-- page content javascript -->
    <th:block layout:fragment="pageCustomScript"></th:block>
</body>
</html>
cs



exampleLayout.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
  layout:decorator="layout/defaultLayout">
<head>
    <title>Thymeleaf 예제</title>
</head>
<body>
<th:block layout:fragment="content">
<h1>Page Content</h1>
</th:block>
 
<th:block layout:fragment="pageCustomScript">
    <script th:src="@{/assets/vendor/jquery/jquery.js}"></script>
</th:block>    
<th:block layout:fragment="pageCustomCss">
    <script th:src="@{/assets/vendor/jquery/jquery.css}"></script>
</th:block>    
</body>
</html>
cs

Layout 화면 구성 

Thymeleaf에서 화면 구성을 위해 사용하는 주요 속성은 namespace, decorate, title-pattern, insert, replace, include, fragment 등이 있습니다. 

namespace 정의 

1
2
3
4
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
...
</html>
cs

html에 이렇게 정의하면 레이아웃 화면으로 인식됩니다. 


title 처리 

1
<title layout:title-pattern="$LAYOUT_TITLE : $CONTENT_TITLE">Eblo</title>
cs
이렇게 하면 "Layout에 정의된 타이틀+' : '+컨텐츠페이지 타이틀" 형태로 생성됩니다.  


결과 화면 

1
<title>Eblo : Thymeleaf 예제</title>
cs

공통 컨텐츠 처리 

공통 컨텐츠는 th:insert, th:replace, th:include 등을 이용해서 구성할 수 있습니다. 

1
<footer th:replace="fragments/commonFooter :: commonFooter"></footer>
cs

 공통 head, footer, script, css 등을 만들고 위의 형태로 원하는 위치에 배치를 하면 됩니다. 


페이지별 컨텐츠 삽입 

layout:fragment를 이용하여 원하는 위치에 배치를 하고 컨텐츠 페이지에서 fragement를 생성하면 해당 컨텐츠가 삽입됩니다. 

1
<th:block layout:fragment="content"></th:block>
cs

컨텐츠 페이지 예제 

1
2
3
<th:block layout:fragment="content">
<h1>Page Content</h1>
</th:block>
cs

th:insert, th:replace, th:include 차이 비교 

위의 세가지는 유사한 결과물을 만들지만 조금씩 차이가 있습니다. 

1
2
3
4
5
6
    <!-- th:include -->
    <th:block th:include="fragments/commonHead"></th:block>
    <!-- th:insert -->
    <th:block th:insert="fragments/commonHead"></th:block>
    <!-- th:replace -->
    <th:block th:replace="fragments/commonHead"></th:block>
cs

 위의 세가지는 동일한 결과물을 만듭니다. 주요 차이점은 fragment의 태그를 포함하느냐 안하느냐 인데 위의 예제에서는 태그를 <th:block>을 이용하영ㅆ고 commonHead.html 자체도 tag가 없이 페이지 전체를 include하기 때문에 의미가 없습니다. 


태그와 fragment를 사용하는 예제 

1
2
3
    <footer th:include="fragments/commonFooter :: commonFooter"></footer>
    <footer th:replace="fragments/commonFooter :: commonFooter"></footer>
    <footer th:insert="fragments/commonFooter :: commonFooter"></footer>
cs


결과 화면 

1
2
3
4
5
6
7
8
9
    <footer>
        Layout Common Footer
    </footer>
    <footer>
        Layout Common Footer
    </footer>
    <footer><footer>
        Layout Common Footer
    </footer></footer>
cs

th:include와 th:replace는 fragment 태그를 제외하고 결과를 가져오고 th:insert는 fragment 태그를 포함해서 가져오는 차이가 있습니다. 


또 다른 테스트 

1
2
3
    <footer th:include="fragments/commonFooter :: commonFooter" th:remove="tag"></footer>
    <footer th:replace="fragments/commonFooter :: commonFooter" th:remove="tag"></footer>
    <footer th:insert="fragments/commonFooter :: commonFooter" th:remove="tag"></footer>
cs

th:remove="tag" attribute를 이용하는 테스트 입니다. 


결과 화면 

1
2
3
4
5
6
7
8
        Layout Common Footer
    
    <footer>
        Layout Common Footer
    </footer>
    <footer>
        Layout Common Footer
    </footer>
cs

th:include는 th:remove="tag"가 적용이 되서 태그가 사라졌습니다. 


결론적으로 공통을 처리하는 것은 th:include, th:replace, th:insert가 있고 tag를 제외 하고 싶다면 태그 자체를 <th:block></th:block>를 이용하는게 좋습니다. 

Layout 사용하기 

exampleLayout.html 코드를 보셔야 합니다. 

레이아웃 선택하기 

1
2
3
4
5
6
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
  layout:decorator="layout/defaultLayout">
...
</html>
cs

layout:decorator에 선언을 합니다. 이때 확장자는 제외합니다. 


타이틀 

1
    <title>Thymeleaf 예제</title>
cs

이렇게 하면 Layout에 타이틀과 컨텐츠 페이지의 타이틀이 조합이 되서 결과가 만들어 집니다. (상단 참조) 


컨텐츠 구성 

1
2
3
4
5
6
7
8
9
10
<th:block layout:fragment="content">
<h1>Page Content</h1>
</th:block>
 
<th:block layout:fragment="pageCustomScript">
    <script th:src="@{/assets/vendor/jquery/jquery.js}"></script>
</th:block>    
<th:block layout:fragment="pageCustomCss">
    <script th:src="@{/assets/vendor/jquery/jquery.css}"></script>
</th:block>
cs

layout:fragment를 이용하여 구현하면 Layout 페이지에 선언된 위치에 컨텐츠가 생성이 됩니다. 

위의 예제에는 <th:block>을 이용했지만 <section>, <div>, <p>, <footer> 등 원하는 태그를 사용해서 구현할 수 있습니다. 

마무리 

다른 프레임워크와의 비교 

Tiles의 경우 새로운 페이지를 개발 할 때 마다 xml에 설정을 잡아 줘야 하는 불편함이 있습니다. 

Sitemesh의 경우에는 url 패턴에 따라 레이아웃을 적용하게 되어 있고 컨텐츠 페이지에도 별도 코딩을 삽입하지 않기 때문에 사용의 편리함은 최고 인거 같습니다. 단점은 sitemesh는 단지 화면 구성만 하기 때문에 front에는 jsp 또는 별도 template language를 추가해서 사용해야 한다는 것과 속도가 아무래도 느립니다. 

Thymeleaf의 장점은 html 안에서 모든걸 해결한다는 것입니다. 화면 구성을 하면서 별도 xml에 선언을 해주거나 설정을 잡을 필요가 없습니다.  이 부분은 직접 사용해봐야 그 편리함을 알 수 있을 거 같습니다. 단점은 사용하는 페이지에 명시적으로 layout을 설정해 줘야 한다는 것입니다. 만약 다른 프레임워크로 변경한다든지 다른 레이아웃을 적용하려 한다면 각각의 페이지를 수정해 줘야 합니다. 


제가 Thymeleaf를 사용하는 이유는 view 단 처리와 화면 구성을 하나의 프레임워크로 처리할 수 있고 설정과 사용이 쉽기 때문입니다. 

기존에 freemarker나 velocity를 사용해 봤다면 반 나절 정도 학습하고 바로 개발을 할 수 있습니다. 

참고 


Github 링크

https://github.com/kkaok/examples

댓글
  • 프로필사진 진화 안녕하세요 글 정말 잘봤습니다 ~ 감사합니다.
    그런데 혹시 defaultLayout.html 파일 17번째 줄에
    <th:block layout:fragment="pageCustomScript"></th:block>
    을 fragments/commonScript.html 파일에 넣어도 상관없는것 인가요?

    <th:block layout:fragment="pageCustomCss"></th:block>
    이부분은 fragments/commonHead.html 파일에 넣으셨는데
    js쪽은 왜 다르게 하셨는지 궁금하네요~

    그리고 저는 defaultLayout.html 쪽에 넣는것이 빨간줄도 안뜨고 더 깔끔한것 같은데 상관없겠죠~?
    2019.08.10 20:06 신고
  • 프로필사진 까오기 까오기 넵 pageCustomScript는 commonScript.html 안에 넣으셔도 상관은 없습니다.

    화면 구성은 개발 하시면서 쉽고 이해하기 쉬운 방향으로 구성하시면 될거 같습니다.

    좋은 하루되세요.

    2019.08.23 17:24 신고
  • 프로필사진 리액트 혹시 이렇게 설정하면 화면 이동 href 하면 뷰로 지정한 영역만 교체가 되는건가요?

    그게 아니라면 혹시 이런 방법을 구현할 수 있을까요?
    2019.09.06 19:33
  • 프로필사진 까오기 까오기 href는 화면 자체를 이동하는 처리입니다.
    thymeleaf는 화면을 조합해서 최종 html을 만드는 거구요.
    원하시는 특정 영역만 변경하는 처리는 ajax로 구현하시면 될거 같습니다.
    좋은 하루되세요.
    2019.09.16 18:02 신고
댓글쓰기 폼