참고 링크 : https://wikidocs.net/book/7601
오라클DB와 intelliJ로 작업하였습니다
HTML에 익숙한 독자라면 눈치겠지만, 지금까지 작성한 질문 목록, 질문 상세 템플릿은 표준 HTML 구조가 아니다. 어떤 웹 브라우저를 사용하더라도 웹 페이지가 동일하게 보이고 정상적으로 작동 하게 하려면 반드시 웹 표준을 지키는 HTML 문서를 작성해야 한다.
표준 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 엘리먼트 등이 포함되어야 한다.
태그와 엘리먼트
<table> (... 생략 ...) </table> <!-- table 엘리먼트 -->
위에서 <table>은 table 태그이고 <table> ~ </table> 처럼 table 태그로 시작해서 table 태그로 닫힌 구간(Block)은 table 엘리먼트이다.
템플릿 상속
앞에서 작성한 질문 목록, 질문 상세 템플릿을 표준 HTML 구조가 되도록 수정해 보자. 그런데 템플릿 파일들을 모두 표준 HTML 구조로 변경하면 body 엘리먼트 바깥 부분(head 엘리먼트 등)은 모두 같은 내용으로 중복된다.
그러면 CSS 파일 이름이 변경되거나 새로운 CSS 파일이 추가될 때마다 모든 템플릿 파일을 일일이 수정해야 한다.
타임리프는 이런 중복의 불편함을 해소하기 위해 템플릿 상속 기능을 제공한다.
템플릿 상속은 기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법이다.
템플릿 상속에 대해서 자세히 알아보자.
layout.html
먼저 표준 HTML 구조의 기본 틀이 되는 layout.html 템플릿을 다음처럼 작성하자.
/src/main/resources/templates/layout.html
<!DOCTYPE html>
<html lang="ko" xmlns:layout="">
<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" href="style.css" th:href="@{/bootstrap.min.css}">
<!--sbb CSS-->
<link rel="stylesheet" href="style.css">
<title>Title</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 템플릿을 다음과 같이 변경하자.
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layout}">
<!--layout:decorate 속성 : 템플릿의 레이아웃(부모템플릿)으로 사용할 템플릿을 설정-->
<div layout:fragment="content" class="container my-3"> <!--<th:block layout:fragment="content"></th:block>-->
<table class="table">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question, loop : ${questionList}">
<td th:text="${loop.count}"></td><!--테이블항목에 번호추가-->
<td>
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
</td>
<td th:text="${#dates.format(question.createDate, 'yyyy-MM-dd')}"></td> <!--날짜객체 날짜포맷에맞게 변환-->
</tr>
</tbody>
</table>
<a th:href="@{/question/create}" class="btn btn-primary">질문 등록하기</a> <!--질문버튼추가 부트스트랩으로 버튼디자인!-->
</div>
</html>
부트스트랩 스타일 링크는 삭제했다.
왜냐하면 부모 템플릿인 layout.html 템플릿에서 이미 부트스트랩 스타일을 링크하기 때문이다.
layout.html 템플릿을 상속하기 위해 <html layout:decorate="~{layout}"> 처럼 사용했다.
타임리프의 layout:decorate 속성은 템플릿의 레이아웃(부모 템플릿)으로 사용할 템플릿을 설정한다.
속성의 값인 ~{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_list.html은 layout.html 템플릿을 상속받아 표준 HTML문서가 된다.
question_detail.html
question_detail.html도 마찬가지 방법으로 수정하자.
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
<!--질문-->
<h2 class="border-bottom py-2" th:text="${question.subject}"></h2>
<div class="card my-3">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${question.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#dates.format(question.createDate, 'yyyy-MM-dd')}"></div>
</div>
</div>
</div>
</div>
<!--답변의 갯수표시-->
<h5 class="border-bottom my-3 py-2"
th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<!--#list.size(이터러블객체)-타임리프가 제공하는 유틸리티로 객체의 길이를 반환-->
<!--답변 반복 시작-->
<div class="card my-3" th:each="answer : ${question.answerList}">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${answer.content}"></div>
<div class="d-flex justify-content-end">
<div class="badge bg-light text-dark p-2 text-start">
<div th:text="${#dates.format(answer.createDate, 'yyyy-MM-dd')}"></div>
</div>
</div>
</div>
</div>
<!--답변 반복 끝-->
<!--답변 작성-->
<form th:action="@{|/answer/create/${question.id}|}" method="post" class="my-3">
<textarea name="content" id="content" rows="10" class="form-control"></textarea>
<input type="submit" value="답변등록" class="btn btn-primary my-2">
</form>
</div>
</html>
yyyy-MM-dd HH:mm 년-월-일 시:분 - 표시
question_list.html 템플릿과 동일한 방법으로 layout.html 템플릿을 상속했다.
템플릿 상속을 적용한 후 질문 목록, 질문 상세를 조회해 보자.
화면에 보여지는 것은 동일하지만 표준 HTML 구조로 변경된 것을 확인할 수 있다.
'Follow Work > SpringbootBoard' 카테고리의 다른 글
[StringBoot] 공통템플릿 (13) (0) | 2022.08.15 |
---|---|
[StringBoot] 질문 등록과 폼 (12) (0) | 2022.08.15 |
[StringBoot] 부트스트랩 (10) (0) | 2022.08.12 |
[StringBoot] 스태틱 디렉터리와 스타일시트 (9) (0) | 2022.08.12 |
[StringBoot] 답변등록 (8) (0) | 2022.08.12 |