해당 포스팅은 타임 리프 공식 문서를 참고하여 만든 포스팅입니다.
타임리프 준비
타임리프 템플릿을 사용하기 위해서 가장 먼저 아래의 코드를 꼭 넣어줘야 한다.
<html xmlns:th="http://www.thymeleaf.org">
기본 출력 th:text && utext
가장 간단한 방법이다. model의 key로 값을 가져와서 아래와 같이 랜더링할 수 있다.
만약, 태그 속성 없이 출력하고 싶을 땐 "[[...]]"와 같이 사용하면 된다.
<ul>
<li><span th:text="|안녕하세요 ${name}님|"></span></li>
<li>안녕하세요 [[${name}]]님</li>
</ul>
하지만 만약 "<b>guest</b>와 같은 HTTP Entity가 포함되어 있는 문자열을 넘길 때 기본적으로 Escape 처리가 되어서 태그는 속성으로 인정되지 않는다. 이런 경우 utext를 이용하면 된다.
<ul>
<li><span th:utext="|안녕하세요 ${name}님|"></span></li>
<li>안녕하세요 [(${name})]님</li>
</ul>
주의
실제 서비스를 개발하다 보면 이스케이핑 처리를 하지 않아서 정상 랜더링이 되지 않는 수 많은 문제가 발생하므로, 꼭 필요한 경우에만 unescape를 사용하자.
Spring EL 표현식
SpringEL 표현식을 사용하여 여러 케이스를 간단히 작성해보았다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>spring el expressions</h1>
<ul>
<li>${pet.name} : <span th:text="${pet.name}"></span></li>
<li>${pet['name'] : <span th:text="${pet['name']}"></span></li>
<li>${pet.getName()} : <span th:text="${pet.getName()}"></span></li>
<li th:inline="none">[[${pet.name}]] : <span>[[${pet.name}]]</span></li>
</ul>
<ul>
<li>${pets[0].name} : <span th:text="${pets[0].name}"></span></li>
<li>${pets[0]['name'] : <span th:text="${pets[0]['name']}"></span></li>
<li>${pets[0].getName()} : <span th:text="${pets[0].getName()}"></span></li>
<li th:each="pet : ${pets}">[[${pet.name}]]</li>
</ul>
<ul>
<li>${petMap['cat'].name} : <span th:text="${petMap['cat'].name}"></span></li>
<li>${petMap['cat']['name']} : <span th:text="${petMap['cat']['name']}"></span></li>
<li>${petMap['dog'].getName()} : <span th:text="${petMap['dog'].getName()}"></span></li>
<li th:each="pet : ${petMap.values()}">[[${pet.name}]]</li>
</ul>
<h1>지역 변수 - (th:with)</h1>
<ul th:with="first = ${pets[0]}">
<li>첫번째 동물의 이름은 [[${first.Name}]]</li>
</ul>
</body>
</html>
값을 꺼내오는 방식은 ".property", "['property']", ".메서드명()" 으로 가져올 수 있다.
이건 각자 제일 편한 방법으로 골라서 사용하면 되고, 간단하면서 패턴이 다 비슷하기 때문에 사용하기 어렵지 않다.
기본 객체들
// 스프링 3.0 이후, 기본 객체는 아래만 제공
<li>locale : <span th:text="${#locale}"></span></li>
// 편의 객체
<ul>
<li>Request parameter : <span th:text="${param.name}"></span></li>
<li>Session : <span th:text="${session.sessionData}"></span></li>
<li>spring bean <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ul>
- HTTP 요청 파라미터 접근 : param
- HTTP 세션 접근 : session
- 스프링 빈 접근 : @
- ${@beanName.methodName('args')} 와 같이 사용 가능
4 다양한 표준 표현식들
타임리프 공식 문서에 정리되어 있는 내용을 정리해보았다.
- 단순 표현식
- 변수 표현식: ${...}
- 선택 변수 표현식: *{...}
- 메세지 표현식: #{...}
- 링크 URL 표현식: @{...}
- 프라그먼트 표현식: ~{...}
- 리터럴
- 텍스트 리터럴 : 'one text', 'Another one!',…
- 숫자 리터럴 : 0, 34, 3.0, 12.3,…
- 논리형 리터럴 : true, false
- Null 리터럴 : null
- 리터럴 토큰 : one, sometext, main,…
- 텍스트 연산 :
- 문자열 연산자 : +
- 리터럴 대체 표기법 : |The name is ${name}|
- 숫자 연산 :
- 이항 연산자 : +, -, *, /, %
- Minus sign (unary operator): -
- 논리형 연산 :
- 이항 연산자 : and, or
- 부정 연산자 (unary operator): !, not
- 비교 연산과 동등성 비교 :
- 비교 연산자 : >, <, >=, <= (gt, lt, ge, le)
- 동등성 연산자 : ==, != (eq, ne)
- 조건 연산 :
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
- Special tokens:
- No-Operation: _
5 선택 변수 표현식
이미 알고 있겠지만, ${...}으로만 변수를 가져와서 쓸 수 있는 것이 아니다. 여기엔 더 편리한 표현식을 쓸 수 있는데 바로 선택 변수 표현식 *{...} 이다. 타임리프 공식 문서에 적혀있는 아래의 코드를 확인해보자.
// 선택 변수 표현식
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
// 변수 표현식
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
보면 알겠지만, 선택 변수 표현식이 중복성이 줄어들기 때문에 더 간결하다. 이걸 사용할 때 주의해야 할 점은 resource를 단순히 보여줄 때에도 빈 객체라도 넣어줘야 한다는 것이다.
6 Link URL
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
이걸 사용하는 방법은 "/"가 앞에 있는 것들은 URL 경로가 될 것이고, 그렇지 않은 것들은 마지막에 소괄호에 넣어주면 된다.
중간에 경로 변수가 사용될 경우엔 마지막 URI를 적고 나서 소괄호 안에 값을 넣어주면 된다. 3번째 코드를 보면 된다.
7 Literals
7.1 Text Literal
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
'' 를 사용해도 좋고, ||인 리터럴 대체 표기법을 사용해도 좋다. 이렇게 하면 여러 토큰을 + 연산자로 굳이 잇지 않고 한번에 입력할 수 있다.
7.2 Number Literal
<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>
th:field
<div th:each="type : ${types}" class="form-check form-check-inline">
<input type="radio" th:field="${item.itemType}" th:value="${type.name()}" class="form-check-input" disabled/>
<label th:for="${#ids.prev('itemType')}" th:text="${type.getDescription()}" class="form-check-label"></label>
</div>
- 데이터 바인딩: th:field를 사용하여 HTML 폼 필드와 모델 객체의 속성을 연결하면, 사용자가 폼을 제출할 때 입력한 값을 모델 객체에 자동으로 바인딩할 수 있다. 이를 통해 데이터의 전송과 수신을 간편하게 처리 가능.
- 필드 값 설정: th:field를 사용하여 폼 필드의 초기값을 설정할 수 있음. 모델 객체의 속성 값을 폼 필드에 자동으로 채워줌. 이를 통해 수정 폼이나 데이터 수정 시 이전 값으로 미리 채워진 폼을 제공할 수 있음.
- 필드 유형 추론: th:field는 모델 객체의 속성을 기반으로 폼 필드의 유형을 추론 가능. 예를 들어, 모델 객체의 속성이 String 타입이면 텍스트 필드로, boolean 타입이면 체크박스로 자동으로 변환.
- 필드 이름 생성: th:field는 폼 필드의 name 속성을 자동으로 생성. 이는 서버에서 폼 데이터를 처리할 때 필요한 필드 식별자로 사용됨. 폼 필드의 이름을 하드코딩하는 대신, th:field를 사용하여 모델 객체와 연결된 필드 이름을 자동으로 생성할 수 있습니다.
- th:each를 사용하는 경우, name을 카운팅 해서 만드는데, 이때는 label 태그와 연결하려면 th:for=${#ids.prev('name명')}과 같이 해야함.
- 또한, th:field는 value와 일치하는 값이 있다면 여러 인풋 폼을 자동으로 체크 해줌.
- 바인딩 오류 : 링크를 확인하면 나온다.
타임리프에 기능이 많기도 하고, 워낙에 다양한 케이스를 사용해서 공식문서에 잘 정리되어 있어서 필요할 때 공식 문서를 확인하는게 훨씬 좋을 거 같다는 생각이 들어서 이번 포스팅은 여기에서 마치도록 한다.
타임 리프 공식 문서
https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html