|
Thymeleaf는 View Template Engine으로 JSP, Freemarked와 같이 서버에서 클라이언트에게 응답할 브라우저 화면을 만들어주는 역할을 한다. Thymeleaf는 웹 뿐만 아니라 다른 환경을 위한 최신의 서버-사이드 자바 Template Engine이며, HTML, CSS, XM, JS 및 Text까지 포함한다. Thymeleaf의 목표는 유지관리가 쉬운 템플릿 생성 방법을 제공하는 것이며, 실제로 템플릿에 영향을 주지 않는 (HTML의 구조 유지)방식을 사용한다. 즉, Natural Templates 개념을 기반으로 한다.
Thymeleaf의 주 목표는 템플릿을 만들 때 유지관리가 쉽도록 하는 것이다. 이를 위해 디자인 프로토타입으로 사용되는 템플릿에 영향을 미치지 않는 방식인 Natural Templates을 기반으로 한다. Natural Templates은 기존 HTML 코드와 구조를 변경하지 않고 덧붙이는 방식이다.
Thymeleaf의 장점
- 코드를 변경하지 않기 때문에 디자인 팀과 개발 팀 간의 협업이 쉽다.
- JSP와 달리 Servlet Code로 변환되지 않기 때문에 비즈니스 로직과 분리되어 View에 집중할 수 있다.
- 서버상에서 동작하지 않기 때문에 서버없이 화면을 확인할 수 있다. 그래서 더미 데이터를 넣고 화면 디자인 및 테스트에 용이하다.
* Thymeleaf에서 자주 사용하는 문법. 대부분의 html 속성을 th:예약어 로 변경할 수 있다.
<html lang="en" xmlns:th="http://www.thymeleaf.org">
: 타임리프의 th 속성을 사용하기 위해 선언된 네임스페이스이다.
th:text
: 타임리프는 ${} 표현식을 사용하여 자바 컨트롤러에서 전달받은 데이터에 접근할 수 있다.
: text 속성은 일반적인 텍스트 형식으로 데이터를 화면에 출력한다.
: <div th:text="${data}"></div>
th:if, th:unless
: th:if는 if 문과 동일하고, th:unless는 else 문과 같다고 볼 수 있다.
: <span th:if="${userNum} == 1"></span>
<span th:unless="${userNum} == 2"></span>
th:switch, th:case
: <th:block th:switch="${num}">
<span th:case="1">처리1</span>
<span th:case="2">처리2</span>
</th:block>
: switch case문으로 제어할 태그를 th:block으로 설정하고 그 안에 코드를 작성한다.
: num라는 변수의 값이 1이거나 2일때 동작한다.
th:each
: 컬렉션 객체를 반복처리 한다. 자바의 forEach와 유사한 기능이다.
: <li th:each="article : ${articleList}"></li>
th:fragment
: <head>태그에 해당 속성을 사용해서 fragment의 이름을 지정한다.
: fragment는 다른 HTML에서 include 또는 replace 속성을 사용해서 적용할 수 있다.
: <footer th:fragment="footer"><p>바닥글</p></footer>
th:block
: 영역을 지정해서 명령을 주는 기능이다. 해당 기능은 동적인 처리가 필요할 때 사용된다.
: <th:block th:fragment="footer">
th:replace
: JSP의 <include> 태그와 유사한 속성이다.
: th:fragment을 통해 설정한 이름을 찾아 해당 코드로 치환한다.
: <div th:replace="~{/common/footer :: footer}"></div>
th:href
: <a> 태그의 href 속성과 동일한 기능을 수행한다.
: 웹 애플리케이션을 구분하는 Context Path를 포함한다.
: <a th:href="@{/boardlist?id=${id}}">go</a>
: <a th:href="@{/boardlist(id= ${id})}">go</a>
th:action
: <form> 태그 사용시 해당 경로로 요청을 보낼 때 사용한다.
th:object
: 컬렉션 객체 데이터를 th:object에 설정해둔 객체로 받아들인다.
: 즉, 컨트롤러와 뷰(화면) 사이의 DTO클래스의 객체이다.
th:field
: 위의 th:object 속성을 이용하면, th:field를 이용해서 HTML 태그에 멤버 변수를 매핑 할 수 있다.
: th:field을 이용한 사용자 입력 필드(input, textarea 등)는 id, name, value 속성 값이 자동으로 매핑된다.
: 그러므로 각 속성을 따로 지정할 필요가 없다. th:field는 ${}표현식이 아닌, *{}표현식을 사용한다.
: th:object와 th:field는 컨트롤러에서 특정 클래스의 객체를 전달 받은 경우에만 사용 가능하다.
th:field
: 위의 th:object 속성을 이용하면, th:field를 이용해서 HTML 태그에 멤버 변수를 매핑 할 수 있다.
: th:field을 이용한 사용자 입력 필드(input, textarea 등)는 id, name, value 속성 값이 자동으로 매핑된다.
: 그렇기에, 각 속성을 따로 지정할 필요가 없다. th:field는 ${}표현식이 아닌, *{}표현식을 사용한다.
: th:object와 th:field는 컨트롤러에서 특정 클래스의 객체를 전달 받은 경우에만 사용 가능하다.
th:checked
: checkbox의 경우, th:checked 속성을 이용해서 조건이 "true"에 해당하면, checked 속성을 적용한다.
th:inline="javascript"
: <script> 태그에 th:inline 속성을 javascript로 지정한 후 자바스크립트를 사용할 수 있다.
th:with="${}"
: 변수형태의 값을 재정의하는 속성이다. 즉, th:with를 이용하여 새로운 변수값을 생성할 수 있다.
: <div th:with=”userId=${number}” th:text=”${usesrId}”>
th:value="${}"
: input의 value에 값을 삽입할 때 사용한다. 여러 개의 값을 넣을 땐 + 기호를 사용한다.
: <input type="text" id="userId" th:value="${userId} + '의 이름 ${userName}"/>
#numbers.sequest(start, end, step)
: 타임리프는 #numbers라는 숫자 포맷 메소드를 지원한다. #numbers에는 다양한 메소드가 있는데 가장 많이 사용하는 것이 #numbers.sequece이다. #numbers.sequece 메소드는 start, end, step을 이용하여 원하는 범위에 대해 시퀀스를 생성해준다.
참고 : Thymeleaf에서 a 태그를 작성할 때는 th:href="@{ }" 을 이용하여 작성한다.
🏃♀️ 특정 url로 이동 : <a th:href="@{https://localhost/detail}">상세보기</a>
🏃♀️ 현재 서버 내에서 이동 : <a th:href="@{/board/list}">게시글 목록</a>
🏃♀️ 파라미터를 넘길 시 : <a th:href="@{/board/view(id = ${board.id})}">상세보기</a>
🏃♀️ 파라미터를 여러 개 넘길 시 : <a th:href="@{/board/view(id = ${board.id}, writer = ${board.writer}})}">상세보기</a>
🏃♀️ PathVariable 사용 시 : <a th:href="@{/board/view/{id}(id = ${board.id})}">상세보기</a>
참고 : Thymeleaf 문법 중 변수 및 메세지 표현 방식의 이해
표현 | 설 명 | 예 제 |
@{ ... } | URL 링크 표현식 | th:href="@{/css/bootstrap.min.css}" th:href="@{/{itemId}/edit(itemId=${item.id})}" |
| ... | | literal 대체 | th:text="|Hello ${user.name}!|" (= th:text="'Hello '+${user.name}+'!'" |
${ ... } | 변수 | th:text=${user.name} |
*{ ... } | 선택 변수 | <tr th:object="${items}"> <td th:text="*{price}">5000</td> </tr> |
#{ ... } | 메시지. properties 같은 외부 자원에서 코드에 해당하는 문자열 get. | th:text="#{member.name}" |
참고 : Thymeleaf로 Javascript 적용 예 ~~~~~~~~~~~~~~
--- Controller 파일 내용
@GetMapping("/hello")
public String abc(Model model) {
model.addAttribute("msg", "자바스크립트 적용");
return "/kbs";
}
--- kbs.html 파일 내용
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<th:block th:layout:fragment="script">
<script th:inline="javascript">
let aa = [[${msg}]];
alert(aa);
</script>
</th:block>
</head>
참고 : Thymeleaf에서 javascript 함수에 값 전달하는 방법 예 ~~~~~~~~~~~~~~~~
<a th:xxxxonclick="|xxxxjavascript:page('${pageNum}')|">함수 호출</a> 이렇게 th:xxxxonclick과 함께 작성하면 된다.
또는
<a th:href="|xxxxjavascript:func('${pageNum}')|">함수 호출</a> 이렇게 a tag의 고유 클릭 이벤트를 사용해도 된다.
func는 javascript 함수의 이름이다.
<script type="text/javascript">
function func(no) {
alert(no);
let pageNo = no;
location.href="http://localhost:80/list?page="+pageNo;
}
</script>
참고 : Thymeleaf에서 Javascript를 사용하는 경우에 반복문 기술 방법
let articleArr = new Array();
let article = new Object();
/*[# th:each="article : ${articleList}"]*/
article.id = /*[[${article.id}]]*/;
article.title = /*[[${article.title}]]*/;
article.cont = /*[[${article.cont}]]*/;
articleArr.push(article);
/*[/]*/
참고 : Thymeleaf로 css 적용 예 ~~~~~~~~~~~~~~
<body>
어쩌구...
<!-- 사용자 CSS 추가 -->
<th:block th:layout:fragment="css">
<style>
#cont{ font-size: 24pt; }
</style>
</th:block>
<div id="cont">바디영역</div>
어쩌구...
</body>
참고 : 스타일시트 파일 작성 후 css 적용 예 ~~~~~
스타일시트 파일은 static 폴더에 저장한다. style.css의 내용은 다음과 같다.
파일명 위치 : /프로젝트명/src/main/resources/static/style.css
input[type=submit] { margin-top:10px; }
textarea { width:100%; }
---템플릿에 스타일 적용하기---
파일명: /sbb/src/main/resources/templates/list.html
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
<h1>스타일 적용하기</h1>
<form th:action="@{|/product/detail/${product.id}|}" method="post">
<input type="text" name="name" id="name">
<textarea name="content" id="content" rows="5" cols="100"></textarea>
<input type="submit" value="등록">
</form>
템플릿 상단에 style.css를 사용할수 있는 링크를 추가했다.
static 폴더가 리소스의 root이기 때문에 th:href="@{/style.css}"로 사용해야 한다.
참고 : Bootstrap으로 스타일 적용 예 ~~~~~~~~~~
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
<div class="container my-3">
<table class="table">
<thead class="table-dark">
<tr>
<th>번호</th><th>이름</th><th>등록일시</th>
</tr>
</thead>
<tbody>
<tr th:each="product, loop : ${productList}">
<td th:text="${loop.count}"></td>
<td>
<a th:href="@{|/product/detail/${product.id}|}" th:text="${product.subject}"></a>
</td>
<td th:text="${#temporals.format(product.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
날짜 및 시간를 보기 좋게 출력하기 위해 타임리프의 #temporals.format 을 사용할 수 있다.
형식 : #temporals.format(날짜객체, 날짜포맷) - 날짜객체가 포맷에 맞게 변환됨.
타임리프의 템플릿 상속에 대한 이해 ~~~~~~~~~~~
템플릿 레이아웃 : 여러 템플릿에서 같은 디자인 레이아웃을 적용하는 일반적으로 공통적인 레이아웃을 정의하고 공유하게 된다.
이 때 유효한 라이브러리로 타임리프 레이아웃 다이얼렉트가 있다.
Dependency 추가
- spring-boot-starter-thymeleaf : thymeleaf 뷰 템플릿 엔진 기본 라이브러리
- thymeleaft-layout-dialect : thymeleaf를 이용한 layout 라이브러리
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
...
하고 해당프로젝트에서 우측 버튼 클릭 후 메뉴에서 Gradle- Refresh Gradle Project를 선택한다.
예시 코드
HomeController.java
@Controller
public class HomeController {
@GetMapping("/page1")
public String page1() {
return "page1";
}
@GetMapping("/page2")
public String page2() {
return "page2";
}
}
Controller에 page1, page2를 매핑해 주었다. 이제 page1과 page2에 공통으로 들어갈 템플릿을 만들어 적용할 것이다.
layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>layout</title>
</head>
<body>
<h1>공통으로 들어가는 부분</h1>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
</body>
</html>
page1
<html layout:decorate="~{layout}">
<div layout:fragment="content">
<h1>page1</h1>
</div>
</html>
page2
<html layout:decorate="~{layout}">
<div layout:fragment="content">
<h1>page2</h1>
</div>
</html>
설명 : page1과 page2의 <html layout:decorate="~{layout}"> 부분에 공통 템플릿인 layout.html이 들어가고,
layout.html의 <th:block layout:fragment="content"></th:block>부분에 page1과 page2의 div엘리먼트가 삽입되어 하나의 페이지를 완성시키는 것이다.
참고 : session 읽기 예
java 파일에서
@PostMapping("login")
public String login(HttpSession session, @RequestParam("id")String id, @RequestParam("pwd")String pwd){
if(id.equals("kor") && pwd.equals("111")) {
session.setAttribute("idkey", id);
return "redirect:/list";
}else {
return "redirect:/err.html";
}
}
html 파일에서
<div th:if="${#session.getAttribute('idkey') == null}">
<a href="login.html">로그인 하세요</a>
</div>
Thymeleaf 사용 단편 예제
https://s-yeonjuu.tistory.com/6
https://nomoreft.tistory.com/64
Tutorial: Thymeleaf + Spring
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
* 스프링 부트와 JPA, Thymeleaf를 이용한 게시판 개발
https://developer-rooney.tistory.com/153?category=496529
https://github.com/hojunnnnn/board