[스프링 웹 MVC 2편] 001. 타임리프 기본기능
타임 리프의 특징
- 서버 사이드 렌더링 (SSR 지원) - Client가 아닌 Server에서 HTML을 동적으로 생성하여 Client에 전달
- 네츄럴 템플릿
- JSP의 경우 Chrome 개발자 도구에서 소스를 보면 개발자가 알기 어렵게 렌더링되어 출력되는 반면에
Spring Thymeleaf의 경우 HTML 소스가 Plain하게 짜인듯이 소스를 볼 수 있음.
즉, 순수 HTML을 유지하면서도 뷰 템플릿을 사용할 수 있는 특징을 칭함.
1. 타임리프의 기본 특징
타임리프를 사용하려면 HTML 코드 위에 다음과 같이 명시해야 함
<html xmlns:th="http://www.thymeleaf.org">
2. 타임리프의 기본 표현식
1. 텍스트 : text, utext
text와 utext를 구분하는 가장 큰 차이는 Escape 처리가 되느냐 마느냐의 차이이다.
예를 들어 <b>
는 기본적으로 html에서 bold처리를 위한 태그에 해당하는데 이 태그를 html 상에서 그대로 클라이언트 브라우저에 출력하고 싶다고 하면 어떻게 해야할지 난감해지는데 이때 사용하는 것이 unescape에 해당한다.
따라서 unescape의 맨 앞자를 딴 utext
를 사용하면 그 태그 내에 있는 문자들은 태그로 인식되지 않는다
2. 변수 - SpringEL
타임리프에서 변수를 사용하는 경우 변수 표현식을 활용해야한다.
${...}
해당 변수 표현에는 스프링 EL 이라는 스프링이 제공하는 표현식을 사용할 수 있다.
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()
: 리스트에서 첫 번째 회원을 찾고 메서드를 직접 호출함.
Map
userMap['userA'].username
: Map에서 UserA를 찾고 username 프로퍼티 접근
-> map.get("userA").getUsername()
userMap['userA']['username']
: 위와 같은 방식
userMap['userA'].getUsername
: Map에서 userA를 찾고 메서드를 직접 호출함.
3. 스프링 기본 객체
타임리프는 기본 객체들을 제공한다.
${#request}
${#response}
${#session}
${#servletContext}
${#locale}
결과는 다음과 같이 나타난다.
이 때, request의 경우 HttpServletRequest 객체가 그대로 제공되기 때문에
타임 리프는 기본적으로 http요청 파라미터 접근에 대해 param
편의 객체와
HTTP 세션 접근을 위한 session
객체를 제공한다.
용례는 다음과 같다
${param.paramData}
${session.sessionData}
스프링 빈에 직접 접근하려면
${helloBean.hello('Spring!')}
과 같이 사용하자.
5. 유틸리티 객체와 날짜
#message // 메세지 국제화 처리
#uris // URI 이스케이프 지원
#dates // java.util.date 지원
#calendars // java.util.calendar 지원
#temporals // 자바8 날짜 서식 지원
#numbers // 숫자 서식 지원
#strings // 문자 관련 편의기능
#objects // 객체 관련 기능 제공
#bools // boolean 관련 기능 제공
#arrays // 배열 관련 기능 제공
#lists, #sets, #maps // 컬렉션 관련 기능 제공
#ids // 아이디 처리 관련 기능 제공
사용 용례는 다음과 같다
@GetMapping("/date")
public String date(Model model){
model.addAttribute("locatDateTime", LocalDateTime.now());
return "basic/date";
}
<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>
URL 링크
타임리프에서 url을 생성하는 경우 @{...}
문법을 사용한다.
<h1>URL 링크</h1>
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello queryparam</a></li>
<!-- /hello(param1=${param1}, param2=${param2}) 쿼리 파라미터로 처리 -->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">
Path Variable
</a></li>
<!-- @{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}
가 /hello/data1/data2 이렇게 처리되는 식-->
<!-- URL 경로상에 변수가 생기는 경우 () 부분 경로 변수로 처리됨 (쿼리 파라미터 X) -->
<li><a th:href="@{/hello/{param1}(param1 = ${param1}, param2 = ${param2})}">
Path Variable + Query Parameter
</a></li>
<!-- 다음과 같이 혼용도 가능하다 URL 경로에 변수가 없는데 ()에 없으면 쿼리 파라미터로 처리 -->
</ul>
리터럴
정의 : 코드 상에 고정된 값을 말하는 용어
- 타임리프에서 문자 리터럴은 항상 ' (작은 따옴표)로 감싸야 함. (공백 없으면 안감싸도 됨)
<span th:text="Hello World"></span>
<!-- 위 처럼 작성하면 오류 -->
<!-- 아래가 정상 -->
<span th:text="'Hello World!'"></span>
- 다음과 같이 대체 문법을 사용하면 템플릿을 사용하는 것 사용 가능
<span th:text="|hello ${data}|">
연산
비교 연산 : HTML 엔티티를 사용해야 할 경우 주의
> (gt)
< (lt)
>= (ge)
<= (le)
! (not)
== (eq)
!= (not eq)
조건식의 경우 자바와 유사
no operation _ 을 사용하면 타임리프가 실행되지 않는 것 처럼 동작한다.
속성 값 설정
th:*
속성을 설정하는 경우 타임리프는 기존 속성을 th:*
로 지정한 속성으로 대체한다.
기존 속성이 없다면 새로 만든다
<input type="text" name="mock" th:name="userA"/>
-> 렌더링 하면 <input type="text" th:name="userA"/>
속성 추가
th:attrappend : 속성의 값의 뒤에 값을 추가
th:attrprepend : 속성의 값의 앞에 값을 추가
th:classappend : 클래스 속성에 자연스럽게 추가한다
Checked 처리
<input type="checkbox" name="active" checked="false"/>
-> 이 경우에도 checked 속성이 있기 때문에 checked 처리가 되어버림
HTML에서는 속성의 값과 관계없이 그 속성이 있으면 체크가 된다.
TRUE FALSE로 구분하기 어렵기 때문에 불편
th:checked
는 값이 false인 경우 checked 속성 자체를 제거한다.
반복
타임리프에서 반복은 th:each
를 사용한다.
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">username</td>
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
<td>
index = <span th:text="${userStat.index}"></span>
count = <span th:text="${userStat.count}"></span>
size = <span th:text="${userStat.size}"></span>
even? = <span th:text="${userStat.even}"></span>
odd? = <span th:text="${userStat.odd}"></span>
first? = <span th:text="${userStat.first}"></span>
last? = <span th:text="${userStat.last}"></span>
current = <span th:text="${userStat.current}"></span>
</td>
</tr>
<tr th:each="user : ${users}">
반복시 오른쪽 컬렉션의 값을 하나씩 꺼내서 왼쪽 변수에 담아서 태그를 반복함
이 태그는 리스트 뿐 아니라 Iterable Enumeration을 구현한 모든 객체를 반복에 사용할 수 있음.
<tr th:each="user, userStat : ${users}">
다음과 같이 두번째 파라미터의 경우 반복의 상태를 확인할 수 있는데
이때 생략하게 되는 경우 지정한 변수명 + Stat이 된다.
반복 상태 유지 기능
index : 0부터 시작하는 값
count : 1부터 시작하는 값
size : 전체 사이즈
even , odd : 홀수, 짝수 여부( boolean )
first , last :처음, 마지막 여부( boolean )
current : 현재 객체