아래 글은 스프링 프레임 워크 첫걸음 책을 기반하여 작성한 글입니다.
6-1 템플릿 엔진의 개요
(1) 템플릿 엔진이란?
: 데이터를 미리 정의된 템플릿에 *바인딩해서 뷰의 표시를 도와주는 것이다.
* 바인딩이란 어떤 요소나 데이터, 파일 등을 서로 연결하는 것
(2) 타임리프란?
타임 리프의 기능 2가지
- HTML 기반의 템플릿 엔진으로, 정해진 문법으로 작성하면 페이지를 동적으로 조립해준다.
- HTML을 기반으로 하기 때문에 최종 출력을 생각하면서 뷰를 만들 수 있다. 즉, 타임리프를 사용하면 디자이너와 쉽게 분업할 수 있다.
6-2 Model 인터페이스의 사용법
(1) Model 인터페이스란
Model 인터페이스의 특징 3가지
- 처리한 데이터를 뷰에 표시하고 싶을 경우 데이터를 전달하는 역할을 한다,
- 객체를 저장하고 관리하는 기능을 제공한다.
- Model을 이용하고 싶은 경우 요청 핸들러 메서드의 인수에 Model 타입을 전달한다. 그러면 스프링 MVC가 자동으로 Model 타입 인스턴스를 설정한다.
(2) 기억해야 할 중요한 메서드
addAttribute
: 특정 이름에 대해 값을 설정한다. 쉽게 말해, 저장하고 싶은 값에 별명을 붙인다고 생각하면 된다.
여기서 설정한 별명을 뷰에서 사용한다.
Model addAttribute(String name, Object value)
name에는 별명을, value에는 저장하고 싶은 객체를 작성한다.
(3) Model을 사용하는 프로그램 만들기
뷰 측에서 타임리프를 사용해서 컨트롤러에서 뷰를 표시하는 프로그램을 만들어 보겠다.
01 프로젝트 생성
02 컨트롤러 생성
// src/main/java/com.example.demo/controller.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("hello")
public class HelloModelController {
@GetMapping("model")
public String helloView(Model model){
// Model에 데이터를 저장
model.addAttribute("msg", "타임리프!!!");
//반환값으로 뷰 이름을 돌려줌
return "helloThymeleaf";
}
}
타임 리프를 사용하는 경우 컨트롤러에서 뷰에서 표시할 데이터를 준비해야한다.
이때 사용하는 것이 " Model 인터페이스 "이다.
클라이언트가 URL(http://localhost:8080/hello/model)을 GET메서드로 전송하면,
HelloModelController 클래스의 helloView 메서드가 호출되어 반환값으로 뷰 이름을 돌려주어 그에 맞는 뷰가 표시된다.
03 뷰 생성
<!DOCTYPE html>
<!-- 타임리프 사용 선언-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 추가 -->
<h1 th:text="${msg}">표시되는 부분</h1>
</body>
</html>
04 실행과 확인
controller 클래스에서 msg 값에 "타임리프!!!"를 설정해두었기 때문에
helloThymeleaf.html의 $.{msg}에 "타임리프!!!"가 뜰 수 있었다
6-3 타임리프 사용법
(1) 타임리프란?
: 스피링 부트에서 추천하는 템플릿 엔진이다.! html로 템플릿을 작성한다.
(2) 타임리프 사용법
- 직접 문자를 삽입
<h th text="hello world">표시하는 부분</h1>
- 인라인 처리: 본문에 변수 출력
<h1>안녕하세요! [[${name}]]씨</h1>
- 값 결합
<h1 th:text="'오늘의 날씨는' + '맑음' + '입니다'">표시하는 부분</h1>
- 값 결합(리터럴 치환)
<h1 th:text"|안녕하세요 ${name}씨|">표시하는 부분</h1>
- 지역 변수
<div th:with="a=1, b=2">
<span th:text="|${a} + ${b} = ${a+b}|"></span>
</div>
- 비교와 등가
<span th:text="1 > 10"></span>
<span th:text="1 == 10"></span>
<span th:text="길동 == 길동"></span>
- 조건 연산자
<p th:text="${name} == '길동'? '길동입니다!':'길동이가 아닙니다'"></p>
- 조건 분기(true)
<div th:if="${name} == '길동'">
<p>길동씨입니다</p>
</div>
- 조건 분기(false)
<div th:unless="${name} == '길동'">
<p>길동씨가 아닙니다</p>
</div>
- switch
<div th:swhitch="${name}">
<p th:case="길동" th:text="|${name}입니다!|">길ehd</p>
<p th:case="영희" th:text="|${name}입니다!|">길ehd</p>
<p th:case="철수" th:text="|${name}입니다!|">길ehd</p>
</div>
- 참조(데이터를 결합한 객체)
<p th:text="${mb.name}">name</p>
<p th:text="${mb['name']}">name</p>
- 참조(th:object)
<div th:object="${mb}">
<p th:text="*{name}">name</p>
<p th:text="*{['name']}">name</p>
</div>
- 참조(List)
<p th:text="${list[0]}">name</p>
<p th:text="${list[1]}">name</p>
<p th:text="${list[2]}">name</p>
- 참조(Map)
<p th:text="${map.kim.name]}">name</p>
<p th:text="${map['lee']['name']}">name</p>
- 반복
<div th:each="member:*${members}">
<p>[[${member.id}]] : [[${member.name}]]</p>
</div>
- 반복 상태
상태 | 설명 | 예시 |
index | 0부터 시작하는 값 | th:text="${userStat.index |
count | 1부터 시작하는 값 | th:text="${userStat.count} |
size | 전체 사이즈 | th:text="${userStat.size} |
even, odd | 홀수, 짝수 여부 | th:text="${userStat.even} th:text="${userStat.odd} |
first,last | 처음, 마지막 여부 | h:text="${userStat.first} th:text="${userStat.last} |
current | 현재 객체 | th:tect="%userStat.current" |
- 유틸리티 객체
아래는 thymeleaf 공식 사이트에서 제공하는 유틸리티 객체 목록이다.
- #execInfo: information about the template being processed.
- #messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
- #uris: methods for escaping parts of URLs/URIs
- #conversions: methods for executing the configured conversion service (if any).
- #dates: methods for java.util.Date objects: formatting, component extraction, etc.
- #calendars: analogous to #dates, but for java.util.Calendar objects.
- #numbers: methods for formatting numeric objects.
- #strings: methods for String objects: contains, startsWith, prepending/appending, etc.
- #objects: methods for objects in general.
- #bools: methods for boolean evaluation.
- #arrays: methods for arrays.
- #lists: methods for lists.
- #sets: methods for sets.
- #maps: methods for maps.
- #aggregates: methods for creating aggregates on arrays or collections.
- #ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
- 다른 템플릿 삽입하기
frageent화: 여러 템플릿에서 같은 내용이 사용되는 경우 공통 내용을 별개의 파일로 만들고 필요한 부분에 추가하는 것을 뜻한다.
<!--fragment.html 프래그먼트를 정의-->
<span th:fragment="one">하나</span>
<span th:fragment="two">둘</span>
<!--useThymeleaf.html 프래그먼트 사용하기-->
<div id="one" th:insert="fragment :: one"></div>
<div id="two" th: replace="fragment :: two"></div>
insert의 경우 삽입되는 useThymeleaf.html의 태그 안에 삽입 대상 fragment.html 의 태그의 내용이 삽입된다.
replace의 경우 삽입 대상 fragment.html 의 태그 전체로 대체된다.
- 레이아웃
layout화: 여러 템플릿에서 같은 디자인 레이아웃을 사용하는 경우 공통 레이아웃을 만들고 공유하는 것을 말한다.
레이아웃을 이용하려면 전용 라이브러리인 thymeleaf-layout-dialect가 필요하다.
공통 레이아웃을 Decorator라고 하고,
공통 레이아웃을 사용하는 측을 Fragment라고 정의한다.
6-4 타임리프를 사용해서 프로그램 만들기
(1) 타임리프를 사용해서 프로그램 만들기
이번 장에서는 6-3에서 배운 타임리프를 활용해보겠다!!
01 프로젝트 생성
02 컨트롤러와 뷰 생성
03 직접 문자를 삽입하여 값 결합 만들기
// ThymeleafController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ThymeleafController {
@GetMapping("show")
//요청 핸들러 메서드
public String showView(Model model){
// Model에 데이터 추가
model.addAttribute("name", "이순신");
// 반환값으로 뷰 이름을 설정
return"useThymeleaf";
}
}
// http://localhost:8080/show가 GET 메서드로 송신되면 뷰이름을 반환해줌
<!--useThymeleaf.html-->
<!DOCTYPE html>
<!--타임리프 사용 선언-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf Sample</title>
</head>
<body>
<!--직접 문자를 삽입-->
<h1 th:text="'hello world'">표시하는 부분</h1>
<!--인라인 처리-->
<h1>안녕하세요! [[${name}]]</h1>
<!--값 결합-->
<h1 th:text="'오늘의' + '날씨는' + '맑음'">표시하는 부분</h1>
</body>
</html>
위의 코드에서 작은 따옴표를 안에 안써줘서 계속 에러가 났었다..ㅎ
04 값결합에서의 비교와 등가
뷰에 아래의 코드를 추가해주었다.
<!--값결합(리터럴 치환)-->
<h1 th:text="|안녕하세요 ${name}씨|">표시하는 부분</h1>
<!--지역 변수-->
<div th:with="a=1, b=2">
<span th:text="|${a} + ${b} = ${a+b}|"></span>
</div>
<!--비교와 등가-->
<span th:text="길동 == 길동"></span>
05 조건 연산자를 이용한 조건 분기
뷰에 아래의 코드를 추가해주었다.
<!--조건 연산자-->
<p th:text="${name} == '이순신'?'이순신입니다':'이순신이 아닙니다'"></p>
<!--조건 분기(true)-->
<div th:if="${name} == '이순신'">
<p>이순신씨입니다!</p>
</div>
<!--조건 분기(false)-->
<div th:unless="${name} == '영희'">
<p>영희씨가 아닙니다.</p>
</div>
06 switch에서 th:object 만들기
엔티티 생성 -> 컨트롤러에 추가 -> 뷰에 추가
우선 com.example.demo.entity 패키지를 생성해 아래와 같이 Member 클래스를 만들어주겠다.
//Member.java
package com.example.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data //getter, setter 생성
@AllArgsConstructor //필드에 대한 초깃값을 인수로 받는 생성자
public class Member {
private Integer id;
private String name;
}
컨트롤러의 요청 핸들러메서드에 아래와 같이 코드를 추가해주겠다.
@GetMapping("show")
//요청 핸들러 메서드
public String showView(Model model){
//Member를 생성
Member member = new Member(1,"회원01");
// Model에 데이터 추가
model.addAttribute("name", "이순신");
model.addAttribute("mb", member);
// 반환값으로 뷰 이름을 설정
return"useThymeleaf";
}
뷰에 아래와 같이 코드를 추가해주겠다.
<hr>
<!--switch-->
<div th:switch="${name}">
<p th:case="이순신" th:text="|${name}입니다!|"></p>
<p th:case="영희" th:text="|${name}입니다!|"></p>
<p th:case="*">명부에 없습니다!</p>
</div>
<!--참조(데이터를 결합한 객체)-->
.으로 접근: <span th:text="${mb.id}">ID</span> - <span th:text="${mb.name}">이름</span>
[]으로 접근: <span th:text="${mb['id']}">ID</span> - <span th:text="${mb['name']}">이름</span>
<!--참조(th:object)-->
<th:block th:object="${mb}">
.으로 접근: <span th:text="*{id}">ID</span> - <span th:text="*{name}">이름</span>
[]으로 접근: <span th:text="*{id}">ID</span> - <span th:text="*{['name']}">이름</span>
</th:block>
07 리스트 반복하기
Map 타입: 리스트나 배열처럼 순차적으로(sequential) 해당 요소 값을 구하지 않고 key를 통해 value를 얻는다.
컨트롤러의 요청 핸들러 메서드에 아래와 같이 코드를 추가한다.
방향에 대한 리스트와, 멤버에 대한 맵, 멤버에 대한 리스트를 만들어주었다.
@GetMapping("show")
//요청 핸들러 메서드
public String showView(Model model){
//Member를 생성
Member member = new Member(1,"회원01");
//List 생성
List<String> directionList = new ArrayList<String>();
directionList.add("동");
directionList.add("서");
directionList.add("남");
directionList.add("북");
//컬렉션 저장용 Member 생성
Member member1 = new Member(10, "홍길동");
Member member2 = new Member(20,"이영희");
//Map을 생성해서 Member를 저장
Map<String, Member> memberMap = new HashMap<>();
memberMap.put("hong", member1);
memberMap.put("lee", 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"useThymeleaf";
}
뷰는 아래와 같이 코드를 추가해주었다.
<hr>
<!--참조(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>
<!--참조(Map)-->
.으로 접근: <span th:text="${map.hong.name}">이름1</span>
[]으로 접근: <span th:text="${map['lee']['name']}">이름2</span>
<!--반복-->
<div th:each="member : ${members}">
<p>[[${members.id}]] : [[${members.name}]]</p>
</div>
08 반복 상태에서 유틸리티 객체 만들기
뷰에 아래와 같은 코드를 추가해주었다.
<hr>
<!--반복 상태-->
<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>
<!--유틸리티 객체-->
<div th:with="x=1000000">
정수 형식:
<span th:text="${#numbers.formatInteger(x, 3, 'COMMA')}"></span>
</div>
나는 반복 상태 부분이 살짝 어려웠다..ㅎㅎ
여기서 "member"는 무엇일까!!!의 고민이 길어졌다.
member는 현재 반복문이 돌고 있는 리스트의 한 가지 "요소"를 뜻한다!!!
따라서 object에 member 값이 들어간 것이다!
그래서 " * "를 사용했을 때 한가지 그 요소에 대한 정보가 나오는 것이다!
09 다른 템플릿 포함시키기
우선 templates 패키지 아래에 프래그먼트를 생성해주었다.
<!--fragment.html-->
<!DOCTYPE html>
<!--타임리프 사용 선언-->
<html xmlns:th="http://www.thymeleaf.org"><head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--프래그먼트를 정의-->
<span th:fragment="one">하나</span>
<span th:fragment="two">둘</span>
<span th:fragment="three">셋</span>
</body>
</html>
그리고 뷰에 아래와 같이 코드를 추가해주었다.
<!--프래그먼트 사용하기-->
<div id ="one" th:insert="fragment :: one"></div>
<div id ="three" th:replace="fragment :: three"></div>
10 레이아웃 생성
Decorator 생성 -> fragment 생성 -> 컨트롤러에 추가
우선 templates 아래에 새로운 경로인 commons폴더를 만들고 이 아래에 layout.html 파일을 생성한다.
<!DOCTYPE html>
<!--타임리프 사용 선언-->
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title>베이스: 레이아웃</title>
</head>
<body>
<!--공통 머리글-->
<header>
<div align="center">
<h1>공통 머리글</h1>
</div>
<hr />
</header>
<!--대체 위치-->
<div layout:fagment="content" align="center">
여기를 대체함
</div>
<!--공통 바닥글-->
<footer>
<hr />
<div align="center">
<h1>공통 바닥글</h1>
</div>
</footer>
</body>
</html>
그 뒤 templates 파일 아래 pageA.html 파일을 만든다.
<!DOCTYPE html>
<!--타임리프 사용 선언-->
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{commons/layout}">
<head>
<meta charset="UTF-8">
<title>페이지</title>
</head>
<body>
<!--대체되는 내용-->
<div layout:fragment="content">
<h1>PageA</h1>
</div>
</body>
</html>
pageA.html을 띄우기 위한 요청 핸들러 메서드를 컨트롤러에 추가한다.
@GetMapping("a")
public String showA(){
return "pageA";
}
결과 http:localhost:8080/a에 접속하면 ThymeleafController 클래스의 showA 메서드가 호출되어 pageA.html이 표시된다.
지금 책의 절반을 공부했다!
생각보다 재밌다!!
뭔가 node.js랑 비슷하면서 다른 느낌! 노드를 했어서 그런가 막 어렵지 않다.
그리고 노드는 공부를 안하고 프로젝트를 진행하면서 공부했어서 그런가 스프링은 기초부터 탄탄하게 공부하는 중이라 기반이 잘 다져지는 느낌ㅎㅎ
⭐이번 절에서는
컨트롤러 --(값 표시)--> 뷰
이었다면 다음절에서는 뷰에서 입력한 값을 서버에 보내고 컨트롤러가 받는 방법에 대해 공부해보겠다!!
뷰 --(값 전달) --> 컨트롤러
'Back-end > Spring' 카테고리의 다른 글
[Spring] chapter08 유효성 검사 기능 알아보기 (0) | 2023.08.01 |
---|---|
[Spring] chapter07 요청 파라미터 취득하기 (0) | 2023.07.30 |
[Spring] chapter05 MVC 모델 알아보기 (0) | 2023.07.22 |
[Spring] chapter04 데이터베이스 작업 (0) | 2023.07.21 |
[Spring] chapter03 스프링 프레임워크의 핵심 기능 알아보기 (0) | 2023.07.13 |