Spring MVC Part 2 - Thymeleaf - 기본기능
타임리프 소개
-
SSR (Server Side Rendering)
- thymeleaf -
CSR (Client Side Rendering)
- javaScript, Reac -
네츄럴 템플릿
타임리프는 순수 HTML을 최대한 유지한다는 특성을 가진다.
따라서 웹 브라우저에서 파일 자체를 직접 열어도 내용을 확인할 수 있다는 장점을 가지고 이 부분에서 JSP와 차이가 있다.
타임리프 기본 기능
텍스트 - text,utext
<li>th:text = <span th:text="${data}"></span></li>
<li>th:utext = <span th:utext="${data}"></span></li>
<li>th:text = <span th:text="${data}"></span></li>
<li>th:utext = <span th:utext="${data}"></span></li>
utext는 unescaped text이다.
타임리프가 제공하는 th:text ,[[...]] 는 기본적으로 escape를 제공한다.
escape란 HTML에서 사용하는 특수 문자(ex)<b>
)를 HTML 엔티티로 변경하는 것을 이스케이프(escape)라 한다.( 문자 그대로 나오게 함 )
- text
\- 기본 : th:text ="${data}"
\- uescape : th:utext = "${data}"
- utext
\- 기본 : [[${data}]]
\- uescape : [(${data})]
SpringEL 표현식
<ul>Object
<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>
Object : user
List : users[0]
Map : userMap['username']
user.username : user의 username을 프로퍼티 접근 -> user.getUsername()
user['username'] : 위와 같음 -> user.getUsername(
user.getUsername() : user의 getUsername() 을 직접 호출한다.
기본 객체들
코드
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>식 기본 객체 (Expression Basic Objects)</h1>
<ul>
<li>request = <span th:text="${#request}"></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 Parameter = <span th:text="${param.paramData}"></span></li>
<li>session = <span th:text="${session.sessionData}"></span></li>
<li>spring bean = <span th:text="${@helloBean.hello('Spring!')}"></span></li>
</ul>
</body>
</html>
타임리프는 기본 객체들을 제공한다.
- ${#request}
- ${#response}
- - ${#session}
- ${#servletContext}
- ${#locale}
그런데 #request 는 HttpServletRequest 객체가 그대로 제공되기 때문에 데이터를 조회하려면
request.getParameter("data") 처럼 불편하게 접근해야 한다.
이런 점을 해결하기 위해 편의 객체도 제공한다.
- HTTP 요청 파라미터 접근: param
예) ${param.paramData} - HTTP 세션 접근: session
예) ${session.sessionData} - 스프링 빈 접근: @
예) ${@helloBean.hello('Spring!')}
출력 결과
타임리프 유틸리티 객체들
#message : 메시지, 국제화 처리
#uris : URI 이스케이프 지원
#dates : java.util.Date 서식 지원
#calendars : java.util.Calendar 서식 지원
#temporals : 자바8 날짜 서식 지원
#numbers : 숫자 서식 지원
#strings : 문자 관련 편의 기능
#objects : 객체 관련 기능 제공
#bools : boolean 관련 기능 제공
#arrays : 배열 관련 기능 제공
#lists , #sets , #maps : 컬렉션 관련 기능 제공
#ids : 아이디 처리 관련 기능 제공, 뒤에서 설명
필요시에 타임리프 메뉴얼 보고 찾아 쓰기.
url
코드
<body>
<h1>URL 링크</h1>
<ul>
<li><a th:href="@{/hello}">basic url</a></li>
<li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query
param</a></li>
<li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
<li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
</body>
기본적으로 @{}형식을 이용해 링크를 지정한다.
- (param1=${param1}) 을 뒤에 붙이면 링크 뒤에 파라미터가 붙는다.
ex) http://localhost:8080/hello?param1=data1 - hello/{param1} 형식을 이용하면 파라미터 값이 url에 붙는다.
ex) http://localhost:8080/hello/data1/data2
두가지를 합쳐서 사용할 수 있다.
ex) http://localhost:8080/hello/data1?param2=data2
리터럴
리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.
- 문자, 숫자, boolean, null
ex) String a = "hello", int a = 10
html상에서 리터럴은 원칙상''
으로 감싸야 한다.
리터럴 대체(Literal substitutions)
<span th:text="|hello ${data}|">
||
으로 감싸주는 것.
마지막의 리터럴 대체 문법을 사용하면 마치 템플릿을 사용하는 것 처럼 편리하다.
연산
자바에서의 연산과 크게 다르지 않지만, HTML 안에서 사용하기 때문에 HTML엔티티를 사용하는 부분을 주의해야 한다.
속성 값 설정
타임리프는 주로 HTML태그에 th:*
를 이용해 속성을 지정하는 방식으로 동작한다.
이렇게 속성을 지정하면 기존 속성을 대체하고 없다면 새로 만든다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>속성 설정</h1>
<input type="text" name="mock" th:name="userA" />
<h1>속성 추가</h1>
- th:attrappend = <input type="text" class="text" th:attrappend="class=' large'" /><br/>
- th:attrprepend = <input type="text" class="text" th:attrprepend="class='large '" /><br/>
- th:classappend = <input type="text" class="text" th:classappend="large" /><br/>
<h1>checked 처리</h1>
- checked o <input type="checkbox" name="active" th:checked="true" /><br/>
- checked x <input type="checkbox" name="active" th:checked="false" /><br/>
- checked=false <input type="checkbox" name="active" checked="false" /><br/>
</body>
</html>
-
속성추가
- attrappend : class 뒤에 'large' 추가 (띄어쓰기 직접 입력)
- attrprepend : class 앞에 'large' 추가 (띄어쓰기 직접 입력)
- classappend : class 뒤에 'large' 추가 (띄어쓰기 자동) -
checked 처리
기존 html은 checked 속성이 있으면 무조건 체크되는 방식인데,
th:checked
속성을 통해 checked 를 true,false값을 이용해 설정할 수 있다.
이렇게 했을 때 장점은, 개발자 입장에서는 직관적이기 때문에 편하고 boolean 값을 변수로 받아서 checked를 설정할 수도 있다.
반복
th:each
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
users에 리스트, 맵과 같은 객체를 넣으면 thymeleaf가 차례대로 user 에 값을 꺼내서 태그를 반복 실행한다.
반복상태 유지
지정변수명 + Stat으로 사용 가능
ex) user -> userStat
- 반복 상태 유지 기능
- index : 0부터 시작하는 값
- count : 1부터 시작하는 값
- size : 전체 사이즈
- even , odd : 홀수, 짝수 여부( boolean )
- first , last :처음, 마지막 여부( boolean )
- current : 현재 객체
조건부 평가
if,unless
<h1>if, unless</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">1</td>
<td th:text="${user.username}">username</td>
<td>
<span th:text="${user.age}">0</span>
<span th:text="'미성년자'" th:if="${user.age < 20}"></span>
<span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
</td>
</tr>
</table>
unless는 if의 반대, 거짓이면 조건이 성립한다.
if,unless는 조건을 만족시키지 않으면 <span> ...</span>
부분 자체가 렌더링 되지 않고 사라진다.
switch
<h1>switch</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">1</td>
<td th:text="${user.username}">username</td>
<td th:switch="${user.age}">
<span th:case="10">10살</span>
<span th:case="20">20살</span>
<span th:case="*">기타</span>
</td>
</tr>
</table>
주석
<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->
블록
<th:block> 은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다
<th:block th:each="user : ${users}">
<div>
사용자 이름1 <span th:text="${user.username}"></span>
사용자 나이1 <span th:text="${user.age}"></span>
</div>
<div>
요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
</div>
</th:block>
block을 통해 div 여러개를 한 번에 반복할 수 있다.
javascript 인라인
<!-- 자바스크립트 인라인 사용 전 -->
<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>
자바스크립트 인라인을 사용하면 자바스크립트에 맞게 타임리프가 타입을 조정한다.
이스케이프 문자 처리, 자료형 변환, 객체를 JASON형식으로 변환하는 등 의 기능을 타임리프가 수행한다.
또 script 안에서 each를 통해 반복할 수도 있다.
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
템플릿 조각
웹페이지를 개발할 때 공통 영역이 많이 있다. (ex: 페이지 상단 , 하단, 좌측 카테고리 등)
이렇게 공통되는 영역을 복사해서 사용하면 변경해야 할 때 여러 코드를 수정해야만 한다. 이러한 문제를 해결하기 위해서 타임리프는 템플릿 조각과 레이아웃 기능을 지원한다.
- footer
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="copy">
푸터 자리 입니다.
</footer>
<footer th:fragment="copyParam (param1, param2)">
<p>파라미터 자리 입니다.</p>
<p th:text="${param1}"></p>
<p th:text="${param2}"></p>
</footer>
</body>
</html>
- fragmentMain
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>부분 포함</h1>
<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>
<h2>부분 포함 단순 표현식</h2>
<div th:replace="template/fragment/footer :: copy"></div>
<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터
2')}"></div>
</body>
</html>
이렇게 main 페이지에서 th:insert,th:replace를 사용해 footer 템플릿에 있는 th:fragment ="copy" 라는 부분을 템플릿으로 가져와 사용한다.
- insert : 현재 태그(div)내부에 추가
- replace : 현재 태그(div)를 대체
마지막 파라미터 사용 코드와 같이 데이터를 동적으로 전달해서 렌더링 할 수도 있다.
템플릿 레이아웃
- layout base
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://
www.thymeleaf.org">
<head>
<title th:replace="${title}">레이아웃 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
<p>레이아웃 컨텐츠</p>
</div>
<footer>
레이아웃 푸터
</footer>
</body>
</html>
- layout main
<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title},
~{::section})}"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
</body>
</html>
결과
<html>
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
<footer>
레이아웃 푸터
</footer>
</body>
</html>
base 파일을 보면 태그에 th:fragment 속성이 정의되어 있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용들을 전달해서 부분부분 변경한다.
즉 layout main 은 html 자체를 base로 replace 해서 사용하는 걸 볼 수 있다.
Author And Source
이 문제에 관하여(Spring MVC Part 2 - Thymeleaf - 기본기능), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@bins1225/Thymeleaf-기본기능저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)