06장 템플릿 엔진(타임리프)알아보기

박준우·2025년 7월 4일
0

Spring Boot

목록 보기
6/14

1. 타임리프 알아보기

타임리프 템플릿 엔진이란?

HTML, XML등 다양한 View파일에 동적으로 값을 넣어주는 역할을 한다.
탬플릿 엔진은 데이터와 템플릿 파일을 합쳐서 최종적인 뷰를 생성해낸다.

  1. HTML을 기반으로 사용하기 쉽다.
  2. 동적 페이지 생성
  3. 네츄럴 탬플릿(템플릿을 HTML 그대로 특별한 도구 없이 볼 수 있다.)
  4. 브라우저에서 그대로 볼 수 있어 디자이너와 협업 가능

모델 인터페이스란?

일단 스프링 MVC에서 요청 수신에서 응답까지 흐름(복습)은 아래와 같다.

  1. 뷰로 사용자가 요청
  2. Dispatcher Servlet 이 다른 컨트롤러의 요청 메소드(핸들러)를 호출
  3. 컨트롤러는 비지니스로직을 호출해 결과를 가져온다.
  4. 가져온 결과를 모델로 설정하고, 뷰 이름을 return한다.
  5. Dispatcher Servlet 이 return된 뷰에 대해 출력하라고 클라이언트에게 요청한다.
  6. 클라이언트는 뷰를 보여준다.

모델 인터페이스는 컨트롤러가 가져온 모델을 웹페이지(뷰)에 표시하기 위하여 사용한다. 즉, 데이터를 뷰에 전달하는 역할을한다. Model은 Spring이 자동으로 관리해주며, Model을 사용할 때는 컨트롤러의 요청 메소드(핸들러)의 인수에 Model타입을 지정해주면, addAttribute메소드를 통하여, 데이터값을 뷰에 전달시켜 줄 수 있다.

모델 인터페이스 사용방법

Model에 객체를 저장하기 위해서는 addAttribute 함수를 사용한다.

addAttribute(String name, Object value)

이 때 name은 키, value는 값이자 객체의 역할을 의미한다.

2. Model을 사용한 프로그램 만들기

0.사전 프로그램 세팅

05장 MVC 모델 알아보기 3.Spring MVC 사용해보기 예제를 그대로 사용한다.

1.컨트롤러 수정

컨트롤러 파일을 아래처럼 변경한다.

@Controller
@RequestMapping("hello")
public class HelloViewController {

    @GetMapping("view")
    public String helloView() {
        // 반환값으로 뷰 이름(html파일명=hello)을 반환
        return "hello";
    }

    @GetMapping("model")
    public String helloView(Model model) {
        // Model에 데이터를 저장
        model.addAttribute("msg", "타임리프!!!");
        // 반환값으로 뷰 이름(html파일명=helloThymeleaf)을 반환
        return "helloThymeleaf";
    }
}

#### 해석
1. @Controller : 이 클래스를 컨트롤러로 선언한다. HTTP요청을 받아 로직을 
호출하고 응답을 반환하는 역할을 한다.
2. @RequestMapping("hello") : 컨트롤러와 URL을 매핑하기 위한 역할. 
안의 인자는 매핑할 URL의 경로이다. 
3. @GetMapping("view") : GET방식 으로 /hello/view URL로 들어오는 
요청을 이 메소드가 처리한다고 선언한다. 

4. @GetMapping("model") : GET방식으로 /hello/modle URL로 들어오는 
요청을 이 메소드가 처리한다고 선언한다. 
5. public String helloView(Model model) : Modle에 요청 핸들러의 인수로 모델타입을 선언하였다.
6. model.addAttribute("msg", "타임리프!!!"); : 
msg라는 이름으로 타임리프!!! 라는 값을 모델에 저장한다. 이렇게 한번 모델에 저장되면, 
return으로 지정된 html(뷰)에서 ${msg}를 통하여, 타임리프!!!라는 값을 사용할 수 있다. 

만약 Model 객체에서 오류가 난다면?

마우스 우클릭 -> 컨텍스트 액션 -> 클래스 가져오기 -> Model(org.springframework.ui)를 선택

2. 뷰 만들기

뷰는 html파일로, 컨트롤러에서 만들어진 모델의 return "문자열 값"과 같은 이름의 html파일을 사용해야 모델을 사용할 수 있다.

<!DOCTYPE>

<!-- 타임리프 사용 선언 -->
<html xmlns:th= "http://www.thymeleaf.org">
<head>
    <Meta charset = "UTF-8">
    <title>Hello Thymeleaf</title>
</head>
<body>
<h1 th:text="${msg}"> 표시되는 부분</h1>
</body>

</html>

#### 해석
1. <html xmlns:th= "http://www.thymeleaf.org"> :
이 html에서 타입리프 기능을 사용함을 선언한다.

2. <h1 th:text="${msg}"> 표시되는 부분</h1>:
타입리프의 기능은 th:속성명 형식으로 사용하며, th:text=는 문자열 속성을 "${msg}" 라는 모델 이름을 참조해 출력하라는 의미이다. 

