Spring 부트

23-02-13) 스프링부트 3강 - thymeleaf 템플릿 사용법 ( 타임리프 구문)

JadeStone 2023. 2. 13. 19:08

3강

1. thymeleaf템플릿 사용법

 

 

*스프링 부트의 기본 뷰.

*반드시 spring-boot-starter 모듈로 받아야지 별다른 설정없이 자동으로 연결이 됨.

*우리는 자동완성 기능없이 수동으로 할것. 

 

 

<타임 템플릿 사용>

# 파일 생성시 주의 !!

templates 폴더가 베이스 폴더가 됨. 
컨트롤러에서 경로 설정시 여기서부터 경로가 시작된다는 말.

따라서 templates 폴더 아래에다가 view 파일들을 만들어야함.

 

*컨트롤러

@Controller
@RequestMapping("/view") //templates가 베이스폴더가 됨.
public class ThymeleafController {
	
	@GetMapping("/ex01") //get방식
	public String ex01() {
		return "view/ex01";
	}

}

 

★★★ 어떤 속성이든간에 타임리프 문법을 사용할 거다 한다면

 속성 앞에 다 th: 를 붙여주면 된다. ★★★

 

* 타임리프구문을 사용할 떄는 명시적으로 상단에 네임스페이스를 넣어줌.

xmlns:사용명 을 명시함.  (해주는게 좋음)

(위에 tymeleaf.org 로 고쳐져야함)

* 빨간색 부분만 잘 암기해두기.

- a태그에 링크를 걸고 싶으면 @{} 에 경로를 적어주면 됨. 경로는 @이다.

 타임리프구문 없이 그냥 href 만 쓰면 정적인 경로, 주소값  그 자체로만 쓸 수 있음.

 타임리프구문을 사용하면 동적으로 값을 줄 수 있음.

-fragment expression 은 include와 같음.  frag링크에서는 ~{} 로 사용

- 문자는 '' (홑따옴표) 

- 텍스트 결합 은  + .

 

* 타임리프 3.x 에서는 html 태그없이 출력이 가능한데

[[  ${'출력문'} ]] 이렇게 출력이 가능.  

*th:with 변수명 설정.

-th:with="var1=${user.name}"

*else 구문은 unless 로 쓴다.

 이 때 unless 에도 if문과 동일한 조건을 적어줘야 함.

	<!-- if else문 : unless는 동일한 조건을 적습니다-->
	<div th:with="a = 10">
		<span th:if="${a != 10}">10이 아닙니다</span>
		<span th:unless="${a != 10}">10 입니다</span>
	</div>

* else if 문은 없기 때문에  그냥 if 문으로 다 써준다.

 

 

 

