-
Spring Thymeleaf - 1, 기본 기능Spring 2024. 4. 12. 11:25728x90반응형
Thymeleaf - 1, 기본 기능
모든 내용을 다 다루기에는 양이 너무 많으므로,
내가 생각하기에 유용하거나, 특별한 기능들 위주로 짧게 다루겠다.
1. 타임리프 특징
1. 서버 사이드 HTML 렌더링 (SSR)
2. 네츄럴 템플릿
3. 스프링 통합 지원
1 - 1. 서버 사이드 HTML 렌더링 (SSR)
타임리프는 백엔드 서버에서 HTML을 동적으로 렌더링 하는 용도로 사용된다.
1 - 2. 네츄럴 템플릿
타임리프는 순수 HTML을 최대한 유지하는 특징이 있다.
타임리프로 작성한 파일은 HTML을 유지하기 때문에 웹 브라우저에서 파일을 직접 열어도 내용을 확인할 수 있고,
서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다.
JSP를 포함한 다른 뷰 템플릿들은 해당 파일을 열면,
예를 들어서 JSP 파일 자체를 그대로 웹 브라우저에서 열어보면
JSP 소스코드와 HTML이 뒤죽박죽 섞여서 웹 브라우저에서 정상적인 HTML 결과를 확인할 수 없다.
오직 서버를 통해서 JSP가 렌더링 되고 HTML 응답 결과를 받아야 화면을 확인할 수 있다.
반면, 타임리프로 작성된 파일은 해당 파일을 그대로 웹 브라우저에서 열어도 정상적인 HTML 결과를 확인할 수 있다.
물론 이 경우 동적으로 결과가 렌더링 되지는 않는다.
하지만 HTML 마크업 결과가 어떻게 되는지 파일만 열어도 바로 확인할 수 있다.
이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을
네츄럴 템플릿(natural templates)이라 한다.
1 - 3. 스프링 통합 지원
타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원한다.
이 부분은 '스프링 통합과 폼' 장에서 자세히 알아보겠다.
2. 기본 표현식
타임리프는 다음과 같은 기본 표현식들을 제공한다.
2 - 1. 간단한 표현
변수 표현식: ${...}
선택 변수 표현식: *{...}
메시지 표현식: #{...}
링크 URL 표현식: @{...}
조각 표현식: ~{...}
2 - 2. 리터럴
텍스트
'one text', 'Another one!'
숫자
0, 34, 3.0, 12.3
불린
true, false
널: null
리터럴 토큰
one, sometext, main
2 - 3. 문자 연산
문자 합치기
+
리터럴 대체
|The name is ${name}|
2 - 4. 산술 연산
Binary operators
+, -, *, /, %
Minus sign (unary operator)
-
2 - 5. 불린 연산
Binary operators
and, or
Boolean negation (unary operator)
!, not
2 - 6. 비교와 동등
비교
>, <, >=, <= (gt, lt, ge, le)
동등 연산: ==, != (eq, ne)
2 - 7. 조건 연산
If-then
(if) ? (then)
If-then-else
(if) ? (then) : (else)
Default
(value) ?: (defaultvalue)
2 - 8. 특별한 토큰
No-Operation
_
3. HTML 엔티티
웹 브라우저는 < 를 HTML 테그의 시작으로 인식한다.
따라서 < 를 테그의 시작이 아니라 문자로 표현할 수 있는 방법이 필요한데,
이것을 HTML 엔티티라 한다.
그리고 이렇게 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 이스케이프(escape)라 한다.
그리고 타임리프가 제공하는 th:text, [[...]] 는 기본적으로 이스케이프 (escape)를 제공한다.
< 는 <로 표시
> 는 >로 표시
기타 수 많은 HTML 엔티티가 있다.
참고
HTML 엔티티와 관련해서 더 자세한 내용은 HTML 엔티티로 검색해보자.
3 - 1. Unescape
이스케이프 기능을 사용하지 않으려면 어떻게 해야할까?
타임리프는 다음 두 기능을 제공한다.
th:text => th:utext
[[...]] => [(...)]
주의
실제 서비스를 개발하다 보면 escape를 사용하지 않아서 HTML이 정상 렌더링 되지 않는 수 많은 문제가 발생 한다. escape를 기본으로 하고, 꼭 필요한 때만 unescape를 사용하자.
4. 리터럴
타임리프에서 문자 리터럴은 항상 ' (작은 따옴표)로 감싸야 한다.
그런데 문자를 항상 ' 로 감싸는 것은 너무 귀찮은 일이다.
<span th:text="'hello'">
공백 없이 쭉 이어진다면 하나의 의미있는 토큰으로 인지해 서 다음과 같이 작은 따옴표를 생략할 수 있다.
룰: A-Z , a-z , 0-9 , [] , . , - , _
<span th:text="hello">
오류
문자 리터럴은 원칙상 ' 로 감싸야 한다. 중간에 공백이 있어서 하나의 의미있는 토큰으로도 인식되지 않는다.
<span th:text="hello world!"></span>
리터럴 대체(Literal substitutions)
리터럴 대체 문법을 사용하면 마치 템플릿을 사용하는 것 처럼 편리하다.
<span th:text="|hello ${data}|">
5. 연산
비교연산
HTML 엔티티를 사용해야 하는 부분을 주의하자
> (gt),
< (lt),
>= (ge),
<= (le),
! (not),
== (eq),
!= (neq, ne)
Elvis 연산자
조건식의 편의 버전
<span th:text="${data}?: '데이터가 없습니다.'"></span>
No-Operation
_ 인 경우 마치 타임리프가 실행되지 않는 것 처럼 동작한다.
이것을 잘 사용하면 HTML의 내용 그대로 활용할 수 있다.
마지막 예를 보면 '데이터가 없습니다.' 부분이 그대로 출력된다.
<span th:text="${nullData}?: _">데이터가 없습니다.</span>
6. 반복
타임리프에서 반복은 th:each 를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.
반복 상태 유지
<tr th:each="user, userStat : ${users}">
반복의 두번째 파라미터를 설정해서 반복의 상태를 확인 할 수 있습니다.
두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명( user ) + Stat 가 됩니다.
여기서는 user + Stat = userStat 이므로 생략 가능합니다.
반복 상태 유지 기능
index
0부터 시작하는 값
count
1부터 시작하는 값
size
전체 사이즈
even , odd
홀수, 짝수 여부( boolean )
first , last
처음, 마지막 여부( boolean )
current
현재 객체
7. 자바스크립트 인라인
타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.
자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.
<script th:inline="javascript">
예시
@GetMapping("/javascript") public String javascript(Model model) { model.addAttribute("user", new User("userA", 10)); addUsers(model); return "basic/javascript"; }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 자바스크립트 인라인 사용 전 --> <script> var username = [[${user.username}]]; var age = [[${user.age}]]; //자바스크립트 내추럴 템플릿 var username2 = /*[[${user.username}]]*/ "test username"; //객체 var user = [[${user}]]; </script> <!-- 자바스크립트 인라인 사용 후 --> <script th:inline="javascript"> var username = [[${user.username}]]; var age = [[${user.age}]]; //자바스크립트 내추럴 템플릿 var username2 = /*[[${user.username}]]*/ "test username"; //객체 var user = [[${user}]]; </script> </body> </html>
자바스크립트 인라인 사용 전 - 결과
<script> var username = userA; var age = 10; //자바스크립트 내추럴 템플릿 var username2 = /*userA*/ "test username"; //객체 var user = BasicController.User(username=userA, age=10); </script>
자바스크립트 인라인 사용 후 - 결과
<script> var username = "userA"; var age = 10; //자바스크립트 내추럴 템플릿 var username2 = "userA"; //객체 var user = {"username":"userA","age":10}; </script>
자바스크립트 인라인을 사용하지 않은 경우 어떤 문제들이 있는지 알아보고,
인라인을 사용하면 해당 문제들이 어떻게 해결되는지 확인해보자.
7 - 1. 텍스트 렌더링
var username = [[${user.username}]];
인라인 사용 전
var username = userA;
인라인 사용 후
var username = "userA";
인라인 사용 전 렌더링 결과를 보면 userA 라는 변수 이름이 그대로 남아있다.
타임리프 입장에서는 정확하게 렌더링 한 것이지만 아마 개발자가 기대한 것은 "userA"라는 문자일 것이다.
결과적으로 userA가 변수 명으로 사용되어서 자바스크립트 오류가 발생한다.
다음으로 나오는 숫자 age의 경우에는 " 가 필요 없기 때문에 정상 렌더링 된다.
인라인 사용 후 렌더링 결과를 보면 문자 타입인 경우 " 를 포함해준다.
추가로 자바스크립트에서 문제가 될 수 있 는 문자가 포함되어 있으면 이스케이프 처리도 해준다.
예) " => \"
7 - 2. 자바스크립트 내추럴 템플릿
타임리프는 HTML 파일을 직접 열어도 동작하는 내추럴 템플릿 기능을 제공한다.
자바스크립트 인라인 기능을 사용하면 주석을 활용해서 이 기능을 사용할 수 있다.
var username2 = /*[[${user.username}]]*/ "test username";
인라인 사용 전
var username2 = /*userA*/ "test username";
인라인 사용 후
var username2 = "userA";
인라인 사용 전 결과를 보면 정말 순수하게 그대로 해석을 해버렸다.
따라서 내추럴 템플릿 기능이 동작하지 않고, 심지어 렌더링 내용이 주석처리 되어 버린다.
인라인 사용 후 결과를 보면 주석 부분이 제거되고, 기대한 "userA"가 정확하게 적용된다.
7 - 3. 객체
타임리프의 자바스크립트 인라인 기능을 사용하면 객체를 JSON으로 자동으로 변환해준다.
var user = [[${user}]];
인라인 사용 전
var user = BasicController.User(username=userA, age=10);
인라인 사용 후
var user = {"username":"userA","age":10};
인라인 사용 전은 객체의 toString()이 호출된 값이다.
인라인 사용 후는 객체를 JSON으로 변환해준다.
7 - 4. 자바스크립트 인라인 each
자바스크립트 인라인은 each를 지원하는데, 다음과 같이 사용한다.
<!-- 자바스크립트 인라인 each --> <script th:inline="javascript"> [# th:each="user, stat : ${users}"] var user[[${stat.count}]] = [[${user}]]; [/] </script>
자바스크립트 인라인 each 결과
<script> var user1 = {"username":"userA","age":10}; var user2 = {"username":"userB","age":20}; var user3 = {"username":"userC","age":30}; </script>
8. 템플릿 레이아웃
웹 페이지를 개발할 때는 공통 영역이 많이 있다.
예를 들어서 상단 영역이나 하단 영역, 좌측 카테고리 등등 여러 페이지에서 함께 사용하는 영역들이 있다.
이런 부분을 코드를 복사해서 사용한다면 변경시 여러 페이지를 다 수정해야 하므 로 상당히 비효율적이다.
타임리프는 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원한다.
th:insert="~{}" th:replace="~{}"
insert는 태그 내부에 삽입,
replace는 태그를 대체하는 기능이다.
예를 들어서 에 공통으로 사용하는 css , javascript 같은 정보들이 있는데,
이러한 공통 정보들을 한 곳에 모아두고, 공통으로 사용하지만,
각 페이지마다 필요한 정보를 더 추가해서 사용하고 싶다면 다음과 같이 사용하면 된다.
<html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="common_header(title,links)"> <title th:replace="${title}">레이아웃 타이틀</title> <!-- 공통 --> <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script> <!-- 추가 --> <th:block th:replace="${links}" /> </head>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:replace="template/layout/base :: common_header(~{::title},~{::link})"> <title>메인 타이틀</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"> </head> <body> 메인 컨텐츠 </body> </html>
결과
<!DOCTYPE html> <html> <head> <title>메인 타이틀</title> <!-- 공통 --> <link rel="stylesheet" type="text/css" media="all" href="/css/awesomeapp.css"> <link rel="shortcut icon" href="/images/favicon.ico"> <script type="text/javascript" src="/sh/scripts/codebase.js"></script> <!-- 추가 --> <link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/themes/smoothness/jquery-ui.css"> </head> <body> 메인 컨텐츠 </body> </html>
common_header(~{::title},~{::link}) 이 부분이 핵심이다.
::title 은 현재 페이지의 title 태그들을 전달한다.
::link 는 현재 페이지의 link 태그들을 전달한다.
결과를 보자.
메인 타이틀이 전달한 부분으로 교체되었다.
공통 부분은 그대로 유지되고, 추가 부분에 전달한 들이 포함된 것을 확인할 수 있다.
이 방식은 사실 앞서 배운 코드 조각을 조금 더 적극적으로 사용하는 방식이다.
쉽게 이야기해서 레이아웃 개념을 두고,
그 레이아웃에 필요한 코드 조각을 전달해서 완성하는 것으로 이해하면 된다.
앞서 이야기한 개념을 정도에만 적용하는게 아니라 전체에 적용할 수도 있다.
728x90반응형'Spring' 카테고리의 다른 글
Spring 검증 - 1, Validation (0) 2024.04.17 Spring Thymeleaf - 2, 메시지 국제화 (0) 2024.04.16 Spring 스프링 MVC - 5, 기본 기능 - 응답 (0) 2024.04.12 Spring 스프링 MVC - 4, 기본 기능 - 요청 (0) 2024.04.11 Spring 스프링 MVC - 3, 스프링 MVC (0) 2024.04.11