Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch7
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch8
- jpa 활용2 - api 개발 고급
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch4
- 2024 정보처리기사 수제비 실기
- 스프링 db2 - 데이터 접근 기술
- 스프링 db1 - 스프링과 문제 해결
- 타임리프 - 기본기능
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch3
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch11
- 자바의 정석 기초편 ch9
- 코드로 시작하는 자바 첫걸음
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch13
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch5
- @Aspect
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch6
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch14
- 스프링 입문(무료)
- 게시글 목록 api
Archives
- Today
- Total
나구리의 개발공부기록
타임리프 - 기본기능, 프로젝트 생성 및 타임리프소개, 텍스트(text/utext), 변수(SpringEL), 기본 객체들, 유틸리티 객체와 날짜 본문
인프런 - 스프링 완전정복 코스 로드맵/스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술
타임리프 - 기본기능, 프로젝트 생성 및 타임리프소개, 텍스트(text/utext), 변수(SpringEL), 기본 객체들, 유틸리티 객체와 날짜
소소한나구리 2024. 5. 15. 16:12 출처 : 인프런 - 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 프로젝트 생성 및 타임리프 소개
1) 프로젝트 생성
(1) Project
- Project : Gradle - Groovy Project
- Language : Java
- Spring Boot : 3.2.3
(2) Project Metadata
- Group : hello
- Artifact, Name : thymeleaf-basic
- Package Name : hello.thymeleaf
- Packaging : jar
- java : 21 or 17
(3) Dependencies
- Spring Web
- Lombok
- Thymeleaf
(4) index.html 생성은 생략
2) 타임리프 소개
- 공식 사이트: https://www.thymeleaf.org/
- 공식 메뉴얼 - 기본 기능: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
- 공식 메뉴얼 - 스프링 통합: https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
(1) 타임리프 특징
- 서버 사이드 HTML 렌더링 (SSR): 백엔드 서버에서 HTML을 동적으로 렌더링 하는 용도로 사용
- 네츄럴 템플릿
- 타임 리프는 순수 HTML을 최대한 유지하는 특징이 있음
- 타임리프로 작성한 HTML 파일은 웹 브라우저에서 파일을 직접 열어도 내용을 확인할 수 있고 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있음
- 이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 네츄럴 템플릿(natural templates)라고 함
- JSP를 포함한 다른 뷰 템플릿들은 해당 파일을 열면 정상적인 HTML 결과를 확인할 수 없고(JSP 등) 서버를 통해 렌더링 되고 HTML 응답 결과를 받아야 화면을 확인할 수 있음 - 스프링 통합 지원: 타임리프는 스프링과 자연스럽게 통합되고 스프링의 다양한 기능을 편리하게 사용할 수 있는데, 이부분은 뒤에서 자세히 다룸
(2) 기본 표현식
- 간단한 표현
- 변수 표현식: ${...}
- 선택 변수 표현식: *{...}
- 메시지 표현식: #{...}
- 링크 URL 표현식: @{...}
- 조각 표현식: ~{...} - 리터럴
- 텍스트: 'one text', 'Another one!', ...
- 숫자: 0, 34, 3.0, 12.3, ...
- 불린: true, false
- 널: null
- 리터럴 토큰: one, sometext, main, ... - 문자연산
- 문자 합치기: +
- 리터럴 대체: |The name is ${name}| - 산술연산
- Binary operators: +, -, *, /, %
- Minus sign (unary operator): - - 불린연산
- Binary operators: and, or
- Boolean negation (unary operator): !, not - 비교와 동등
- 비교:>,<,>=,<=(gt, lt, ge, le)
- 동등 연산: ==, != (eq, ne) - 조건연산
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue) - 특별한 토큰
- No-Operation: _
(3) 타임리프 사용 선언
<html xmlns:th="http://www.thymeleaf.org">
2. 텍스트 - text, utext
1) text, utext
(1) 데이터 출력
- 타임리프는 기본적으로 HTML 태그의 속성에 기능을 정의해서 동작함
- HTML의 콘텐츠에 데이터를 출력할 때는 th:text 사용하면 됨
- HTML 태그의 속성이 아니라 HTML 콘텐츠 영역 안에서 직접 데이터를 출력하고 싶다면 [[...]] 사용하면 됨
<span th:text="${data}">
컨텐츠 안에서 직접 출력하기 = [[${data}]]
(2) BasicController
- basic 패키지를 생성 후 작성
package hello.thymeleaf.basic;
@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("/text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "Hello Spring");
return "basic/text-basic";
}
}
(3) text-basic.html
- /resources/templates/basic 경로에 작성
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
<li>th:text 사용 <span th:text="${data}"></span> </li>
<li>컨텐츠 안에서 직접 출력 하기 = [[${data}]]</li>
</ul>
</body>
</html>
(4) Escape
- HTML문서는 <,> 같은 특수 문자를 기반으로 정의되므로 뷰 템플릿으로 HTML화면을 생성할 때는 출력하는 데이터에 특수 문자를 주의해서 사용해야 함
- 만일 위 컨트롤러에 hello Spring 이렇게 볼드 처리를 하고 싶어서 "Hello <b>Spring!</b>" 입력하면 웹브라우저에 Hello <b>Spring!</b>가 그대로 출력되고, 소스보기를 하면 Hello <b>Spring!</b> 이렇게 나옴
- < 부분이 < 으로 > 부분이 > 이런식으로 변경된 것을 확인할 수 있음
(5) HTML 엔터티
- 웹 브라우저는 <를 HTML 태그의 시작으로 인식하기 때문에 <를 태그의 시작이 아니라 문자로 표현 할 수 있는 방법을 HTML 엔터티라고 함
- HTML에서 사용하는 특수 문자를 HTML 엔터티로 변경하는 것을 이스케이프(escape)라고 하며 타임리프가 제공하는 th:text, [[...]]는 기본적으로 이스케이프를 제공함
- 수많은 HTML 엔터티가 있으므로 궁금한 사항은 인터넷 검색 해볼 것
(6) Unescape
- 이스케이프를 사용하지 않고자 할 때는 타임리프가 제공하는 th:utext와 [(...)] 를 사용하면 됨
(7) BasicController - textUnescaped() 추가
@GetMapping("text-unescaped")
public String textUnescaped(Model model) {
model.addAttribute("data", "Hello <b>Spring!</b>"); // 스프링에 볼드 처리
return "basic/text-unescaped";
}
(8) text-unescaped.html
- th:inline="none": [[...]]을 타임리프가 해석하지말고 그대로 출력하라는 뜻
- 실행해보면 th:utext와 [(...)]을 사용한 곳은 볼드가 적용되어 의도하고자 했던 Hello Spring! 으로 출력되고 소스보기를 하면 <b>Hello <b>Spring!</b>로 나옴
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>text vs utext</h1>
<ul>
<li>th:text <span th:text="${data}"></span></li>
<li>th:utext <span th:utext="${data}"></span></li>
</ul>
<h1><span th:inline="none">[[...]] vs [(...)]</span></h1>
<ul>
<li><span th:inline="none">[[...]] = </span>[[${data}]]</li>
<li><span th:inline="none">[(...)] = </span>[(${data})]</li>
</ul>
</body>
</html>
** 주의!
- 실제 서비스 개발하다보면 escape를 사용하지 않아서 HTML이 정상 렌더링이 되지 않는 수많은 문제가 발생함
- 예를들어 게시판의 경우 사용자가 < , > , / 등의 다양한 특수기호들을 써버릴 텐데 escape가 되지 않는다면 HTML이 모두 깨지게 됨
- escape를 기본으로 하고 꼭 필요한 곳에만 unescape를 사용
3. 변수 - SpringEL
1) SpringEL
(1) 변수 표현식
- 타임리프 변수 표현식: ${...}
- 이 변수 표현식에는 스프링 EL이라는 스프링이 제공하는 표현식을 사용할 수 있음
(2) BasicController - variable() 추가
@GetMapping("/variable")
public String variable(Model model) {
User userA = new User("userA", 10);
User userB = new User("userB", 20);
List<User> list = new ArrayList<>();
list.add(userA);
list.add(userB);
Map<String, User> map = new HashMap<>();
map.put("userA", userA);
map.put("userB", userB);
model.addAttribute("user", userA);
model.addAttribute("users", list);
model.addAttribute("userMap", map);
return "basic/variable";
}
@Data
static class User {
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
(3-1) variable.html
- basic 경로에 생성
- SpringEL 표현식으로 인스턴스 변수와 콜렉션에 담긴 값을 프로퍼티 접근으로 꺼낼 수 있음
<!DOCTYPE html>
<!-- 타임리프 선언 및 head태그 -->
<body>
<h1>SpringEL 표현식</h1>
<!-- user의 username을 프로퍼티 접근 -> user.getUsername() -->
<ul>Object
<li>${user.username} = <span th:text="${user.username}"></span></li>
<li>${user['username']} = <span th:text="${user['username']}"></span></li>
<li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List <!-- List에서 접근 -->
<li>${users[0].username} = <span th:text="${users[0].username}"></span></li>
<li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span></li>
<li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map <!-- Map에서 접근 -->
<li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
<li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
<li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
</body>
</html>
(3-2) SpringEL 다양한 표현식
- Object
- user.username: user의 username을 프로퍼티 접근, user.getUsername()
- user['username']: 위와 같음
- user.getUsername(): user의 getUsername()을 직접 호출 - List
- users[0].username: List에서 첫 번째 회원을 찾고 username을 프로퍼티 접근, list.get(0).getUsername()
- users[0]['username']: 위와 같음
- users[0].getUsername(): List에서 첫 번째 회원을 찾고 메서드를 직접 호출 - Map
- userMap['userA'].username: Map에서 userA를 찾고 username을 프로퍼티 접근, map.get("userA").getUsername()
- userMap['userA']['username']: 위와 같음
- userMap['userA'].getUsername(): Map에서 userA를 찾고 메서드를 직접 호출
(3-3) 지역 변수 선언 - variable.html 하단에 추가
- th:with를 사용하면 지역 변수를 선언해서 사용할 수 있는데, 지역 변수는 선언한 태그 안에서만 사용할 수 있음
<!-- `th:with` 를 사용하면 지역 변수를 선언해서 사용할 수 있음, 지역 변수는 선언한 태그 안에서만 사용 가능. -->
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}"> <!-- users[0] 이 userA가 되고 그 값이 first에 들어감-->
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
4. 기본 객체들
1) 타임리프가 제공하는 기본 객체들
(1) 기본 객체
- ${#locale}
**주의! - 아래부터는 스프링 부트 3.0 부터 제공하지 않음
- ${#request}
- ${#response}
- ${#session}
- ${#servletContext}
- 만약 스프링 부트 3.0 이상에 사용하면 java.lang.IllegalArgumentException 에러가 발생함
- 사용하고자 하면 model에 해당 객체를 직접 추가해서 사용해야함
(2) request - 편의 객체 제공
- request(#request 포함)는 HttpServletRequest 객체가 그대로 제공되기 때문에 데이터를 조회하려면 request.getParameter("data") 처럼 불편하게 접근해야 하는데, 이런 점을 해결하기 위해 편의 객체를 제공함
- HTTP 요청 파라미터 접근: param
- ex) ${param.paramData} - HTTP 세션 접근: session
- ex) ${session.sessionData} - 스프링 빈 접근: @
- ex) ${@helloBean.hello('Spring!')}
(2) BasicController - basicObjects 추가
- 스프링 부트 3.0 이상은 model에 직접 객체를 추가해야함
- 스프링 부트 3,0 미만은 타임리프가 기본으로 객체를 지원하므로 model에 추가하지 않아도 사용할 수 있었음
- @Component로 helloBean이라는 객체를 스프링 빈으로 등록
// 스프링 부트 3.0 이상 -> model에 직접 객체를 추가
@GetMapping("/basic-objects")
public String basicObjects(Model model, HttpServletRequest request,
HttpServletResponse response, HttpSession session) {
session.setAttribute("sessionData", "Hello Session");
model.addAttribute("request", request);
model.addAttribute("response", response);
model.addAttribute("servletContext", request.getServletContext());
return "basic/basic-objects";
}
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello " + data;
}
// 스프링 부트 3.0 미만
public String basicObjects(HttpSession session) {
session.setAttribute("sessionData", "Hello Session");
return "basic/basic-objects";
}
(3) basic-objects.html
- 스프링 부트 3.0 미만에서는 타임리프가 기본으로 제공하는 객체를 #으로 접근할 수 있으나 스프링 부트 3.0에서는 #locale만 제공하므로 나머지는 #이 없이 model에 담은 attributeName으로 접근해야함
- request편의 객체를 이용하여 요청 파라미터, HTTP 세션, 스프링빈의 데이터에 편리하게 접근할 수 있음
<!DOCTYPE html>
<!-- 타임리프 선언 및 head태그 -->
<body>
<h1>식 기본 객체(Expression Basic Object)</h1>
<!-- 스프링 부트 3.0 이상은 #locale만 기본으로 제공 됨 -->
<ui>
<li>request = <span th:text="${request}"></span></li>
<li>response = <span th:text="${response}"></span></li>
<li>session = <span th:text="${session}"></span></li>
<li>servletContext = <span th:text="${servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ui>
<!-- 스프링 부트 3.0 미만에서는 #으로 객체가 기본으로 제공 됨
<ui>
<li>request = <span th:text="${#request}"></span></li>
<li>response = <span th:text="${#response}"></span></li>
<li>session = <span th:text="${#session}"></span></li>
<li>servletContext = <span th:text="${#servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ui>
-->
<h1>편의 객체</h1>
<ui>
<li>Request Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ui>
</body>
</html>
5. 유틸리티 객체와 날짜
1) 타임리프 유틸리티 객체들
(1) 다양한 유틸리티 객체들
- #message : 메시지, 국제화 처리
- #uris : URI 이스케이프 지원
- #dates : java.util.Date 서식 지원
- #calendars : java.util.Calendar 서식 지원
- #temporals : 자바8 날짜 서식 지원
- #numbers : 숫자 서식 지원
- #strings : 문자 관련 편의 기능
- #objects : 객체 관련 기능 제공
- #bools : boolean 관련 기능 제공
- #arrays : 배열 관련 기능 제공
- #lists, #sets, #maps : 컬렉션 관련 기능 제공
- #ids : 아이디 처리 관련 기능 제공
(4-1) 자바8 날짜
- 다른건 매뉴얼이 있지만 자바8 날짜에 대한 매뉴얼이 없어서 작성
- 타임리프에서 자바8 날짜인 LocalDate, LocalDateTime, Instant를 사용하려면 추가 라이브러리가 필요함
- 스프링 부트에서 타임리프를 추가하여 사용하면 해당 라이브러리(thymeleaf-extras-java8time)가 자동으로 추가되고 통합됨
(4-2) BasicController - data() 추가
@GetMapping("/date")
public String date(Model model) {
model.addAttribute("localDateTime", LocalDateTime.now());
return "basic/date";
}
(4-3) date.html
<!DOCTYPE html>
<!-- 타임리프 선언 및 head태그 -->
<body>
<h1>LocalDateTime</h1>
<ul>
<li>default = <span th:text="${localDateTime}"></span></li>
<!-- 포맷팅도 가능 -->
<li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime,'yyyy-MM-dd HH:mm:ss')}"></span></li>
</ul>
<h1>LocalDateTime - Utils</h1>
<ul>
<li>${#temporals.day(localDateTime)} = <span th:text="${#temporals.day(localDateTime)}"></span></li>
<li>${#temporals.month(localDateTime)} = <span th:text="${#temporals.month(localDateTime)}"></span></li>
<li>${#temporals.monthName(localDateTime)} = <span th:text="${#temporals.monthName(localDateTime)}"></span></li>
<li>${#temporals.monthNameShort(localDateTime)} = <span th:text="${#temporals.monthNameShort(localDateTime)}"></span></li>
<li>${#temporals.year(localDateTime)} = <span th:text="${#temporals.year(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeek(localDateTime)} = <span th:text="${#temporals.dayOfWeek(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeekName(localDateTime)} = <span th:text="${#temporals.dayOfWeekName(localDateTime)}"></span></li>
<li>${#temporals.dayOfWeekNameShort(localDateTime)} = <span th:text="${#temporals.dayOfWeekNameShort(localDateTime)}"></span></li>
<li>${#temporals.hour(localDateTime)} = <span th:text="${#temporals.hour(localDateTime)}"></span></li>
<li>${#temporals.minute(localDateTime)} = <span th:text="${#temporals.minute(localDateTime)}"></span></li>
<li>${#temporals.second(localDateTime)} = <span th:text="${#temporals.second(localDateTime)}"></span></li>
<li>${#temporals.nanosecond(localDateTime)} = <span th:text="${#temporals.nanosecond(localDateTime)}"></span></li>
</ul>
</body>
</html>
'인프런 - 스프링 완전정복 코스 로드맵 > 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술' 카테고리의 다른 글
메시지 및 국제화, 프로젝트 설정 및 소개, 스프링 메시지 소스 설정, 스프링 메시지 소스 사용, 웹 애플리케이션에 메시지 적용하기, 웹 애플리케이션에 국제화 적용 (3) | 2024.09.03 |
---|---|
타임리프 - 스프링 통합과 폼, 체크박스 단일, 체크박스 멀티, 라디오 버튼, 셀렉트 박스 (1) | 2024.06.16 |
타임리프 - 스프링 통합과 폼, 프로젝트 설정, 타임리프 스프링 통합, 입력 폼 처리, 요구사항 추가 (0) | 2024.05.27 |
타임리프 - 기본기능, 주석, 블록, 자바스크립트 인라인, 템플릿 조각, 템플릿 레이아웃 (0) | 2024.05.19 |
타임리프 - 기본기능, URL링크, 리터럴, 연산, 속성 값 설정, 반복, 조건부 평가 (1) | 2024.05.15 |