참새의 이야기
[MVC2] Thymeleaf - 기본 본문
타임리프는 스프링과의 자연스러운 통합을 지원하는 템플릿 엔진이다.
이번 글에서는 기본적인 사용법을 알아보고, 이후 Form을 만들 때 필요한 기능까지 알아보려 한다.
기본 기능
텍스트
타임리프에서 텍스트를 출력하는 방법으로는 크게 두 가지가 있다.
첫 번째는 th:text
를 사용하는 방법이다.
HTML 태그의 속성으로 추가하면 된다.
아래는 사용 예시이다.
<span th:text="${data}"></span>
HTML 태그가 아닌 문서의 영역에서 출력하고 싶다면 아래와 같이 활용할 수 있다.
[[${data}]]
컨트롤러에서는 model.addAttribute("data", "this is data");
와 같이 넘겨주면 된다.
escape
HTML에서 사용하는 특수문자가 있다.
<
나 >
같은 경우는 태그로 활용된다.
이런 문자가 텍스트에 포함되어 있다면 개발자의 의도와 다른 출력 결과가 나올 수 있다.
위의 예시에서 data의 값이 <b>Spring!</b>
이고 태그로 인한 볼드 처리를 기대하지 않았을 때, escape 기능을 제공하지 않는다면 출력된 결과는 의도와 달리 볼드 처리된 `Spring!`** 이 될 것이다.**
이를 방지하기 위해 타임리프는 기본적으로 escape 기능을 제공한다.
만약 escape 기능을 사용하고 싶지 않다면 아래와 같은 방법으로 출력할 수 있지만, 필요할 때만 사용하는 것이 좋다.
<span th:utext="${data}"></span>
[(${data})]
변수
타임리프에서의 변수 표현식은 굉장히 간단하다.
${…}
하나로 다양하게 활용할 수 있다.
우선 컨트롤러에서 아래와 같은 로직을 수행한다고 하자.
@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";
}
model에는 user, users, 그리고 userMap이라는 attribute가 추가되어 있다.
이들을 ${…}
로 각각 출력하는 것은 물론이고, users나 userMap과 같은 List, Map의 섬세한 출력도 가능하다.
${users[0].username}
${users[0]['username']}
${users[0].getUsername()}
${userMap['userA'].username}
${userMap['userA']['username']}
${userMap['userA'].getUsername()}
지역 변수
선언한 태그 내에서 활용 가능한 지역 변수도 있다.
<div th:with="first=${users[0]}">
<p>first의 이름은 <span th:text="${first.username}"></span></p>
</div>
th:with
로 선언한 지역 변수 first
를 활용한 예시이다.
유틸리티 객체와 날짜
아래와 같은 유틸리티 객체들이 있으니 외우기보다는 필요할 때 찾아서 활용하는 것이 좋다.
- #message : 메시지, 국제화 처리
- #uris : URI 이스케이프 지원
- #dates : java.util.Date 서식 지원
- #calendars : java.util.Calendar 서식 지원
- #temporals : 자바 8 날짜 서식 지원
- #numbers : 숫자 서식 지원
- #strings : 문자 관련 편의 기능
- #objects : 객체 관련 기능 제공
- #bools : boolean 관련 기능 제공
- #arrays : 배열 관련 기능 제공
- #lists , #sets , #maps : 컬렉션 관련 기능 제공
- #ids : 아이디 처리 관련 기능 제공, 뒤에서 설명
날짜
${localDateTime}
을 출력하면 2023-08-05T12:10:48.173652300
와 같은 형태로 출력된다.
필요에 따라 아래와 같이 포맷을 설정하여 활용하면 된다.
<span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span>
URL 링크
url 연결은 @{…}
표현식으로 할 수 있다.
아래와 같이 model의 attribute를 query parameter 혹은 path variable로 활용할 수 있다.
<!--"/hello"-->
<li><a th:href="@{/hello}">basic url</a></li>
<!--"/hello?param1=data1¶m2=data2"-->
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
<!--"/hello/data1/data2"-->
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<!--"/hello/data1?param2=data2"-->
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
literal
타임리프에서 문자열은 작은따옴표로 감싸줘야 한다.
th:text=”hello”
와 같이 큰 따옴표로 감싸면 오류가 발생한다.
아래와 같은 리터럴 대체 문법도 있으니 참고하자.
<li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
연산
산술 연산, 비교 연산
산술 연산은 아래와 같이 할 수 있다.
<span th:text="10 + 2"></span>
비교 연산은 조금 다르다.
비교 연산자를 HTML 엔티티로 바꿔 사용해야 한다.
> (gt), < (lt), >= (ge), <= (le), ! (not), == (eq), != (neq, ne)
Elvis 연산자
편의를 위해 제공하는 조건식이다.
null 값을 허용하지 않는 곳에 null 값이 들어오면 기존 조건식의 false에 해당하는 값을 출력한다.
<li>${data}?: '데이터가 없습니다.' = <span th:text="${data}?: '데이터가없습니다.'"></span></li>
<li>${nullData}?: '데이터가 없습니다.' = <span th:text="${nullData}?: '데이터가 없습니다.'"></span></li>
속성 값 설정
타임리프에서는 없는 속성을 추가하거나 이미 존재하는 속성을 대체할 수 있다.
<input type="text" name="mock" th:name="userA" />
위와 같은 경우, 타임리프는 기존 name
인 mock을 userA로 변경한다.
아래의 속성을 이용하여 기존 속성에 값을 추가할 수 있다.
- th:attrappend
- th:attrprepend
- th:classappend
반복
컨트롤러가 model.addAttribute("users", list);
를 실행 후 반환한다고 하자.
list의 user들을 반복문으로 출력하고 싶다면 th:each
를 활용하면 된다.
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
each는 List 뿐만 아니라, Iterable한 객체들을 모두 반복할 수 있다.
조건부 평가
if, unless
타임리프에서의 조건식은 if와 unless가 대표적이다.
<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
<span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
switch
<td th:switch="${user.age}">
<span th:case="10">10살</span>
<span th:case="20">20살</span>
<span th:case="*">기타</span>
</td>
case가 10도 아니고 20도 아닌 경우, 즉 만족하는 조건이 없을 때 *
의 값을 사용한다.
자바스크립트 인라인
인라인을 사용하지 않으면 텍스트 렌더링 과정에서 변수 이름이 그대로 남아있거나 객체의 toString()이 호출된다.
자바스크립트의 인라인을 th:inline
으로 사용하여, 텍스트를 문자로 출력하거나 객체를 JSON 형식으로 출력할 수 있다.
템플릿 조각
웹에서는 여러 페이지에서 공통적으로 사용하는 영역이 있다.
이런 부분들을 템플릿 조각을 이용한다면 코드의 중복 없이 생성할 수 있다.
insert, replace
<div th:insert="~{template/fragment/footer :: copy}"></div>
div 태그 안에 template/fragment/footer.html
의 copy라는 fragment를 가져와 삽입한다.
replace의 경우 삽입대신 대체한다.
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
위와 같이 파라미터를 전달할 수도 있다.
reference
이 글은 김영한님의 '스프링 MVC 2편'을 듣고 작성했습니다.
'Spring' 카테고리의 다른 글
[MVC2] 메시지와 국제화 (0) | 2023.08.06 |
---|---|
[MVC2] Thymeleaf - 스프링 통합과 폼 (0) | 2023.08.05 |
[MVC1] 데이터 담아 Redirect하기 (2) | 2023.08.04 |
[MVC1] HTTP 메시지 컨버터 (0) | 2023.08.04 |
[MVC1] 스프링 MVC Response (0) | 2023.08.04 |