결과

표시되는 부분 텍스트는 모델에 의하여 데이터가 들어갈 자리를 의미한다.


실제로 메인 함수를 호출하면 모델의 타임리프!!! 라는 텍스트가 표시되는 부분에 표기된다.

요약

  1. 사용자가 GET과 URL(hello/model)로 컨트롤러에게 명령한다.
  2. 컨트롤러는 GET과 URL에 대응하는 요청 핸들러 메서드인 helloview 메소드를 실행시킨다.
  3. helloview메소드는 모델을 만들어 이름과, 텍스트 데이터를 저장하고, 뷰 이름을 리턴한다.
  4. 탬플릿 엔진은 html 뷰와 모델을 바인딩하여 타임리프를 생성, 그 후 사용자에게 결과를 응답한다.

3. 타임리프 알아보기

타임리프는 뷰 파일(HTML)에 동적인 값을 넣어준다고 했었다. 이때 조건문등도 사용해서 제어구문이나 반복문도 만들 수 있고, 텍스트 외의 데이터도 넣어줄 수 있는데 타임리프의 사용법을 알아보자.

1. 타임리프 문법

(1) 직접 문자 삽입하기

<!-- 01: 직접 문자를 삽입 -->
<h1 th:text="'hello world'">표시할 부분</h1>
<h1 th:text="${모델명}">표시할 부분</h1>

th:text= 설정한 문자를 표기한다. 아래쪽 "${모델명}"의 경우, 모델명을 컨트롤러에서 불러와 이를 적용 및 표기한다. 타입리프에서 문자열은 작은따움표 ''안에 넣어야한다.

(2) 인라인 처리

<!-- 02: 인라인 처리 -->
<h1>[[${name}]] 님, 안녕하세요!</h1>

인라인 처리란? HTML 태그안에서 변수사용이 아닌, 텍스트문 안에서 변수를 사용하고 싶을 때 [[ ]]로 감싸주어 사용가능하다.

(3) 값 결합

<!-- 03: 값 결합 -->
<h1 th:text="'내일은 ' + '맑음' + '입니다.'">표시할 부분</h1>

+를 통하여 문자열을 합칠 수 있다.

(4) 값 결합(리터럴 치환)

기존
<h1 th:text="${name} + '님, 안녕하세요!'"></h1>

개선
<!-- 04: 값 결합(리터럴 치환) -->
<h1 th:text="|${name} 님, 안녕하세요!|">표시할 부분</h1>

리터럴 치환이란? |${name}| 처럼 사용하여, 문자열과, 변수값을 한 곳에 기술할 수 있다. (기존엔 +로 연결, ''(작은따움표)로 문자를 감싸줘야하는 불편함이 존재했다.)

(5) 지역변수

<!-- 05: 지역 변수 -->
<div th:with="a=1,b=2">
    <span th:text="|${a} + ${b} = ${a+b}|"></span>
</div>

th:with="변수명=값"을 사용하여, 변수에 숫자값을 대입할 수 있다.
div는 html박스와 같이 전체구역을 지정, span은 그중 하나를 감쌀 수 있는 태그다.

(6) 비교와 등가

<!-- 06: 비교와 등가 -->
<span th:text="1 > 10"></span>
<span th:text="1 < 10"></span>
<span th:text="1 >= 10"></span>
<span th:text="1 <= 10"></span>
<span th:text="1 == 10"></span>
<span th:text="1 != 10"></span>
<span th:text="철수 == 철수"></span>
<span th:text="철수 != 철수"></span>

이처럼 비교연산자를 사용하면, 화면에 True, False가 표기된다.

(7) 조건연산자

<!-- 07: 조건 연산자 -->
<p th:text="${name} == '철수'?'철수입니다!':'철수가 아닙니다.'"></p>

왼쪽 식이 참이면 ? 이후인 철수입니다.를 출력, 아니라면 : 이후인 철수가 아닙니다.를 출력한다.

(8) 조건부 분기

<!-- 08: 조건부 분기(true) -->
<div th:if="${name} == '철수'">
    <p>철수입니다!</p>
</div>

<!-- 09: 조건부 분기(false) -->
<div th:unless="${name} == '영희'">
    <p>영희가 아닙니다.</p>
</div>

th:if의 조건이 철수면 이 태그와 자식요소를 실행한다.
th:unless의 조건이 영희가 아니라면 이 태그와 자식요소를 실행한다.

(9) switch

<!-- 10: switch -->
<div th:switch="${name}">
    <p th:case="철수" th:text="|${name}입니다!|"></p>
    <p th:case="영희" th:text="|${name}입니다!|"></p>
    <p th:case="민수" th:text="|${name}입니다!|"></p>
    <p th:case="*">명단에 없습니다.</p>
</div>

th:switch값과, th:case값이 일치하면 HTML요소를 실행한다.
th:th:case="*" 를 통해 else 구문을 구현할 수 있다.

(10) 객체 데이터 참조

