김영한 강사님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 듣고 정리한 내용입니다. 자세한 내용은 강의를 참고해주세요
이번강의에서는 thymelaf 에 대해서 알아보는 시간이다
<html xmlns:th="http://www.thymeleaf.org">이제 하나하나 알아보자!
th:text 를 사용 : <span th:text="${data}">[[${data}]]EscapeHello <b>Spring!</b>Hello <b>Spring!</b>Unescape${...}th:text="${user.username}"></span>th:text="${user['username']}"></span>th:text="${user.getUsername()}"></span>th:text="${users[0].username}"></span>th:text="${users[0]['username']}"></ span>th:text="${users[0].getUsername()}"></ span>'th:text="${userMap['userA'].username}"></span>th:text="${userMap['userA']['username']}"></span>th:text="${userMap['userA'].getUsername()}"></span>th:with 를 사용하면 지역 변수를 선언해서 사용할 수 있다. 지역 변수는 선언한 테그 안에서만 사용할 수 있다.<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
${#request} - 스프링 부트 3.0부터 제공하지 않는다.${#response} - 스프링 부트 3.0부터 제공하지 않는다.${#session} - 스프링 부트 3.0부터 제공하지 않는다.${#servletContext} - 스프링 부트 3.0부터 제공하지 않는다.${#locale} -> 이것만 지금 사용할 수 있다@GetMapping("basic-objects")
public String basicObjects(Model model, HttpServletRequest request, HttpServletResponse response, HttpSession session){
session.setAttribute("sessionData","Hello Session");
model.addAttribute("request", request);
model.addAttribute("response", response);
model.addAttribute("servletContext", request.getServletContext());
return "basic/basic-objects";
}
<!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>
@{...} 문법 : thymeleaf에서 URL 생성
단순한 URL
쿼리 파라미터
@{/hello(param1=${param1}, param2=${param2})}/hello?param1=data1¶m2=data2경로 변수
@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})} /hello/data1/data2경로 변수 + 쿼리 파라미터
{/hello/{param1}(param1=${param1}, param2=${param2})} /hello/data1?param2=data2<span th:text="'hello'"><span th:text="'hello world!'"></span><span th:text="|hello ${data}|">|| 안에 리터럴을 적어주면 된다!!! -> 여기 안에서는 같은 토큰으로 다 생각함> (gt), < (lt), >= (ge), <= (le), ! (not), == (eq), != (neq, ne)타임리프는 주로 HTML 태그에 th:* 속성을 지정하는 방식으로 동작한다. th:* 로 속성을 적용하면 기존 속성을 대체한다.
기존 속성이 없으면 새로 만든다.
속성 설정
th: 속성을 지정하면 타임리프는 기존 속성을 th: 로 지정한 속성으로 대체한다. 기존 속성이 없다면 새로 만든다.
<input type="text" name="mock" th:name="userA" />
타임리프 렌더링 후 <input type="text" name="userA" />
속성 추가
th:attrappend : 속성 값의 뒤에 값을 추가한다.
th:attrprepend : 속성 값의 앞에 값을 추가한다.
th:classappend : class 속성에 자연스럽게 추가한다
checked 처리
HTML에서는 <input type="checkbox" name="active" checked="false" /> 이 경우에도checked 속성이 있기 때문에 checked 처리가 되어버린다.
HTML에서 checked 속성은 checked 속성의 값과 상관없이 checked 라는 속성만 있어도 체크가 된다.
이런 부분이 true , false 값을 주로 사용하는 개발자 입장에서는 불편하다.
타임리프의 th:checked 는 값이 false 인 경우 checked 속성 자체를 제거한다.
<input type="checkbox" name="active" th:checked="false" /><input type="checkbox" name="active" />th:each 를 사용한다. <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>기본 테이블</h1>
<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>
<h1>반복 상태 유지</h1>
<table border="1">
<tr>
<th>count</th>
<th>username</th>
<th>age</th>
<th>etc</th>
</tr>
<tr th:each="user, userStat : ${users}">
<td th:text="${userStat.count}">username</td>
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
<td>
index = <span th:text="${userStat.index}"></span>
count = <span th:text="${userStat.count}"></span>
size = <span th:text="${userStat.size}"></span>
even? = <span th:text="${userStat.even}"></span>
odd? = <span th:text="${userStat.odd}"></span>
first? = <span th:text="${userStat.first}"></span>
last? = <span th:text="${userStat.last}"></span>
current = <span th:text="${userStat.current}"></span>
</td>
</tr>
</table>
</body>
</html>
<tr th:each="user : ${users}">( ${users} )의 값을 하나씩 꺼내서 왼쪽 변수( user )에 담아서 태그를 반복 실행합니다.th:each 는 List 뿐만 아니라 배열, java.util.Iterable,java.util.Enumeration 을 구현한 모든 객체를 반복에 사용할 수 있습니다. 반복 상태 유지
<tr th:each="user, userStat : ${users}">변수명( user ) + Stat 가 됩니다.userStat 이므로 생략 가능합니다반복 상태 유지 기능
index : 0부터 시작하는 값count : 1부터 시작하는 값size : 전체 사이즈even , odd : 홀수, 짝수 여부( boolean )first , last :처음, 마지막 여부( boolean )current : 현재 객체if, unless<span>...<span> 부분 자체가 렌더링 되지 않고 사라진다.<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>switch*은 만족하는 조건이 없을 때 사용하는 디폴트이다.<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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 lt 20}"></span>
<span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
</td>
</tr>
</table>
<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>
</body>
</html>
<!-- <span th:text="${data}">html data</span> --><!--/* [[${data}]] */--><!--/*/ <span th:text="${data}">html data</span> /*/--><th:block> 은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다.<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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>
</body>
</html>
<div>2개를 딱딱 맞춰서 반복하고 싶다<div>1개는 그냥 <div th:each="user:${users}">써서 하면 되는데,, 이렇게 속성 단위로!each만으로 해결하기 어려울 때, th:block을 같이 쓴다<script th:inline="javascript">자바스크립트 인라인 each
자바스크립트 인라인은 each를 지원하는데, 다음과 같이 사용한다.
<!-- 자바스크립트 인라인 each -->
<script th:inline="javascript">
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
이번 예시에서는 html의 하단 부분을 footer로 지정하고, 이를 다른 html에 적용하는 예제를 보겠다!
<footer th:fragment="copy">
푸터 자리 입니다.
</footer>
fragment : 템플릿 조각의 이름 -> "copy"로 두었다<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>
template/fragment/footer :: copy : template/fragment/footer.html 템플릿에 있는 th:fragment="copy" 라는 부분을 템플릿 조각으로 가져와서 사용한다는 의미이다.
insert
<div th:insert="~{template/fragment/footer :: copy}"></div>
replace
<div th:replace="~{template/fragment/footer :: copy}"></div>
파라미터 사용
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
<!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>
<!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> 에 th:fragment 속성이 정의되어 있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.layoutExtendMain.html 는 현재 페이지인데, <html> 자체를 th:replace 를 사용해서 변경하는 것을 확인할 수 있다.<html> 자체를 layoutFile.html 로 변경한다.