스프링 공부 (인프런 김영한 선생님)/스프링 MVC 2편

[스프링 웹 MVC 2편] 001. 타임리프 기본기능

ProgYun. 2023. 8. 5. 19:14

타임 리프의 특징

  1. 서버 사이드 렌더링 (SSR 지원) - Client가 아닌 Server에서 HTML을 동적으로 생성하여 Client에 전달
  2. 네츄럴 템플릿
  • 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>

리터럴

정의 : 코드 상에 고정된 값을 말하는 용어

  1. 타임리프에서 문자 리터럴은 항상 ' (작은 따옴표)로 감싸야 함. (공백 없으면 안감싸도 됨)
<span th:text="Hello World"></span>
<!-- 위 처럼 작성하면 오류 -->
<!-- 아래가 정상 -->
<span th:text="'Hello World!'"></span>
  1. 다음과 같이 대체 문법을 사용하면 템플릿을 사용하는 것 사용 가능
    <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 : 현재 객체