<!-- 11: 참조(데이터를 모아놓은 객체) --> 리스트, 배열 등등 
<p th:text="${mb.id}">ID</p>
<p th:text="${mb.name}}">이름</p>
<p th:text="${mb['id']}">ID: []로 접근</p>
<p th:text="${mb['name']}">이름: []로 접근</p>

객체의 데이터를 참조할 경우, public인 get 함수를 생성해두면, 참조가 가능하다.

<!-- 12: 참조(th:object) -->
<div th:object="${mb}">
    <p th:text="*{id}">ID</p>
    <p th:text="*{name}}">이름</p>
    <p th:text="*{['id']}">ID: []로 접근</p>
    <p th:text="*{['name']}">이름: []로 접근</p>
</div>

List의 참조

<!-- 13: 참조(List) -->
<p th:text="${list[0]}">방위</p>
<p th:text="${list[1]}">방위</p>
<p th:text="${list[2]}">방위</p>
<p th:text="${list[3]}">방위</p>

Map의 참조

<!-- 14: 참조(Map) -->
<p th:text="${map.minsoo.name}">이름1</p>
<p th:text="${map.jiyoung.name}">이름2</p>
<p th:text="${map['minsoo']['name']}">이름1: []로 접근</p>
<p th:text="${map['jiyoung']['name']}">이름2: []로 접근</p>

(11) 반복

<!-- 15: 반복 -->
<div th:each="member : ${members}">
    <p>[[${member.id}]] : [[${member.name}]]</p>
</div>

반복문은 th:each="변수명 : ${반복할 객체(리스트 등)명}"로 사용할 수 있다. 

#### 해석
$member라는 리스트에 들어있는 각 요소를 반복하여 출력한다. 
이 때 각 반복시의 요소들을 member라는 변수로써 지정한다.
만약 컨트롤러에게 아래와 같은 데이터가 넘어온다면, 

List<Member> members = List.of(
    new Member(1, "철수"),
    new Member(2, "영희"),
    new Member(3, "민수")
);
model.addAttribute("members", members); // member라는 객체요소를 모델에추가

뷰에서는 아래처럼 표기된다. 
<p>1 : 철수</p>
<p>2 : 영희</p>
<p>3 : 민수</p>

(12) 반복 상태

<!-- 16: 반복 상태 -->
<div th:each="member, s : ${members}" th:object="${member}">
    <p>
        index-> [[${s.index}]], count-> [[${s.count}]],
        size-> [[${s.size}]], current-> [[${s.current}]],
        even-> [[${s.even}]], odd-> [[${s.odd}]],
        first-> [[${s.first}]], last-> [[${s.last}]],
        [[*{id}]] : [[*{name}]]
    </p>
</div>

#### 해설 
th:each="변수명, 반복상태변수 : ${반복할 객체명}"
여기서 반복 상태변수란? 이 반복요소가 반복하는 조건에 대하여 출력해주는 함수이다.

th:object="${member} 이것을 사용해주면, member라는 객체를 기본 객체로 사용하여
아래처럼 member기술을 생략할 수 있다.

th:object를 사용 안한 경우
<p>[[${member.id}]] : [[${member.name}]]</p>
위처럼 th:each로 지정된 member객체의 이름로 직접 접속해야한다.

사용한 경우 아래처럼 자신을 가리키는 포인터로 접속 가능하다. 
<div th:object="${member}">
    <p>[[*{id}]] : [[*{name}]]</p>
</div>

상태변수 사용법

상태 변수의미값 예시설명
s.index반복 인덱스(0부터)0, 1, 2리스트에서 현재 위치, 0부터
s.count반복 순서(1부터)1, 2, 3현재 몇 번째 반복인지, 1부터
s.size전체 리스트 크기3members 리스트의 크기
s.current현재 원소 객체member현재 member 객체
s.even현재 반복이 짝수 번째인지true/false0, 2, 4, ...이면 true
s.odd현재 반복이 홀수 번째인지true/false1, 3, 5, ...이면 true
s.first첫 번째 반복인지true/false첫 번째 루프면 true
s.last마지막 반복인지true/false마지막 루프면 true

2. 유틸리티 객체

타임 리프에서는 자주사용되는 클래스를 메서드 형태로 쉽게 사용할 수 있도록 '#이름' 이라는 상수로 정의한다. 그리고 이를 변수 표현식 내에서 사용할 수 있다.

(1) 유틸리티 객체 정리

이름설명예시
#strings문자열 관련 함수#strings.contains(str, 'a')
#numbers숫자/포맷 관련 함수#numbers.formatInteger(1234, 3)
#dates날짜/시간 관련 함수#dates.format(date, 'yyyy-MM-dd')
#calendarsCalendar 객체 관련 함수#calendars.format(cal, 'yyyy')
#temporalsJava8 LocalDateTime 등 관련 함수#temporals.format(ldt, 'yyyy-MM-dd')
#bools불린(boolean) 관련 함수#bools.isTrue(var)
#arrays배열 관련 함수#arrays.contains(arr, item)
#lists리스트 관련 함수#lists.size(list)
#sets집합(Set) 관련 함수#sets.size(set)
#maps맵(Map) 관련 함수#maps.size(map)
#objects기타 객체 관련 함수#objects.defaultObject(obj, '기본')
#messages메시지/국제화 관련 함수#messages.msg('key')
#ids랜덤 ID 생성 등#ids.seq('abc')

