Spring4 + Thymeleaf(3버전) + thymeleaf layout dialect(2버전) 레이아웃 설정하기

실습환경

개발환경
windows10(64)
spring4
java1.8
tomcat9

들어가기

저번엔 Spring4에 Thymeleaf2 그리고 thymeleaf layout dialect 레이아웃을 셋팅해보았다. 이번에는 Thymeleaf를 현재 최신버전인 3버전, thymeleaf layout dialect 2버전 설정을 해보겠다.

Spring4 + Thymeleaf3 + thymeleaf layout dialect 레이아웃 셋팅하기

1.pom.xml 수정하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Thymeleaf 라이브러리 -->
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>

<!-- Thymeleaf 라이브러리 -->
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring4 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.9.RELEASE</version>
/dependency>

<!-- thymeleaf-layout-dialect 라이브러리 -->
<!-- https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>2.3.0</version>
</dependency>

2.servlet-context.xml 파일 수정하기.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

<!-- 기존에 사용하던 리졸버 주석처리 -->
<!--
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
-->

<!-- thymeleaf View 설정 -->
<beans:bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".html" />
<beans:property name="templateMode" value="HTML" />
<beans:property name="characterEncoding" value="UTF-8" />
<beans:property name="cacheable" value="false" />
</beans:bean>


<!-- thymeleaf laytout을 쓰기위해 3rd Party 추가 -->
<beans:bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<beans:property name="templateResolver" ref="templateResolver" />
<beans:property name="additionalDialects">
<beans:set>
<beans:bean class="nz.net.ultraq.thymeleaf.LayoutDialect" />
</beans:set>
</beans:property>
</beans:bean>


<!-- thymeleaf laytout ViewResolver 설정 -->
<beans:bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<beans:property name="templateEngine" ref="templateEngine" />
<beans:property name="characterEncoding" value="UTF-8" />
<beans:property name="order" value="1" />
</beans:bean>

3.템플릿 레이아웃관련 html 파일을 관리할 폴더를 만들자.

WEB-INF/views/fragments 폴더를 생성하자. 이 폴더는 레이아웃 템플릿에서 재사용할 조각 html파일이 들어간다. WEB-INF/views/layout 폴더를 생성하자. 이 폴더는 레이아웃 템플릿 html파일이 들어간다.

4.WEB-INF/views/layout에 default.html 파일을 생성하자.

이 파일은 레이아웃의 템플릿, 즉 틀이 되는 html파일이다. 자세한 설명은 주석을 보길바란다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="ko"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">

<head>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MokaBoard</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" th:href="@{/resources/bower_components/bootstrap/dist/css/bootstrap.min.css}">
<!-- Font Awesome -->
<link rel="stylesheet" th:href="@{/resources/bower_components/font-awesome/css/font-awesome.min.css}">
<!-- Ionicons -->
<link rel="stylesheet" th:href="@{/resources/bower_components/Ionicons/css/ionicons.min.css}">
<!-- DataTables -->
<link rel="stylesheet" th:href="@{/resources/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css}">
<!-- Theme style -->
<link rel="stylesheet" th:href="@{/resources/dist/css/AdminLTE.min.css}">
<!-- AdminLTE Skins. We have chosen the skin-blue for this starter
page. However, you can choose any other skin. Make sure you
apply the skin class to the body tag so the changes take effect. -->
<!-- <link rel="stylesheet" th:href="@{/resources/dist/css/skins/skin-blue.min.css}"> -->
<link rel="stylesheet" th:href="@{/resources/dist/css/skins/_all-skins.min.css}">


<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

<!-- Google Font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">


