Thymeleaf 란 :
서버사이드 템플릿 엔진으로, 자바기반 웹 애플리케이션에서 HTML, XML, JavaScript, CSS를 생성하는 데 사용된다. (jsp의 템플릿 엔진 jstl과 비슷한 개념. Model에 담긴 데이터를 뷰로 전달받아 타임리프 템플릿에서 렌더링함.)
• Thymeleaf layout은 웹 애플리케이션의 일관된 레이아웃을 유지하기 위해 템플릿을 모듈화 하고 재사용할 수 있게 하며, 레이아웃 템플릿을 통해 공통된 구조를 정의하고, 각 페이지별로 변경되는 부분만 별도로 관리할 수 있다.
레이아웃의 기본 파일 위치는 : src/main/resources/templates 에 위치한다.
Thymeleaf의 특징:
- EL 표기법 사용 - ${}
<p> 안녕하세요, ${name}님! </p> - URL 표현 - @{}
<a href="@{/home}"> Home </a> - 속성 수정자 - th:attr
<a th:href="@{/user/{id}(id=${user.id})}"> 사용자 프로필 </a> - for each처럼 사용하는 반복문 - th:each 구문으로 반복 처리 한다.
-예제코드-
<ul>
<li th:each="user : ${users}">
<span th:text="${user.name}">이름</span>
</li>
</ul>
th:each의 범위는 th:each를 선언한 li 태그이며,
user.name이라고 지정했는데, user는 th:each에서 사용할 변수를 정의한 것이고,
th:text="${user.name}"은 foreach구문 안쪽에 코드라고 생각할 수 있다. -> ${users}의 name으로 지정한 속성값을 가리킴
- th:if와 th:unless 구문으로 조건식을 처리.
-예제코드-
<div th:if="${user != null}">
<p>사용자가 로그인했습니다.</p>
</div>
<div th:unless="${user != null}">
<p>사용자가 로그인하지 않았습니다.</p>
</div>
th:if는 조건문이며 조건에 해당하면 렌더링을 하고,
th:unless는 조건에 해당하지 않으면 렌더링을 한다.
따라서 위 코드로 보면,
th:if="${user!= null}" 유저가 null이 아니면 사용자가 로그인했습니다.
th:unless="${user!= null}" 유저가 null이면 사용자가 로그인하지 않았습니다.
라는 p 태그를 보여준다.
- th:fragment: Thymeleaf에서 템플릿의 일부분을 정의하고 재사용할 수 있도록 하는 속성
-예제코드-
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="header">
<header>
<h1>My Application Header</h1>
</header>
</div>
<div th:fragment="footer">
<footer>
<p>My Application Footer</p>
</footer>
</div>
</body>
</html>
위 코드는 th:fragment라는 속성을 이용해 div 태그의 내용을 각각 header와 footer라는 이름의 조각으로 만들겠다.
라는 의미이며, 각각 header와 footer로 재사용할 수 있다.
- th:fragment로 만든 프레그먼트를 포함할 때는 th:insert, th:replace, th:include를 사용한다.
- th:insert: 프래그먼트를 현재 요소 내에 삽입.
- th:replace: 현재 요소를 프래그먼트로 교체.
- th:include: 프래그먼트의 내용을 현재 요소 내에 포함.
기본적으로 태그 내부에 작성하지만, jstl처럼 태그 밖에 작성하고 싶다면 th:block을 이용해 작성해 준다.
Thymeleaf를 사용하기 위해서는 다음 의존성이 필요하다.

html 문서에서 타임리프와 레이아웃을 사용하기 위해 다음 내용을 포함시킨다.
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
위에서 fragment 속성을 활용해 보자.
우선,
헤더로 공통으로 사용할 헤더 태그를 다음과 같이 header라는 이름으로 조각을 만들 수 있음.
다음 코드는 fragment 폴더의 header.html이다.
<html lang="en"
xmlns:th="http://www.thymeleaf.org">
<body>
<header th:fragment="header" class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
<button type="button" class="btn btn-outline-light btn-lg text-secondary" onclick="location.href='/'">홈</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="board/free-list.html">자유게시판</a>
</li>
<li class="nav-item">
<a class="nav-link" href="board/notice-list.html">공지사항</a>
</li>
<li class="nav-item">
<a class="nav-link" href="user/login.html">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="user/join.html">회원가입</a>
</li>
</ul>
</div>
</nav>
</header>
조각낸 header를 불러올 때, 다음과 같이 ~{조각 경로 :: 조각 명}으로 지정해 주어야 됨
(물론 th를 사용하려면 다음과 같이 타임리프 namespace를 추가해야 된다.
<html lang="en"
xmlns:th="http://www.thymeleaf.org">)
다음은 layout1.html 코드이다.
<body>
<div class="wrapper">
<header th:replace="~{/fragments/header :: header}"></header>
<main layout:fragment="content"></main>
<footer th:replace="~{/fragments/footer :: footer}"></footer>
</div>
</body>
코드를 간략하게 설명해 보자면,
<header 태그를 th:replace = 대체해 주겠다.
각각의 경로에서 header와 footer라는 조각을 가져옴.
layout:fragment :
index.html에서 다음 레이아웃을 이용해서 꾸며주겠다고 선언한 느낌임→
<html…
layout:decorate="~{/layouts/layout1}">
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{/layouts/layout1}">
<body>
<main layout:fragment="content" class="container d-flex justify-content-center my-5 py-5">
<h2 class="my-5 py-5">게시판 홈</h2>
</main>
</body>
</html>
index.html과 layout1.html을 보면 각각
<main layout:fragment="content" class="container d-flex justify-content-center my-5 py-5">
<main layout:fragment="content"></main>
에서 서로 매핑된다.
- th:insert는 현재 태그를 유지하며 템플릿 조각을 가져옴
- th:replace는 현재 태그 자리에 템플릿 조각으로 대체함
+
ModelAndView 객체로 값 넘겨주어 타임리프로 보여주기
package com.bit.springboard.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/member")
public class MemberController {
// (@ResponseBody 가 포함 안된)@Controller 를 사용해서 Controller bean 객체를 생성했을 때는
// String 값을 리턴했을 때 ViewResolver 가 동작했지만,
// (@ResponseBody 가 포함된)@RestController 에서 String 값을 리턴하면,
// ViewResolver 가 동작하지 않고 String 값을 그대로 리턴한다.
// @RestController 에서 화면을 리턴하기 위해서는 ModelAndView 객체를 리턴해 주어야됨.
@GetMapping("/join")
public String joinView(){
return "member/join";
} // @Controller 에서는 이와같이 사용했지만, @RestController 에서 사용하면 스트링값 그대로 화면에 나온다.
}
@Controller에서는 이와 같이 사용했지만, @RestController에서 사용하면 스트링값 그대로 화면에 나온다.

@RestController에서는 다음과 같이 화면을 이동함
@GetMapping("/join")
public ModelAndView joinView(){
ModelAndView mav = new ModelAndView();
mav.addObject("name", "model객체의 key값으로 가져온 Value");
mav.setViewName("member/join"); // 찾아갈 화면의 이름 지정해주기
// mav.setViewName("templates/member/join.html");
// application.properties 에서 설정한 prefix와 suffix 가 붙어 위와 같이 동작하는거다.
return mav;
}


'Spring > Boot' 카테고리의 다른 글
| 스프링부트 응답 메시지 보내기 (0) | 2024.09.01 |
|---|---|
| 스프링부트 ResponseEntity<T> (0) | 2024.08.17 |
| 스프링부트 RESTful Api (0) | 2024.08.15 |