(2) 유틸리티 객체 사용예

숫자 표현

<!-- 17: 유틸리티 객체(숫자) -->
<div th:with="x=1000000, y=123456.789">
    정수 형식: <span th:text="${#numbers.formatInteger(x, 3, 'COMMA')}"></span><br/>
    부동 소수점 형식: <span th:text="${#numbers.formatDecimal(y, 3, 'COMMA',2,'POINT')}"></span>
</div>

#### 해석
th:with= 를 통해 div영역 안에서 x, y 지역변수를 만든다.
#numbers :  숫자 관련 함수를 사용가능한 유틸리티 객체이다. 
.formatInteger : 정수의 형식을 지정한다.
(x, 3, 'COMMA') : 변수x를, 최소 3자리 이상으로 표현, 3자리 마다 쉽표 사용

.formatDecimal : 소수점으로 표현
(y, 3, 'COMMA',2,'POINT') : 변수 y를, 최소 3자리 표현, 3자리마다 쉽표, 2자리, 고정소수점 표현

날짜 및 시간 표현

<!-- 17: 유틸리티 객체(날짜 및 시간) -->
<div th:with="today=${#dates.createNow()}">
    yyyy/mm/dd 형식:<span th:text="${#dates.format(today, 'yyyy/MM/dd')}"></span><br/>
    yyyy년 mm월 dd일 형식:<span th:text="${#dates.format(today, 'yyyy년 MM월 dd일')}"></span><br/>
    yyyy년:<span th:text="${#dates.year(today)}"></span><br/>
    MM월:<span th:text="${#dates.month(today)}"></span><br/>
    dd일:<span th:text="${#dates.day(today)}"></span><br/>
    요일:<span th:text="${#dates.dayOfWeek(today)}"></span><br/>
</div>

#### 해석
<div th:with="today=${#dates.createNow()}">: 지역변수 today에 현재 날자와, 시간을 #dates 
유틸리티 객체를 담는다.  그 후 format함수나, 기타 함수들을 통해 날짜나 시간을 표현한다.

문자열 표현

<!-- 17: 유틸리티 객체(문자열) -->
<div th:with="str1='abcdef'">
    대문자 변환: <span th:text="${#strings.toUpperCase(str1)}"></span><br/>
    빈 문자 판정: <span th:text="${#strings.isEmpty(str1)}"></span><br/>
    길이: <span th:text="${#strings.length(str1)}"></span>
</div>

#### 해석 
<div th:with="str1='abcdef'"> : 지역변수 str1에 'abcdef문자를 집어넣는다.
#strings.toUpperCase(str1): string 유틸리티 객체를 통해 str1을 대문자 변환
#strings.isEmpty(str1): str1이 빈 문자열인지 확인한다. 즉, false 반환
#strings.length(str1): str1의 길이를 반환한다.

3. HTML 파일의 공통부분 표현

HTML 또한 헤더 푸터 등과 같이 여러 페이지에서 공통으로 사용하는 요소를 한곳에 모을 수 있다. 이를 HTML프래그먼트 라고 부른다. 또한 HTML프래그먼트가 담긴 파일을 공통 레이아웃 이라고도 부른다.

<!-- common.html -->
<div th:fragment="header">
    <h1>=== 【헤더】 ===</h1>
</div>
<div th:fragment="footer">
    <h1>=== 【푸터】 ===</h1>
</div>

#### 해석
<div th:fragment="header">: 이 div를 header라는 이름의 HTML프래그먼트로 설정한다. 

이렇게 설정된 html을 사용하려면 th:replace를 사용해주면된다.

<html>
<body>
    <div th:replace="common :: header"></div>
    <!-- 본문 내용 -->
    <div th:replace="common :: footer"></div>
</body>
</html>

4. 타임리프 사용 예제

0. 사전 프로그램 세팅

의존성 추가
1. Spring Boot DevTools(개발자 도구)
2. Thymeleaf(탬플릿 엔진)
3. Spring Web(웹)
4. Lombok(개발자 도구)

빌드 세팅
1. 설정-빌드도구-gradle-gradle jvm에서 java버전 변경하기.
2. 프로젝트 구조- 프로젝트설정-프로젝트SDK에서 java버전 변경하기.

1. 직접 문자를 삽입해 값 결합 만들기

컨트롤러 만들기

@Controller
public class ThymeleafController {
    @GetMapping("show")
    public String showView(Model model) {
        model.addAttribute("name", "철수");
        return "main";
    }
}
#### 해석
@Controller: 이 클래스를 컨트롤러로 설정한다.
@GetMapping("show") : Get 메소드로 http://localhost:8080/show URL에 접근하면, 
public String showView(Model model) : Model을 스프링이 만들어준다.
model.addAttribute("name", "철수"); : 모델의 name변수에 철수라는 텍스트 추가
return "main" : 모델을 return할 뷰(html)의 이름은 main이다. 