<!-- jQuery 3 -->
<script th:src="@{/resources/bower_components/jquery/dist/jquery.min.js}"></script>
<!-- Bootstrap 3.3.7 -->
<script th:src="@{/resources/bower_components/bootstrap/dist/js/bootstrap.min.js}"></script>
<!-- DataTables -->
<script th:src="@{/resources/bower_components/datatables.net/js/jquery.dataTables.min.js}"></script>
<script th:src="@{/resources/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js}"></script>
<!-- SlimScroll -->
<script th:src="@{/resources/bower_components/jquery-slimscroll/jquery.slimscroll.min.js}"></script>
<!-- FastClick -->
<script th:src="@{/resources/bower_components/fastclick/lib/fastclick.js}"></script>
<!-- AdminLTE App -->
<script th:src="@{/resources/dist/js/adminlte.min.js}"></script>
<!-- AdminLTE for demo purposes -->
<script th:src="@{/resources/dist/js/demo.js}"></script>

</head>

<!-- body 태그 내부의 header 를 담당하는 부분이다. -->
<header th:replace="fragments/header :: headerFragment"></header>

<!-- 아래 div가 컨트롤러에 의해 교체되는 content 영역이다. -->
<div layout:fragment="content"></div>

<!-- body 태그 내부의 footer 를 담당하는 부분이다. -->
<footer th:replace="fragments/footer :: footerFragment"></footer>

</html>

5.header.html, footer.html 파일을 만들다.

WEB-INF/views/fragments 폴더에 header.html, footer.html을 생성하자. header.html과 footer.html은 별 내용은 없다. header.html 파일은 아래와 같다.

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<header class="main-header" th:fragment="headerFragment">
<!-- 헤더 영역, 원하는 것을 넣으면 된다. -->
헤더 영역
</header>

</html>

footer.html 파일은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<!-- Main Footer -->
<footer class="main-footer" th:fragment="footerFragment">
<!-- footer 영역, 원하는 것을 넣으면 된다. -->
푸터 영역
</footer>

</html>

6.content영역에 들어갈 home.html 파일을 만들자.

WEB-INF/views에 home.html 파일을 만들자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
data-layout-decorate="~{layout/default}"
>

<head>
<script th:inline="javascript">
/*<![CDATA[*/

alert("여긴 컨텐츠 영역 스크립트!");

/*]]>*/
</script>
</head>

<div layout:fragment="content">

컨텐츠 영역

</div>

</html>

위 파일에서 중요한 부분을 찝어 보겠다. 일단 content가 될 html파일의 html태그를 보면 아래와 같다.

1
2
3
4
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
data-layout-decorate="~{layout/default}"
>

html 태그 내부에 템플릿 레이아웃을 결정하는 구문은 아래다.

1
data-layout-decorate="~{layout/default}"

thymeleaf-layout-dialect 1버전과는 다른 형식이다. layout/default는 해당 컨텐츠 html이 컨트롤러에 의해 호출될 때 가져오는 템플릿 레이아웃 html 파일이다.

콘텐츠.html파일 자체에서 템플릿레이아웃.html파일을 결정된다.

그리고 home.html의 아래부분이

1
2
3
4
5
6

<div layout:fragment="content">

컨텐츠 영역

</div>

default.html 의 아래를 치환하게 된다.

1
<div layout:fragment="content"></div>

또 중요한 부분이 있다. 컨텐츠.html의 head 태그는 자동으로 레이아웃.html의 head 에 추가된다. thymelef-layout-dialect2 에서는 1버전과 달리, head를 fragment로 관리할 필요 없다. 라이브러리에서 자동으로 head를 관리해 준다.

즉 아래처럼 레이아웃.html 의 head태그가 있다.

1
2
3
4
5

<head>
<title>레이아웃 페이지의 타이틀</title>
<script src="제이쿼리"></script>
</head>

그리고 레이아웃에 컨텐츠 영역에 들어갈 컨텐츠.hmlt의 head가 아래와 같다면

1
2
3
4
<head>
<title>컨텐츠 페이지 타이틀</title>
<script src="뷰JS"></script>
</head>

결과적으로 렌더링된 최종 html의 head태그 내부는 다음과 같다.

1
2
3
4
5
<head>
<title>컨텐츠 페이지 타이틀</title>
<script src="제이쿼리"></script>
<script src="뷰JS"></script>
</head>