* ex01.html 파일 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- 타임리프 명시적 선언 -->
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h3>타임리프 출력과 변수</h3>
	
	<!--  타임리프 문법을 사용하려면 속성앞에 th를 붙입니다 -->
	
	<h3 th:text="${'헬로월드'}"></h3>
	<h3>[[${'헬로월드'}]]</h3>
	
	<h3 th:text="${1==1}"></h3>
	<h3>[[${ 1 == 1}]]</h3>
	
	<h3 th:text="${true and false}"></h3>
	<h3>[[${true and false}]]</h3>	
	
	<h3>[[${'가' eq '가나다'}]]</h3>
	
	<hr/>
	
	<!-- 변수선언 -->
	<div th:with="a = 10">
		[[${a}]]
	</div>
	<!-- 표현은 위와같이 쓰는게 우리에게 익숙하기 때문에 위와같이 더 많이 사용함. -->
	<div th:with="a = '홍길동'" th:text="${a}">
	
	</div>
	
	<!-- value 속성의 사용 -->
	<!-- 어떤속성이든 (여기서는 value) 간에 타임리프구문을 사용하고 싶다면 속성앞에 th: 를 붙여주면 됨. -->
	<div th:with="a = '이순신'">
		<input type="text" th:value="${a}">
	</div>
	
	<!-- if 문 -->
	<div th:with="a=10">
		<span th:if="${a == 10}">[[${a + '입니다'}]]</span>
		<span th:if="${a == 20}">[[${a + '입니다']]</span>
	</div>
	
	<!-- if else문 : unless는 동일한 조건을 적습니다-->
	<div th:with="a = 10">
		<span th:if="${a != 10}">10이 아닙니다</span>
		<span th:unless="${a != 10}">10 입니다</span>
	</div>
	
	<!-- 삼항연산자 -->
	<div th:with="a = 10">
		[[${a == 10 ? '참' : '거짓'} ]]<br/>
		[[${a} == 10 ? '참' : '거짓' ]]
	</div>
	

</body>
</html>

 

---- 여기서부터는 ex02.html 에서 확인 ----

*th:each 반복문 에서는 향상된 for문만 제공이 된다. (일반 for문은 없다)

- each에 두번째 변수를 선언하면 상태값(state)을 담아줍니다.

state에 담겨있는 값 ->  (index, count, size, current 등)

 

 

*ex02.html 파일

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- 타임리프 명시적 선언 -->
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h3>반복문</h3>
	[[${list}]]
	
	<ul>
		<li th:each="vo : ${list}">
			[[${vo.name}]]
			[[${vo.age}]]
		</li>
	</ul>
	
	<hr/>
	
	<h3>반복문과 state변수(jstl= varState)</h3>
	<!-- each에 두번째 변수를 선언하면 상태값을 담아줍니다. (index, count, size, current등) -->
	<ul>
		<li th:each="vo, a : ${list}">
			[[${a.count}]]번 - [[${vo.name}]]
		</li>
	</ul>
	
	
	<h3>반복문과 조건문</h3>
	<ul>
		<li th:each="vo:${list}" th:if="${vo.age % 2 == 0}">
			[[${vo.age + '는 짝수'}]]
		</li>
	</ul>
	
	<h3>반복문과 조건문2</h3>
	<ul>
		<li th:each="vo:${list}">
			<span th:if="${vo.age % 2 == 0}">짝수</span>
			<span th:unless="${vo.age % 2 == 0}">홀수</span>
		</li>
	</ul>

</body>
</html>

 

 

---- 여기서부터는 ex03.html 에서 확인 ----

 

* th:block 은 제어문을 쓰고싶은데 태그는 딱히 쓰고싶지 않을 때 사용.

- 가짜 태그라고 보면됨.

 

*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>

위 코드 출처: 

https://developer-rooney.tistory.com/181

- 사용 예시

<!-- 
		a링크로 값을 넘기는 방법
		경로(키=값, 키=값)
		
		경로/변수/변수(변수=값, 변수=값)
	 -->
	<ul>
		<li th:each="vo : ${list}">
			<a th:href="@{test(age=${vo.age}, name=${vo.name})}">키값넘기기(쿼리스트링)</a>
			<a th:href="@{test2/{age}/{name}(age=${vo.age}, name=${vo.name})}">키값넘기기(쿼리파라미터)</a>
		</li>
	</ul>

- 위에 내용을 받는 컨트롤러

	//쿼리파라미터
	@GetMapping("/test2/{a}/{b}")
	public String test(@PathVariable("a") String a,
					   @PathVariable("b") String b) {
		
		System.out.println(a);
		System.out.println(b);
		
		return "view/test";
	}

 

*ex03.html 파일

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- 타임리프 명시적 선언 -->
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h3>타임리프 block문</h3>
	[[${list}]]
	
	<hr/>
	
	<!-- block은 별도의 태그를 사용하지 않고, 마치 중괄호 처럼 사용하고 싶을 때 적용. -->
	<ul>
		<th:block th:each="vo : ${list}">
		<li>[[${vo}]]</li>
		</th:block>
	</ul>
	
	<hr/>
	<h3>타임리프 a태그</h3>
	
	<a href="test?a=10">일반a태그</a>
	<a th:href="@{test?a=10}">타임리프a태그</a>
	
	<!-- 
		a링크로 값을 넘기는 방법
		경로(키=값, 키=값)
		
		경로/변수/변수(변수=값, 변수=값)
	 -->
	<ul>
		<li th:each="vo : ${list}">
			<a th:href="@{test(age=${vo.age}, name=${vo.name})}">키값넘기기(쿼리스트링)</a>
			<a th:href="@{test2/{age}/{name}(age=${vo.age}, name=${vo.name})}">키값넘기기(쿼리파라미터)</a>
		</li>
	</ul>

</body>
</html>

 

 

---- 여기서 부터는 ex04.html 파일에서 확인 ----

 

* 자바스크립트에서 타임리프 구문 사용하기

- 스크립트 구문안에 th:inline=javascript 를 안 적어도 

 값은 받아와 진다.

 하지만 단순한 문자열로 받아와짐. 

이런 문자열로는 아무것도 못 함.

반면에 스크립트 구문에 

 th:inline=javascript  를 적어주면,

 

이렇게 제이슨 형식으로 변환할  수 있는 문자열로 받아와짐.

아래 코드에서 확인해보면 알수있듯이

문자열을 제이슨으로 바꿔주어서 사용하면 된다.

★ JSON.parse()   함수  -> 문자열을 제이슨 형식으로 바꿔줌.

 

-컨트롤러

	@GetMapping("/ex04")
	public String ex04(Model model) {
		
		BuilderVO2 vo = new BuilderVO2("이순신", 20);
		
		model.addAttribute("name","홍길동");
		model.addAttribute("vo", vo);
		
		return "view/ex04";
	}

-ex04.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"><!-- 타임리프 명시적 선언 -->
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	[[${name}]]<br/>
	[[${vo}]]
	
	<!-- 컨트롤러에서 보내는 값을 JSON 문자열 형태로 받아줍니다. -->
	<script th:inline="javascript">
	var aa = '[[${name}]]';
	var bb = '[[${vo}]]';
	
	console.log(JSON.parse(aa));
	console.log(JSON.parse(bb));
	</script>

</body>
</html>

 

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

# 타임리프 내장함수

참고: 

https://abbo.tistory.com/56

 

Thymeleaf Utility Objects (1)

Author: 니용 이전 글에서 Thymeleaf의 기본적인 문법을 확인하였다면, 이번 글에서는 Thymeleaf를 더 심도 있게 활용할 수 있는 방법을 알려드리려고 합니다. Thymeleaf는 Utility Object라고 하는 함수를 기

abbo.tistory.com

 

 

*날짜형은 Date 타입 쓰지말고 웬만하면 

TimeStamp 나  LocalDateTime.now() 을 사용하기.

 

- 컨트롤러

//타임리프 내장함수
	@GetMapping("/ex05")
	public String ex05(Model model) {
		
		//날짜의 형변환은 database, 자바, 화면에서 처리
		model.addAttribute("regdate", LocalDateTime.now()); //날짜형
		return "view/ex05";
	}

-화면

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h3>타임리프 내장함수 (구글링 하세요)</h3>
	
	[[${regdate}]]
	
	<br/>
	[[${#temporals.format(regdate, 'yyyy-MM-dd')}]]
	
	<br/>
	[[${#strings.substring('홍길동', 0, 1)}]]

</body>
</html>

 

-결과

 

 

 

# 타임리프 문법 include 방식

 

 

*th:fragment 로 가져올 부분을 조각내기.

*th:replace 로  조각낸 부분을 가져오기.

- th:replace 가 들어가는 태그가 통째로 대체된다고 생각하면 됨.

★ th:replace="~{경로 :: 가져올 fragment의 이름 }"    ->   경로는 절대경로(/) 로 적어주기.

 

- 가져오는 방법으로 총 3가지 정도 알고있으면 됨 -

 

1. fragment 를 가져오기

*조각 ( layout01.html)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<div th:fragment="part1">
		<h3>프래그먼트1</h3>
	</div>
	
	<div th:fragment="part2">
		<h3>프래그먼트2</h3>
	</div>
	
</body>
</html>

 

*경로

 

* 조각 가져오기 (ex06.html)

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

	<h3>타임리프 include</h3>
	
	<!-- fragment를 가져오기 -->
	<!-- 태그자체가 통째로 대체된다고 생각하면 됨. -->
	<div th:replace="~{/include/layout01 :: part1}"></div>
	<th:block th:replace="~{include/layout01 :: part2}"></th:block>

</body>
</html>

 

2. 파일을 통째로 가져오기.

 

*가져올 파일

layout2.html

<header>
	<nav>
		<ul>
			<li><a href="#">메뉴</a></li>
			<li><a href="#">메뉴</a></li>
			<li><a href="#">메뉴</a></li>
			<li><a href="#">메뉴</a></li>
		</ul>
	</nav>
</header>

*가져오는 구문

-  :: fragment 의 이름  -> 빼주면 파일을 통째로 가져오게됨.

	<!-- 파일을 통째로 가져오려면 ~{경로} -->
	<th:block th:replace="~{/include/layout02}"></th:block>

 

3. 템플릿화 시켜서 사용하기.

 

* layout03.html  ( 템플릿화 시킨 내용을 가진 파일) 

- 겉에 감싸는 태그( th:block  , 가상태그 ) 에는 

  th:fragment 에  fragment 이름으로 함수를 줬다.  

 함수에는 매개변수를 받을 수 있도록 하여

 템플릿화 시킨 구조에서  안에 한 부분을 다른 내용으로 바꿔 낄수 있도록 했다.

- 계속해서 바뀌는 부분이 되는 태그에는 

 th:replace 에  함수의 매개변수 값이 오도록 했다.

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

<th:block th:fragment="함수(section)">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<header>
		공통 템플릿 헤더
	</header>

	<section th:replace="${section}">
	
	</section>

	<footer>
		공통 템플릿 푸터
	</footer>

</body>
</th:block>

</html>

 

* ex07.html ( 템플릿구조를 적용 시킬 파일)

- 템플릿에 부분적으로 들어갈 태그(부분)에 

   id 혹은 class 값을 준다.

- 템플릿에 부분으로 들어가는 태그를 감싸는 태그를 만든다.

  감싸는 태그에

  th:replace="~{절대경로 :: fragment 이름에 해당하는 함수(~{:: 클래스나, 아이디이름 참조})} 

  함수의 매개변수 부분에 절대경로를 기재하지 않은 이유는 현재파일의 태그를 참조한다는 의미. 

  id 는 #아이디값,  class는 .클래스값   이렇게   :: (쌍콜론) 뒷부분에 써넣어주면 된다.

 

위와같이 해주면  템플릿 구조화된 태그의  변경되는 태그 부분에 

넣고자 하는 태그가 들어감.

 

★ 추가 ★

자바스크립트 구문을 사용하고자 한다면

<script> 태그를 , 템플릿 구조를 대체하는 태그의 바깥 부분에 적어줘야 함.  

아래 코드 참고.

  

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

	<!-- ~{파일경로 :: 템플릿함수(~{::선택자})} -->
	<th:block th:replace="~{/include/layout03 :: 함수(~{:: .second})}">
	
		<div id="first">
			아이디 선택자 #
		</div>
		
		<div class="second">
			클래스 선택자.
		</div>
		
	</th:block>
	
		<script>
			console.log("이 파일에서만 사용")
		</script>

 

 

# 오늘 실습 #

 

*quiz01.html

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

	<th:block th:replace="~{/include/layout03 :: 함수(~{::.wrap})}">
	
	<h3>이 화면에 진입할 때 SimpleVO를 이용하여 데이터를 출력 합니다. (값은 아무거나)</h3>
	 [[${vo.id}]] <br/>
	 [[${vo.userName}]] <br/>
	 [[${vo.userNum}]] 
	 
	<p>
		회원정보확인 링크에는 quiz_result01?키=값 형태로
		회원번호, 이름, 아이디 을 넘겨주세요
		
		아래 class="wrap" 부분만 layout03 템플릿에 전달 될 수 있도록 처리하세요
	</p>
	
	
	<div class="wrap" >
		회원번호: [[${vo.userNum}]] 
		이름: [[${vo.userName}]]
		아이디: [[${vo.id}]]
		<br>
		<a th:href="@{quiz_result01(id=${vo.id}, name=${vo.userName}, num=${vo.userNum} )}">회원정보확인</a>
	</div>
	
	</th:block>
	
</html>

* layout03.html (템플릿 구조화 파일)

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

<th:block th:fragment="함수(section)">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<header>
		공통 템플릿 헤더
	</header>

	<section th:replace="${section}">
	
	</section>

	<footer>
		공통 템플릿 푸터
	</footer>

</body>
</th:block>

</html>

 

*SimpleVO.java

package com.simple.basic.command;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SimpleVO {
	
	private int userNum;
	private String userName;
	private String id;
	
}

* 컨트롤러

//quiz01 - 실습
	@GetMapping("/quiz01")
	public String quiz01(Model model) {
		SimpleVO vo = SimpleVO
					  .builder()
					  .userName("jordan")
					  .userNum(23)
					  .id("michle")
					  .build();
		model.addAttribute("vo", vo);
		return "view/quiz01";
	}
	
	//quiz_result01 - 실습
	@GetMapping("/quiz_result01")
	public String quiz_result01(@RequestParam("name") String name,
							    @RequestParam("id") String id,
							    @RequestParam("num") int num,
							    Model model) {

		model.addAttribute("id", id);
		model.addAttribute("name", name);
		model.addAttribute("num", num);
		
		return "view/quiz_result01";
	}

* quiz_result01.html  (전달받은 데이터 출력 페이지)

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

	[[${id}]]
	[[${name}]]
	[[${num}]]

</body>
</html>

 

 

<기억할것>

#날짜의 형변환

날짜의 형변환은 database, 자바, 화면에서 처리

*날짜의 형변환은 자신 있는 곳에서 하면 되는데

10 중 7,8 은 데이터베이스에서 함.