뷰 만들기

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

<body>
<!-- 01: 직접 문자를 삽입 -->
	<h1 th:text="'hello world'">표시할 부분</h1>
<!-- 02: 인라인 처리 -->
	<h1>[[${name}]] 님, 안녕하세요!</h1>
<!-- 03: 값 결합 -->
	<h1 th:text="'내일은 ' + '맑음' + '입니다.'">표시할 부분</h1>

</body>
</html>

#### 해석
<html xmlns:th="http://www.thymeleaf.org"> : 이 html이 타입리프를 사용함을 선언한다.

body의 각 방법은 다음과 같다.
1. <h1 th:text="'hello world'">: string으로 hello world 직접 출력
2. <h1>[[${name}]] 님, 안녕하세요! : 태그가아닌 내용에 name 모델을 사용해 출력
3. <h1 th:text="'내일은 ' + '맑음' + '입니다.'">: 1번과 같은 원리로 string출력

결과

2. 값 결합(리터럴치환)에서의 비교와 등가

뷰 만들기

	<!-- 01: 직접 문자를 삽입 -->
	<h1 th:text="'hello world'">표시할 부분</h1>
	<!-- 02: 인라인 처리 -->
	<h1>[[${name}]] 님, 안녕하세요!</h1>
	<!-- 03: 값 결합 -->
	<h1 th:text="'내일은 ' + '맑음' + '입니다.'">표시할 부분</h1>
	<hr>
	<!-- 04: 값 결합(리터럴 치환) -->
	<h1 th:text="|${name} 님, 안녕하세요!|">표시할 부분</h1>
	<!-- 05: 지역 변수 -->
	<div th:with="a=1,b=2">
		<span th:text="|${a} + ${b} = ${a+b}|"></span>
	</div>
	<!-- 06: 비교와 등가 -->
	<span th:text="1 > 10"></span>
	<span th:text="1 < 10"></span>
	<span th:text="1 >= 10"></span>
	<span th:text="1 <= 10"></span>
	<span th:text="1 == 10"></span>
	<span th:text="1 != 10"></span>
	<span th:text="철수 == 철수"></span>
	<span th:text="철수 != 철수"></span>

기존 뷰에 리터럴 치환 (|${name}|) 과, 비교 등가문을 추가하였다.

결과

3. 조건 연산자를 이용한 조건부 분기

뷰 만들기

	<hr>
	<!-- 07: 조건 연산자 -->
	<p th:text="${name} == '철수'?'철수입니다!':'철수가 아닙니다.'"></p>
	<!-- 08: 조건부 분기(true) -->
	<div th:if="${name} == '철수'">
		<p>철수입니다!</p>
	</div>
	<!-- 09: 조건부 분기(false) -->
	<div th:unless="${name} == '영희'">
		<p>영희가 아닙니다.</p>
	</div>
    
#### 결과 해석
th:if 문은 참조건이면 실행하고,
th:unless면 거짓 조건이면 실행한다.
    

결과

현재 ${name} 변수에는 철수가 저장되어 있음.

4. switch에서 th:object(객체)만들기

1. 엔티티 생성

@Data
@AllArgsConstructor
public class Member {
	/** 회원 ID */
	private Integer id;
	/** 회원명 */
	private String name;
}

#### 해석
@Data : 롬복의 기능으로 getter, setter를 자동으로 생성해준다.
@AllArgsConstructor : 모든 변수에 대한 초기화 값을 인수로 받는 생성자를 생성한다.

구조(alt+7)

  1. @Data만 사용했을 때

  2. @AllArgsConstructor만 사용했을 때

2. 컨트롤러 만들기

1. 아래의 임포트 추가
import com.example.demo.entitiy.Member;
Member 클래스에서 id, name, get+set+생성자 정보 를 가진 객체를 만들어야 하기 때문이다.

2. 컨트롤러를 아래처럼 구성
@Controller
public class ThymeleafController {
    @GetMapping("show")
    public String showView(Model model) {
        Member member = new Member(1, "회원01");
        // Model에 데이터를 저장
        model.addAttribute("name", "철수");
        model.addAttribute("mb", member);
        return "main";
    }
}

#### 해석
Member 클래스타입의 객체 member를 생성자를 통해 만든다.
그 후, name에는 철수텍스트를, mb에는 member라는 객체를 모델로 설정한다. 

3. 뷰 추가하기


<hr>
<!-- 10:switch -->
<div th:switch="${name}">
	<p th:case="철수" th:text="|${name}입니다!|"></p>
    <p th:case="영희" th:text="|${name}입니다!|"></p>
    <p th:case="민수" th:text="|${name}입니다!|"></p>
    <p th:case="*">명단에 없습니다</p>
