[Thymeleaf] 템플릿 레이아웃 적용하기

레이아웃을 사용하는 이유?

레아아웃을 사용하는 이유는 만약 100개의 페이지에 똑같은 header와 footer가 들어갈 경우

각각의 HTML마다 똑같은 header와 footer를 추가하는 것은 상당히 비효율적이고 유지보수면에서도 좋지 않습니다.

그래서 반복되는 화면이 있어 HTML 코드를 줄일 때 레이아웃을 적용하면 상당히 효과적입니다.


템플릿 조각

템플릿 조각은 공통으로 적용할 부분을 조각조각으로 만들어 필요한 공통의 부분들에 가져다 쓰는 방식입니다.

<th:fragment> 가 있는 태그는 다른곳에 포함되는 코드 조각으로 코드 조각을 사용할 때는 조각 표현식 ~{...}를 사용합니다.

 

Controller
@Controller
@RequestMapping("/template")
public class TemplateController {

    @GetMapping("/fragment")
    public String template() {
        return "template/fragment/fragmentMain";
    }
}

 

footer.HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<body>
<footer th:fragment="copy">
  푸터 자리 입니다.
</footer>
<footer th:fragment="copyParam (param1, param2)">
  <p>파라미터 자리 입니다.</p>
  <p th:text="${param1}"></p>
  <p th:text="${param2}"></p>
</footer>
</body>

</html>

 

fragmentMain.HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<h1>부분 포함</h1>

<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>

<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>

<h2>부분 포함 단순 표현식</h2>
<div th:replace="template/fragment/footer :: copy"></div>

<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>

</body>
</html>

 

  • template/fragment/footer :: copy

template/fragment/footer.html 템플릿에 있는 <th:fragment="copy"> 라는 부분을 템플릿 조각으로 가져와서 사용한다는 의미입니다.

  • 부분 포함 insert

<th:insert> 를 사용하면 현재 태그( div ) 내부에 추가

 

  • 부분 포함 replace

<th:replace> 를 사용하면 현재 태그( div )를 대체

 

  • 부분 포함 단순 표현식

~{...} 를 사용하는 것이 원칙이지만 템플릿 조각을 사용하는 코드가 경로나 이름 정도만 있는 단순한 경우에는 이 부분을 생략가능합니다.

 

  • 파라미터 사용

파라미터를 전달해서 동적으로 조각을 렌더링 할 수도 있습니다.

 

 


템플릿 레이아웃

앞의 코드 조각을 조금 더 적극적으로 활용해 HTML 전체에 적용할 수도 있습니다.

레이아웃으로 사용할 HTML 파일의 <html> 속성에 th:fragment 속성을 추가합니다.

 

Controller
@Controller
@RequestMapping("/template")
public class TemplateController {

    @GetMapping("/layoutExtend")
    public String layoutExtend() {
        return "template/layoutExtend/layoutExtendMain";
    }
}

 

layoutFile.HTML
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
  <title th:replace="${title}">레이아웃 타이틀</title>
</head>
<body>

<h1>레이아웃 H1</h1>

<div th:replace="${content}">
  <p>레이아웃 컨텐츠</p>
</div>

<footer>
  레이아웃 푸터
</footer>

</body>
</html>

 

layoutExtendMain.HTML
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title},~{::content})}" xmlns:th="http://www.thymeleaf.org">
<head>
  <title>메인 페이지 타이틀</title>
</head>
<body>

<content>
  <p>메인 페이지 컨텐츠</p>
  <div>메인 페이지 포함 내용</div>
</content>

</body>
</html>

 

  • layout(~{::title},~{::content})

앞의 layout은 layoutFile 파일의 fragment 이름인 layout을 뜻합니다.

(~{::title},~{::content})은 이 layout의 <태그> 이름을 뜻합니다.

::title는 현재 페이지(layoutExtendMain)의 title 태그들을 전달

::content는 는 현재 페이지(layoutExtendMain)의 content태그들을 전달

 

layoutExtendMain.html 는 현재 페이지인데, 이 페이지 자체를 <th:replace> 를 사용해서 layoutFile으로 변경하였습니다.

결국 layoutFile.html 에 필요한 내용을 전달하면서 자체를 layoutFile.html 로 변경했습니다.