본 Thymeleaf 공부하기 시리즈는 김영한 님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의를 듣고 작성한 내용입니다.
들어가면서
몇몇 포스팅에서 계속 이야기해왔지만 나는 정말정말 HTML 에 서툴다. CSS 는 말 할 것도 없고.
다만 어쨌든 내가 만든 프로젝트의 결과물들을 단순한 자바 코드로 보여 줄 수는 없었기에 HTML 을 공부...가 아니라 검색해서 사용해왔다.
다행히도 JSP 와 JSTL 을 배운 경험이 있어서 JSP 로 프론트 페이지를 겨우 구현할 수는 있었다.
사실 jsp 를 쓰면서 굉장히 불편했던 점이 많았다. JSP의 특성상 HTML 파일에 JAVA 코드가 들어갈 때가 많았고, JAVA 코드가 아닌 JSTL 을 사용해서 구현한다고 하더라도 JSTL 과 HTML, CSS 까지 모두 사용할때면 정말정말 머리아플때가 많았다 ㅠ.ㅠ
그래서 이번 기회에 spring 과 연동이 정말 잘되는? Thymeleaf 를 공부하고 정리해보려 한다. 아무리 백엔드 개발자라고 하더라도 결국 프론트를 아무것도 모를 수 없기에 그리고 무엇보다, jsp 과 jstl 을 벗어나고 싶다!!
1. 타임리프의 특징
- 서버 사이드 HTML 랜더링(SSR)
=> SSR 이란 서버 사이드 HTML 랜더링을 의미한다. 타임리프는 백엔드 서버에서 HTML 을 동적으로 랜더링 하는 용도로 사용한다
- 네츄럴 템플릿
=> 타임리프는 순서 HTML 을 유지하는 특징이 있다.
=> 타임리프로 작성한 파일은 HTML 을 유지하기 때문에 웹 브라우저에서 파일을 직접 열어도 내용을 확인 할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인 할 수 있다.
=> HTML 이 뒤죽박죽 섞여서 브라우저에서 정상적인 HTML 결과를 확인 할 수 없다 : 대표적으로 JSP
=> 타임 리프는 해당 파일 그대로 웹 브라우저에서 열어도 정상적인 HTML 결과를 확인 할 수 있다. 물론 이 경우 동적인 결과가 랜더링 되서 보여지지는 않는다.
=> 이렇게 순수 HTML 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 내츄럴 템플릿 이라고 한다
- 스프링 통합 지원
=> 타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용할 수 있게 제공한다
2. HTML 엔티티
웹 브라우저는 < 를 HTML 테그의 시작으로 인식한다. 따라서 < 를 테그의 시작이 아니라 문자로 표현할 수 있는 방법이 필요한데, 이것을 HTML 엔티티라 한다. 그리고 이렇게 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 이스케이프(escape)라 한다. 그리고 타임리프가 제공하는 th:text , [[...]] 는 기본적으로 이스케이스(escape)를 제공한다
1) Unescape
타임리프는 다음 두 기능을 제공한다.
th:text -> th:utext : [[...]] -> [(...)]
3. 텍스트 출력하기
텍스트를 출력하는 방법은 크게 2가지가 있다.
태그를 사용하는 th:text="${model attribute name}" 와
태그 없이 사용하는 [[${model attribute name}]]
<body>
<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
<li>th:text 사용
<span th:text="${data}"></span>
<li>컨텐츠안에 직접 출력하기 = [[${data2}]]</li>
<li>태그 기능 유지 = <span th:text="${esc}"></span></li>
<li>unescaped 태그까지 포함해서 그대로 출력 <span th:utext="${un}"></span></li>
</li>
</ul>
<h1><span th:inline="none">[[..]] vs [(..)]</span></h1>
<ul>
<li><span th:inline="none">[[...]] = </span>[[${em}]]</li>
<li><span th:inline="none">[(...)] = </span>[(${em})]</li>
</ul>
</body>
4. 변수 - SpringEL
1) 변수 출력
타임리프에서 변수를 사용할 때는 변수 표현식을 사용한다.
변수 표현식 : ${...}
이 변수 표현식에는 스프링 EL 이라는 스프링이 제공하는 표현식으로도 사용 가능
<h1>SpringEL 표현식</h1>
<ul>Object : attributeName.변수명, ['변수명'], .getter메서드
<li>${user.username} = <span th:text="${user.username}"></span></li>
<li>${user['username']} = <span th:text="${user['username']}"></span></li>
<li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List : list[인덱스].변수명, ['변수명'], .getter메서드
<li>${users[0].username} = <span th:text="${list[0].username}"></span>
</li>
<li>${users[0]['username']} = <span th:text="${list[0]['username']}"></span>
</li>
<li>${users[0].getUsername()} = <span th:text="${list[0].getUsername()}"></span></li>
</ul>
<ul>Map : map['key'].변수명, ['변수명'], .getter메서드
<li>${userMap['레이너'].username} = <span th:text="${map['레이너'].username}"></span></li>
<li>${userMap['레이너']['username']} = <span th:text="${map['레이너']['username']}"></span></li>
<li>${userMap['레이너'].getUsername()} = <span th:text="${map['레이너'].getUsername()}"></span></li>
</ul>
</body>
2) 지역 변수 생성
지역 변수 생성은 th:with="변수명=${변수에 담을 값}"
이때 생성된 지역 변수는 해당 태그 내에서만 사용 가능하다
아래 코드의 경우 div 내에서만 사용 가능!!
<h1>지역 변수 - (th:with)</h1>
<div th:with="var=${list[1]}">
<p>두번째 사람의 이름은 <span th:text="${list[1].username}"></span></p>
</div>
5. 기본 객체
타임리프는 웹에서 사용하는 기본 객체들을 사용 할 수 있도록 제공한다
request, response, session, servletContext, locale, HTTP 요청 파라미터, 스프링 빈
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>각 객체.get('name') 해서 request에 담긴 값들을 꺼내올 수 있다</li>
<li>request = <span th:text="${#request}"></span></li>
<li>#request. = <span th:text="${#request.getAttribute('obj')}"></span></li>
<li>response = <span th:text="${#response}"></span></li>
<li>session = <span th:text="${#session}"></span></li>
<li>servletContext = <span th:text="${#servletContext}"></span></li>
<li>locale = <span th:text="${#locale}"></span></li>
</ul>
<h1>편의 객체</h1>
<ul>
<li>Request : param.파라미터명 = <span th:text="${param.paramData}"></span></li>
<li>session = session.세션명 <span th:text="${session.sessionData}"></span></li>
<li>spring bean = @Bean이름.Bean <span th:text="${@springBean.Bean('Spring!')}"></span></li>
</ul>
6. 유틸리티 객체와 날짜
타임리프는 문자, 숫자, 날짜, URI 등을 편리하게 다루는 다양한 유틸리티 객체들을 제공한다
대표적으로 #message, #uris, #dates, #calendars, #temporals, #numbers, #strings, #objects, #bools, #arrays, #lists, #sets, #maps, #ids
이중 메시지와 관련한 #message, 아이디와 관련한 #ids 정도는 기억하자. 나머지는 그냥 있구나 정도만 알고있고, 쓸때 검색하자
<body>
<h1>LocalDateTime</h1>
<ul>
<h2>날짜 유틸리티</h2>
<li>default = <span th:text="${localDateTime}"></span></li>
<li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></span></li>
</ul>
</body>
7. url 링크
<h1>URL 링크</h1>
<ul>
<li>url : <a th:href="@{https:/terianp.tistory.com/}">기0본 url</a></li>
<li>url?param1="data1"¶m2="data2" : <a th:href="@{https:/terianp.tistory.com/(param1=${param1}, param2=${param2})}">파라미터 추가</a></li>
<li>url/data1/data2 : <a th:href="@{https:/terianp.tistory.com/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<li>url/data1?param2="data2" : <a th:href="@{https:/terianp.tistory.com/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
8. 리터럴
리터럴은 소스 코드상에 고정된 값을 말하는 용어
타임리프에서 문자 리터럴은 항상 원칙적으로 ' (작은 따옴표)로 감싸야 한다. 특히 중간에 공백이 있으면 하나의 의미있는 토큰으로 인식되지 않는다.
따라서 "hello world" 가 아닌 " '(작은 따옴표)hello world' " 로 만들어야 한다
혹은 작은 따옴표를 대체하기 위해 "|문자 ${..}|" 를 사용할 수도 있다
<h1>리터럴</h1>
<ul>
<!--주의! 다음 주석을 풀면 예외가 발생함-->
<!-- <li>"hello world!" = <span th:text="hello world!"></span></li>-->
<li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li>
<li>'hello world!' = <span th:text="'hello world!'"></span></li>
<li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li>
<li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
</ul>
9. 연산
비교연산: HTML 엔티티를 사용해야 하는 부분에 주의할 것
조건식: 자바의 조건식과 유사하다.
Elvis 연산자: 조건식의 편의 버전, thymeleaf 버전!
No-Operation: _ 인 경우 마치 타임리프가 실행되지 않는 것 처럼 동작한다.
즉 ${...} 의 내용이 없을 때 출력되는 내용을 태그 안에 작성해두고 사용하면 ${...} 의 내용이 null 일 때와 아닐 때는 구분하여 편리하게 사용 가능
<ul>
<li>산술 연산
<ul>
<li>10 + 2 = <span th:text="10 + 2"></span></li>
<li>10 % 2 == 0 = <span th:text="10 % 2 == 0"></span></li>
</ul>
</li>
<li>비교 연산
<ul>
<li>1 > 10 = <span th:text="1 > 10"></span></li>
<li>1 gt 10 = <span th:text="1 gt 10"></span></li>
<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
<li>1 ge 10 = <span th:text="1 ge 10"></span></li>
<li>1 == 10 = <span th:text="1 == 10"></span></li>
<li>1 != 10 = <span th:text="1 != 10"></span></li>
</ul>
</li>
<li>조건식
<ul>
<li>(10 % 2 == 0)? '짝수':'홀수' = <span th:text="(10 % 2 == 0)?'짝수':'홀수'"></span></li>
</ul>
</li>
<li>Elvis 연산자 : 조건식의 축약 버전 => ${data}:? 했을 때 값이 있으면 ${data} 출력, 없으면 데이터가 없습니다 출력
<ul>
<li>${data}?: '데이터가 없습니다.' = <span th:text="${data}?: '데이터가 없습니다.'"></span></li>
<li>${nullData}?: '데이터가 없습니다.' = <span th:text="${nullData}?:'데이터가 없습니다.'"></span></li>
</ul>
</li>
<li>No-Operation
<ul>
<li>${data}?: _ = <span th:text="${data}?:_">데이터가 없습니다.</span></li>
<li>${nullData}?: _ = <span th:text="${nullData}?:_">span 태그 안 내용 출력</span></li>
</ul>
</li>
</ul>
Thymeleaf 표현법 정리 : 아주 중요
표현법 | 설명 |
<span th:text="${model attribute name}"></span> |
태그 사용하여 Attribute 내용 출력 |
<span>[[${model attribute name}]]</span> | 태그 없이 Attribute 내용 출력 |
${attributeName.변수명} ${attributeName.['변수명']} ${attributeName.getter메서드} |
Attribute 내용 출력 - 단순 Object |
${list[인덱스].변수명} ${list[인덱스].['변수명']} ${list[인덱스].getter메서드} |
Attribute 내용 출력 - List |
${map['key'].변수명} ${map['key'].['변수명']} ${map['key'].getter메서드} |
Attribute 내용 출력 - Map |
th:with="변수명=${변수에 담을 값}" | 태그 내에서 사용가능한 지역 변수 생성 |
${#request} ${#response} ${#session} ${#servletContext} ${#locale} |
Thymeleaf 에서 제공하는 기본 객체 객체.get('name') 을 사용해서 값들을 꺼내올 수 있다 |
파라미터 : ${param} session : ${session} |
Thymeleaf 에서 제공하는 편의 객체 마찬가지로 객체.get('name') 해서 값들을 꺼내 올 수 있다 |
springBean : ${@Bean이름.메서드명('매개변수')} | Thymeleaf 에서 제공하는 편의 객체 SpringBean 에 접근 가능하도록 하며 Bean 의 메서드를 실행할 수 있다 |
${#temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')} | 자바 객체인 localDateTime 을 temporals 를 이용해서 날짜를 특정한 포멧으로 출력 가능 |
@{url} | 단순 url |
@{url(param1=${param1}, param2=${param2})} | 쿼리 파라미터 |
@{url/param1/{param2}(param1=${param1}, param2=${param2})} | 경로 변수 |
@{url/{param1}(param1=${param1}, param2=${param2})} | 쿼리파라미터 + 경로 변수 |
| 문자 ${...} | | 리터럴 대체 문법 |
gt, >=, == , != | gt : 크다 >= : 크거나 같다 == : 같다 != : 다르다 |
<span th:text="${data}?: '데이터가 없습니다.'"></span> | Elvis 연산자 : ${data}?: => 3항연산의 thymeleaf 형 데이터가 있는 경우 ${...} 안에 있는 내용이 출력되고, 데이터가 없는 경우 : 뒤에 있는 내용이 출력된다 |
<span th:text="${data}?:_">span 태그 안 내용 출력</span> | No-Operation : ${data}?:_(언더바) ${...} 의 내용이 없을 시 - null 일 때 태그 안에있는 내용이 출력된다 |
Reference
공식 사이트
공식 메뉴얼 - 기본 기능
공식 메뉴얼 - 스프링 통합
타임리프 유틸리티 객체
objects 유틸리티 객체 예시
'Front-End > thymeleaf' 카테고리의 다른 글
Thymeleaf 공부하기 (2) : 속성 값 설정, 반복, 조건, 주석, 블록, 자바스크립트 인라인, 템플릿 조각, 템플릿 레이아웃 (0) | 2022.10.11 |
---|
댓글