</div>
<!-- 11: 참조(데이터를 모아놓은 객체) -->
.으로 접근: <span th:text="${mb.id}">ID</span>-<span th:text="${mb.name}">이름</span>
<br/>
[]로 접근: <span th:text="${mb['id']}">ID</span>-<span th:text="${mb['name']}">이름</span>
<br/>
<!-- 12: 참조(th:object) -->
<th:block th:object="${mb}">
    .으로 접근: <span th:text="*{id}">ID</span>-<span th:text="*{name}">이름</span>
<br/>
    []로 접근: <span th:text="*{['id']}">ID</span>-<span th:text="*{['name']}">이름</span>
</th:block>

#### 해석
th:switch을 통해 ${name}에 저장된 텍스트를 모델로부터 받아온다. 
그리고 name변수와 th:case의 값이 같은 case를 찾아 출력한다.

mb는 모델로써 member라는 객체를 가진다. 따라서 ${mb.id}는 member의 id변수를 출력하게 된다. 
-> 1 출력 
${mb.name}는 회원01을 출력한다.

결과

5. 참조 (List형) 반복 예시

1. 컨트롤러 만들기

@Controller
public class ThymeleafController {
	@GetMapping("show")
	public String showView(Model model) {
		// Member 생성
		Member member = new Member(1, "회원01");
		// 컬렉션 저장용: Member 생성
		Member member1 = new Member(10, "민수");
		Member member2 = new Member(20, "지영");
		// 리스트 생성
		List<String> directionList = new ArrayList<>();
		directionList.add("동");
		directionList.add("서");
		directionList.add("남");
		directionList.add("북");
		// Map을 생성하고 Member를 저장
		Map<String, Member> memberMap = new HashMap<>();
		memberMap.put("minsoo", member1);
		memberMap.put("jiyoung", member2);
		// List를 생성하고 Member를 저장
		List<Member> memberList = new ArrayList<>();
		memberList.add(member1);
		memberList.add(member2);
		// Model에 데이터 저장
		model.addAttribute("name", "철수");
		model.addAttribute("mb", member);
		model.addAttribute("list", directionList);
		model.addAttribute("map", memberMap);
		model.addAttribute("members", memberList);
		// 반환값으로 뷰 이름을 반환
		return "main";
	}
}
#### 해석
Member member = new Member(1, "회원01");
Member member1 = new Member(10, "민수");
Member member2 = new Member(20, "지영");
컨트롤러에서 맴버객체 3개를 생성한다.

List<String> directionList: 자바 List 객체를 만드는 문법으로, 문자열에 String만 입력받음을 선언
new ArrayList<>() : List객체를 새로 만든다. 
directionList.add("동") 만들어진 객체에 데이터를 삽입하는 메소드

따라서 리스트의 결과는 ["동", "서", "남", "북"] 처럼 된다. 

Map<String, Member> memberMap = new HashMap<>();
Map은 key와 value를 이용하여 데이터를 저장한다. 이 때 HashMap<>() 을 기술하면,
Map을 찾을 때 Hash 방식을 이용해 검색하게 된다. 

memberMap.put("minsoo", member1);
맴버는 put함수를 이용하여 키와 값을 추가한다. 
이 때 값으로 객체를 입력하였다.

List<Member> memberList = new ArrayList<>();
memberList.add(member1);
memberList.add(member2);

원소로 객체가 담긴 리스트를 만든다.

이들을 model.addAttribute("name", "철수"); 처럼 모델에 추가할 수 있다.

2. 뷰 만들기

<hr>
<!-- 13: 참조(List) -->
<span th:text="${list[0]}">방위</span>
<span th:text="${list[1]}">방위</span>
<span th:text="${list[2]}">방위</span>
<span th:text="${list[3]}">방위</span><br>
<!-- 14: 참조(Map) -->
.으로 접근: <span th:text="${map.minsoo.name}">이름1</span>
<span th:text="${map.jiyoung.name}">이름2</span><br/>
[]로 접근: <span th:text="${map['minsoo']['name']}"> 이름1: []로 접근</span>
<span th:text="${map['jiyoung']['name']}">이름2: []로 접근</span>
<!-- 15: 반복 -->
<div th:each="member : ${members}">
   <p>[[${member.id}]] : [[${member.name}]]</p>
</div>

#### 해석
<span th:text="${list[0]}">방위</span> : 모델명이 list 를 찾아가서, 입력된 list의 0번째 원소를 
출력한다. 

<span th:text="${map.minsoo.name}">이름1</span> : Map을 출력하기위해, 
map(모델명).minsoo(맵속 객체의 이름).name변수를 출력하게 된다. 

<div th:each="member : ${members}"> :member라는 객체를 담을 변수를 임시로 생성하고, 
${members} : members 객체가담긴 리스트 모델의 원소(객체)를 하나씩 member 변수에 저장한다.
<p>[[${member.id}]] : [[${member.name}]]</p> : 모든 원소가 끝날때 까지 th:each로 인해 반복한다. 

결과

6. 반복상태에서 유틸리티 객체 만들기

