서버 사이드 HTML 렌더링 (SSR)
<html xmlns:th="[http://www.thymeleaf.org](http://www.thymeleaf.org/)">기본 표현 식
<span th:text="${data}"></span> // HTML 테그 속성에 기능 정의하여 동작하기
컨텐츠 안에서 직접 출력하기 = [[${data}]] //HTML 테그 속성이 아니라, 콘텐츠 영역안에서 직접 데이터 출력하기
→ <span th:text="${data}"></span> // HTML 테그 속성에 기능 정의하여 동작하기
→ [[${data}]] : ${data} 로 받아온 텍스트가 [[ … ]] → 전체가 텍스트로 치환되어 출력된다.
HTML 문서는 테그 <,> 와 같은 특수 문자를 기반으로 정의된다.
만약 원하는 텍스트에 Spring! 테그를 통해 강조하고 싶어서, 해당 문자열을 출력해보면,
강조되지 않고, 텍스트 그대로 출력된다.
왜? 타임리프는 html 테그의 시작인 < 기호를 < 로 변환한다. > 기호를 > 로 변환한다.
이 기능이 바로 Escape 이다.
<h1>text vs utext</h1>
<ul>
<li>th:text = <span th:text="${data}"></span></li>
<li>th:utext = <span th:utext="${data}"></span></li>
</ul>
<h1><span th:inline="none">[[...]] vs [(...)]</span></h1>
<ul>
<li><span th:inline="none">[[...]] = </span>[[${data}]]</li>
<li><span th:inline="none">[(...)] = </span>[(${data})]</li>
</ul>
text vs utext
th:text = Hello <b>Spring!</b>
th:utext = Hello Spring!
[[...]] vs [(...)]
[[...]] = Hello <b>Spring!</b>
[(...)] = Hello Spring!
${…}<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>SpringEL 표현식</h1>
<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>
<ul>List
<li>${users[0].username} = <span th:text="${users[0].username}"></span></li>
<li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span></li>
<li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map
<li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
<li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
<li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
</body>
</html>
@GetMapping("/variable")
public String variable(Model model) {
User userA = new User("userA", 26);
User userB = new User("userB", 26);
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";
}
@Data
static class User{
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
}
→ 이를 해결하기 위해 편의 객체를 제공한다.
${param.paramData}${session.sessionData}@${@helloBean.hello(’Spring!’)}타임리프에서 URL 링크를 만들때는 @{…} 를 사용할 수 있고, 이를 활용하여 path vairable, query parameter 를 사용할 수 있다.
<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>
String name "CJW";
int age = 13 * 2;
→ “CJW” 는 문자 리터럴, 13,2 는 숫자 리터럴이다.
‘ 작은 따옴표로 감싸야 한다.<span th:text="'hello'">‘ 감싸는 것이 귀찮은 것이기 때문에, 타임리프에서는 다른 방식도 지원한다. A-Z , a-z , 0-9 , [ ] , . , - , _ : 해당 문자들로 공백없이 이어진다면, 하나의 의미있는 토큰으로 인지해서 작은 따옴표를 생략할 수 있다.<li>"hello world!" = <span th:text="hello world!"></span></li>
→ 해당 라인은 실행 시 에러가 발생한다.
< , > 에 주의 해야 한다.<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>10 > 1 = <span th:text="10 > 1"></span></li>
<li>1 gt 10 = <span th:text="1 gt 10"></span></li>
<li>10 gt 1 = <span th:text="10 gt 1"></span></li>
<li>1 >= 10 = <span th:text="1 >= 10"></span></li>
<li>10 >= 1 = <span th:text="10 >= 1"></span></li>
<li>1 ge 10 = <span th:text="1 ge 10"></span></li>
<li>10 ge 1 = <span th:text="10 ge 1"></span></li>
<li>1 == 1 = <span th:text="1 == 1"></span></li>
<li>1 != 1 = <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 연산자
<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></li>
</ul>
</li>
th: ??? 를 통해 속성을 지정한다. 지정한 속성이 랜더링되며느 기존 속성을 대체한다.checked=”false” 이더라도, 체크가 된 채 랜더링된다.th: 태그를 사용하면, 이 값을 직접 설정하지 않고, 값을 넘겨받아 지정할 수 있도록 해 준다.<h1>checked 처리</h1>
- checked model attribute = false <input type="checkbox" name="active" th:checked="${checkBoolean}"/><br/>
- 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/>
- none checked <input type="checkbox" name="active"/><br/>
th:each 를 사용하여 반복을 할 수 있다.@GetMapping("/each")
public String each(Model model) {
addUsers(model);
return "/basic/each";
}
private void addUsers(Model model) {
List<User> list = new ArrayList<>();
list.add(new User("userA", 10));
list.add(new User("userB", 20));
list.add(new User("userC", 30));
model.addAttribute("users", list);
}
<table border="1">
<tr>
<th>username</th>
<th>age</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</tr>
</table>
<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
<span th:text="'미성년자'" th:if="${user.age < 20}"></span>
<span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
→ 왜? > 나 < 를 사용해도 되는데 lt, gt, <, >를 사용하는 것을 권장하는가? → 타임리프에서 처리가 가능하긴 하나, 오류 발생 가능성을 줄이기 위해 엔티티 표현법이 적합한 방법이라 할 수 있음.
<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->
<h1>2. 타임리프 파서 주석 </h1>
<h4> 타임리프로 랜더링 할 때 주석처리 됨. </h4>
<!--/* [[${data}]] */-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->
<h1>3. 타임리프 프로토타입 주석</h1>
<h4> 타임리프로 랜더링 할 때는 주석처리 됨. 하지만 html 파일을 직접 열면 랜더링 됨. </h4>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->
<!--/* [[${data}]] */--><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>
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
var user1 = {"username":"userA","age":10};
var user2 = {"username":"userB","age":20};
var user3 = {"username":"userC","age":30};
footer.html
<!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.html
1. <div th:insert="~{template/fragment/footer :: copy}"></div>
2. <div th:replace="~{template/fragment/footer :: copy}"></div>
3. <div th:replace="template/fragment/footer :: copy"></div>
fragmentMain.html - 파라미터 사용
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
layoutMain.html
<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>
base.html
<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>
<title>, <link> 정도가 아닌, <html> 전체에 적용이 가능하다.
layoutFile.html
<!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>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
<p>레이아웃 컨텐츠</p>
</div>
→ 이 부분만 페이지마다 코드를 교체하고 싶고, 나머지 title, footer 는 고정하고 싶다. 해당 레이아웃을 고정하고 싶다 라는 상황!
실행과정
th:replace 가 적용되어 있음. 즉, 전체를 교체하는데, 이를 layoutFile 로 교체한다.th:replate=”${title}” 로 교체된다.th:replate=”${content}” 로 교체된다.