즉 중복된 태그는 컨텐츠.html의 것으로 따라가며, 없는 내용은 추가된다. thymelef-layout-dialect버전이 2로 올라가면서 생긴 변화이다.

7.controller 부분

스프링의 컨트롤러는 jsp를 호출하는 것 처럼 home을 호출하면 된다. 앞서 설정한 servlet-context.xml에 설정으로 view/home.html을 리졸브한다.

1
2
3
4
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Locale locale, Model model) throws Exception {
return "/home";
}

내 폴더 트리에는 파일이 위 내용보다 조금더 많지만, 신경쓸거 없다. 그냥 fragments가 조금더 많을 뿐이다.

내 실제 default.html은 참고로 보여준다면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<!DOCTYPE html>
<html th:lang = "ko"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MokaBoard</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" th:href="@{/resources/bower_components/bootstrap/dist/css/bootstrap.min.css}">
<!-- Font Awesome -->
<link rel="stylesheet" th:href="@{/resources/bower_components/font-awesome/css/font-awesome.min.css}">
<!-- Ionicons -->
<link rel="stylesheet" th:href="@{/resources/bower_components/Ionicons/css/ionicons.min.css}">
<!-- DataTables -->
<link rel="stylesheet" th:href="@{/resources/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css}">
<!-- Theme style -->
<link rel="stylesheet" th:href="@{/resources/dist/css/AdminLTE.min.css}">
<!-- AdminLTE Skins. We have chosen the skin-blue for this starter
page. However, you can choose any other skin. Make sure you
apply the skin class to the body tag so the changes take effect. -->
<!-- <link rel="stylesheet" th:href="@{/resources/dist/css/skins/skin-blue.min.css}"> -->
<link rel="stylesheet" th:href="@{/resources/dist/css/skins/_all-skins.min.css}">


<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

<!-- Google Font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">


<!-- jQuery 3 -->
<script th:src="@{/resources/bower_components/jquery/dist/jquery.min.js}"></script>
<!-- Bootstrap 3.3.7 -->
<script th:src="@{/resources/bower_components/bootstrap/dist/js/bootstrap.min.js}"></script>
<!-- DataTables -->
<script th:src="@{/resources/bower_components/datatables.net/js/jquery.dataTables.min.js}"></script>
<script th:src="@{/resources/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js}"></script>
<!-- SlimScroll -->
<script th:src="@{/resources/bower_components/jquery-slimscroll/jquery.slimscroll.min.js}"></script>
<!-- FastClick -->
<script th:src="@{/resources/bower_components/fastclick/lib/fastclick.js}"></script>
<!-- AdminLTE App -->
<script th:src="@{/resources/dist/js/adminlte.min.js}"></script>
<!-- AdminLTE for demo purposes -->
<script th:src="@{/resources/dist/js/demo.js}"></script>

<!--
BODY TAG OPTIONS:
=================
Apply one or more of the following classes to get the
desired effect
|---------------------------------------------------------|
| SKINS | skin-blue |
| | skin-black |
| | skin-purple |
| | skin-yellow |
| | skin-red |
| | skin-green |
|---------------------------------------------------------|
|LAYOUT OPTIONS | fixed |
| | layout-boxed |
| | layout-top-nav |
| | sidebar-collapse |
| | sidebar-mini |
|---------------------------------------------------------|
-->
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">

<header th:replace="fragments/header :: headerFragment"></header>

<aside th:replace="samples/fragments/leftAside :: leftAsideFragment"></aside>

<div layout:fragment="content"></div> <!-- 실제 콘텐츠 -->

<footer th:replace="fragments/footer:: footerFragment"></footer>

<div th:replace="fragments/controlDiv :: controlDivFragment"></div> <!-- 모바일 버전에서 하단과 우측에 나오는 사이즈 조정바 -->

</div>
<!-- ./wrapper -->


<!-- Optionally, you can add Slimscroll and FastClick plugins.
Both of these plugins are recommended to enhance the
user experience. -->
</body>
</html>

Related Posts

"spring4 thymeleaf2 버전 설정"