1. 뷰 만들기

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>Thymeleaf Sample</title>
</head>
<body>
	<!-- 01: 직접 문자를 삽입 -->
	<h1 th:text="'hello world'">표시할 부분</h1>
	<!-- 02: 인라인 처리 -->
	<h1>[[${name}]] 님, 안녕하세요!</h1>
	<!-- 03: 값 결합 -->
	<h1 th:text="'내일은 ' + '맑음' + '입니다.'">표시할 부분</h1>
	<hr>
	<!-- 04: 값 결합(리터럴 치환) -->
	<h1 th:text="|${name} 님, 안녕하세요!|">표시할 부분</h1>
	<!-- 05: 지역 변수 -->
	<div th:with="a=1,b=2">
		<span th:text="|${a} + ${b} = ${a+b}|"></span>
	</div>
	<!-- 06: 비교와 등가 -->
	<span th:text="1 > 10"></span>
	<span th:text="1 < 10"></span>
	<span th:text="1 >= 10"></span>
	<span th:text="1 <= 10"></span>
	<span th:text="1 == 10"></span>
	<span th:text="1 != 10"></span>
	<span th:text="철수 == 철수"></span>
	<span th:text="철수 != 철수"></span>
	<hr>
	<!-- 07: 조건 연산자 -->
	<p th:text="${name} == '철수'?'철수입니다!':'철수가 아닙니다.'"></p>
	<!-- 08: 조건부 분기(true) -->
	<th:block th:if="${name} == '철수'">
		<p>철수입니다!</p>
	</th:block>
	<!-- 09:조건부 분기(false) -->
	<div th:unless="${name} == '영희'">
	    <p>영희가 아닙니다.</p>
	</div>
	<hr>
	<!-- 10:switch -->
	<div th:switch="${name}">
	    <p th:case="철수" th:text="|${name}입니다!|"></p>
	    <p th:case="영희" th:text="|${name}입니다!|"></p>
	    <p th:case="민수" th:text="|${name}입니다!|"></p>
	    <p th:case="*">명단에 없습니다</p>
	</div>
	<!-- 11: 참조(데이터를 모아놓은 객체) -->
	.으로 접근: <span th:text="${mb.id}">ID</span>-<span th:text="${mb.name}">이름</span>
	<br/>
	[]로 접근: <span th:text="${mb['id']}">ID</span>-<span th:text="${mb['name']}">이름</span>
	<br/>
	<!-- 12: 참조(th:object) -->
	<th:block th:object="${mb}">
	    .으로 접근: <span th:text="*{id}">ID</span>-<span th:text="*{name}">이름</span>
	    <br/>
	    []로 접근: <span th:text="*{['id']}">ID</span>-<span th:text="*{['name']}">이름</span>
	</th:block>
    <hr>
    <!-- 13: 참조(List) -->
    <span th:text="${list[0]}">방위</span>
    <span th:text="${list[1]}">방위</span>
    <span th:text="${list[2]}">방위</span>
    <span th:text="${list[3]}">방위</span><br>
    <!-- 14: 참조(Map) -->
	.으로 접근: <span th:text="${map.minsoo.name}">이름1</span>
	<span th:text="${map.jiyoung.name}">이름2</span><br/>
	[]로 접근: <span th:text="${map['minsoo']['name']}"> 이름1: []로 접근</span>
	<span th:text="${map['jiyoung']['name']}">이름2: []로 접근</span>
	<!-- 15: 반복 -->
    <div th:each="member : ${members}">
        <p>[[${member.id}]] : [[${member.name}]]</p>
    </div>
	<!-- ▽▽▽▽▽ 예제 6.38 ▽▽▽▽▽ -->
	<hr>
	<!-- 16: 반복 상태 -->
	<div th:each="member, s : ${members}" th:object="${member}">
	    <p>
	        index-> [[${s.index}]], count-> [[${s.count}]],
	        size-> [[${s.size}]], current-> [[${s.current}]],
	        even-> [[${s.even}]], odd-> [[${s.odd}]],
	        first-> [[${s.first}]], last-> [[${s.last}]],
	        [[*{id}]] : [[*{name}]]
	    </p>
	</div>
	<!-- 17: 유틸리티 객체(숫자, 날짜 및 시간, 문자열) -->
	<div th:with="x=1000000, y=123456.789">
	    정수 형식: <span th:text="${#numbers.formatInteger(x, 3, 'COMMA')}"></span><br/>
	    부동 소수점 형식: <span th:text="${#numbers.formatDecimal(y, 3, 'COMMA', 2,'POINT')}"></span>
	</div>
	<br/>
	<div th:with="today=${#dates.createNow()}">
	    yyyy/mm/dd 형식: <span th:text="${#dates.format(today, 'yyyy/MM/dd')}"></span><br/>
	    yyyy년 mm월 dd일 형식: <span th:text="${#dates.format(today, 'yyyy년 MM월 dd일')}"></span><br/>
	    yyyy년: <span th:text="${#dates.year(today)}"></span><br/>
	    MM월: <span th:text="${#dates.month(today)}"></span><br/>
	    dd일: <span th:text="${#dates.day(today)}"></span><br/>
	    요일: <span th:text="${#dates.dayOfWeek(today)}"></span>
	</div>
	<br/>
	<div th:with="str1='abcdef'">
	    대문자 변환: <span th:text="${#strings.toUpperCase(str1)}"></span><br/>
	    빈 문자 판정: <span th:text="${#strings.isEmpty(str1)}"></span><br/>
	    길이: <span th:text="${#strings.length(str1)}"></span>
	</div>
	<!-- △△△△△ 예제 6.38 △△△△△ -->
