layout:decorate
표준 HTML 구조 살펴보기
다음 예로 표준 HTML 문서의 구조를 살펴보자.
[표준 HTML 구조의 예]
<!doctype html>
<html lang="ko">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
<!-- sbb CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
<title>Hello, sbb!</title>
</head>
<body>
(... 생략 ...)
</body>
</html>
곧이어 작성할 템플릿의 일부이다. 여기서는 눈으로만 살펴보자.
표준 HTML 문서의 구조는 앞의 예처럼 html, head, body 요소가 있어야 하며, CSS 파일은 <head> 태그 안에 링크되어야 한다. 또한 <head> 태그 안에는 meta, title 요소 등이 포함되어야 한다.
템플릿 상속하기
앞에서 작성한 질문 목록과 질문 상세 템플릿이 표준 HTML 구조로 구성되도록 수정해 보자. 그런데 이 템플릿 파일들을 모두 표준 HTML 구조로 변경하면 body 요소를 제외한 바깥 부분은 모두 같은 내용으로 중복된다.
그렇게 되면 CSS 파일 이름이 변경되거나 새로운 CSS 파일을 추가할 때마다 모든 템플릿 파일을 일일이 수정해야 한다. 타임리프는 이런 중복의 불편함을 해소하기 위해 템플릿 상속 기능을 제공한다. 템플릿 상속은 기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법이다. 템플릿 상속에 대해서 자세히 알아보자.
layout.html로 기본 틀 만들기
템플릿을 상속하려면 각 템플릿 파일에서 반복되는 내용을 담아 기본 틀이 되는 템플릿을 만들어야 한다. 그러기 위해 templates에 layout.html 파일을 만들어 다음 내용을 작성해 보자.
[파일명:/templates/layout.html]
<!doctype html>
<html lang="ko">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
<!-- sbb CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
<title>Hello, sbb!</title>
</head>
<body>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
</body>
</html>
layout.html은 모든 템플릿이 상속해야 하는 템플릿으로, 표준 HTML 문서 구조로 정리된 기본 틀이 된다. body 요소 안의 <th:block layout:fragment="content"></th:block>은 layout.html을 상속한 템플릿에서 개별적으로 구현해야 하는 영역이 된다. 즉, layout.html 템플릿을 상속하면 <th:block layout:fragment="content"></th:block> 영역만 수정해도 표준 HTML 문서로 작성된다.
question_list.html에 템플릿 상속하기
question_list.html 템플릿을 다음과 같이 변경하여 layout.html을 상속해 보자.
[파일명:/templates/question_list.html]
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
<table class="table">
(... 생략 ...)
</table>
</div>
</html>
layout.html 템플릿을 상속하려고 <html layout:decorate="~{layout}">을 사용했다. 타임리프의 layout:decorate 속성은 템플릿의 레이아웃(부모 템플릿, 여기서는 layout.html)으로 사용할 템플릿을 설정한다. 속성값인 ~{layout}이 바로 layout.html 파일을 의미한다.
부모 템플릿인 layout.html에는 다음과 같은 내용이 있었다.
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
부모 템플릿에 작성된 이 부분을 자식 템플릿의 내용으로 적용될 수 있도록 다음과 같이 사용했다.
<div layout:fragment="content" class="container my-3">
(... 생략 ...)
</div>
이렇게 하면 부모 템플릿의 th:block 요소의 내용이 자식 템플릿의 div 요소의 내용으로 교체된다.
question_detail.html에 템플릿 상속하기
question_deatail.html도 마찬가지 방법으로 layout.html을 상속해 보자.
[파일명: /templates/question_detail.html]
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
<h2 class="border-bottom py-2" th:text="${question.subject}"></h2>
(... 생략 ...)
</form>
</div>
</html>
question_list.html 템플릿과 동일한 방법으로 layout.html 템플릿을 상속하려고 <html layout:decorate="~{layout}">을 사용했다.
템플릿을 상속한 후, 질문 목록 또는 질문 상세 페이지를 확인해 보자. 화면에 보여 지는 것은 동일하지만 표준 HTML 구조로 변경되었다. 크롬 브라우저에서 마우스 오른쪽 버튼을 클릭하고 [페이지 소스 보기]를 클릭하면 HTML 코드를 확인할 수 있다.


부트스트랩(Bootstrap)을 적용하여 style.css의 내용은 필요 없어졌으므로 기존에 작성한 내용을 모두 삭제하자. 부트스트랩으로 표현할 수 없는 스타일을 작성하기 위해 style.css 파일 자체를 삭제하지는 말고 내용만 삭제