</body>
</html>

#### 해석
<div th:each="member, s : ${members}" th:object="${member}"> :
meber 객체가 담길 변수선언, s는 반복횟수 저장, ${members}모델명은 현재 객체가 담긴 리스트가 담김
th:object="${member} : 이 블록 내에서 member를 기본 객체로 설정한다.

th:object="${member}를 미 사용 : [[${member.id}]] : [[${member.name}]]
th:object="${member}를 미 사용 : [[*{id}]] : [[*{name}]] // 이 코드가 작동한다.,

결과

7. HTML 프래그먼트 만들기

프래그먼트란? html헤더, 푸터와 같이 반복해서 사용하는 부분을 한곳에 몰아 놓은곳

(1) 프래그먼트 뷰 만들기

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>fragment</title>
</head>
<body>
	<!-- 18: 프래그먼트 정의 -->
	<div th:fragment="header">
		<h1>=== 【헤더】 ===</h1>
	</div>
	<div th:fragment="footer">
		<h1>=== 【푸터】 ===</h1>
	</div>
</body>
</html>

#### 해석
<div th:fragment="header">
header라는 이름의 fragment를 가져온다. 

(2) 프로그먼트 뷰를 상속할 원래 html 뷰 작성

<hr>
	<div id="one" th:insert="common :: header"></div>
	<h1>위아래로 프래그먼트 삽입하기</h1>
	<div id="two" th:replace="common :: footer"></div>

#### 해석
th:insert 에서 common은 프래그먼트 html의 이름이며, ::header를 통해 
지정된 다른 뷰에서 지정한 pragment를 가져온다.

th:insert는 프래그먼트를 기존 태그 안에 삽입하고, 

<div id="one" th:insert="common :: header"></div>

<div id="one">
    <div>
        <h1>=== 【헤더】 ===</h1>
    </div>
</div>

th:replace는 기존 div를 대체시킨다. 
<div>
    <h1>=== 【푸터】 ===</h1>
</div>

<div id="two" th:replace="common :: footer"></div>
에서 div id = two 라는 태그속성이 삭제된다. 

결과

8. 공통 레이아웃 만들기

프래그먼트가 기능을 공유한다면, 공통레이아웃은 헤더, 푸터, 사이드 바등 공통적인 부분을 탬플릿(html) 하나로 합친다.

(1) 공통 레이아웃 만들기

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" 
th:fragment="base(title, content)">
<head>
    <meta charset="UTF-8">
    <!-- ▼ 여기가 바뀝니다 ▼ -->
  <title th:replace="${title}">공통 레이아웃</title>
</head>
<body>
  <div style="text-align: center;">
    <h1>☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆<br>
      ☆☆      공통 헤더      ☆☆<br>
      ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆</h1>
  </div>
  <!-- ▼ 여기가 바뀝니다 ▼ -->
  <div style="text-align: center;" th:insert="${content}">내용</div>
  <div style="text-align: center;">
    <h1>☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆<br>
      ☆☆      공통 푸터      ☆☆<br>
      ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆</h1>
  </div>
</body>
</html>

#### 해석
th:fragment="base(title, content)">
이 html 전체를 base라고 하는 프래그먼트로 정의한다.
이때 title과, content라는 파라미터를 받을 수 있도록 설정한다.

<title th:replace="${title}">공통 레이아웃</title>
${title}변수로 넘어온 프래그먼트를 아래 title 태그로 변경한다.

<div style="text-align: center;" th:insert="${content}">내용</div>
${content}변수로 넘어온 프래그먼트에 아래 내용을 삽입한다. 

메인 뷰 변경

<!DOCTYPE html>
<html xmlns:th ="http://www.thymeleaf.org"
 th:replace="~{layout ::base(~{::title}, ~{::body})}">
<head>

#### 해석
th:replace를 사용해 현재 html태그를 layout.html에서 정의된 base(title, contemt)프래그먼트로 대체한다.

layout ::base : layouy.html파일에서, base라는 프래그먼트를 불러온다.
~{::title}, ~{::body} : 현재main.html의 title, body 태그를 공통 레이아웃
base(title, content)의 title, content 라는 인자로써 넘겨준다.

~는 틸데라고 부르며, 어떤 태그를 가져오거나 조작할지 지정하는 명령어

정리하자면, th:replace를 사용해 탬플릿의 정보를 공통레이아웃 th:fragment로 전송하여 공통레이아웃에서 이를 사용한다.

결과

profile
DB가 좋아요

0개의 댓글