LostCatBox

SpringMVC2-CH01(스프링 thymeleaf)

Word count: 3.9kReading time: 24 min
2023/02/16 Share

참조

인프런 김영한 springmvc2편

왜?

김영한의 spring mvc 강의를 들으면 정리하고싶었다.

이번편에는 타임리프가 나왔는데 회사에서는 jsp를 활용중이다.

사실 오래된 기술을 사용하고 그렇다보니, war파일을 tomcat에 직접넣어줄수밖에없었다.

나도 jar파일쓰고, thymeleaf 쓰면서 비즈니스 로직과 분리를 이루고싶다.

스크린샷 2023-02-16 오후 10.44.26

간단한 jsp VS thymeleaf 차이

jsp

  • 서블릿으로 형태가 변환되어 실행된다.
    서블릿은 자바소스라서 jsp에서 자바코드를 사용하는 것이 가능하다.
    따라서 view에 비즈니스 로직이 포함되어 복잡하고 무거워진다.
  • 서버 사이드 렌더링
  • 반드시 war 패키징만 가능하다.
  • 반드시, 웹서버나 war가 필요하고, web-inf 디렉토리에서 사전 정의된 구조만 사용가능하다.

thymeleaf

  • 서블릿형태가 아닌 html 형태
  • 웹서버나 was없이 단독으로 브라우저에서 직접 띄울수있다.
  • jar, war 패키징 상관없이 둘다 가능하다.
  • 타임리프는 순수 HTML을 최대한 유지하는 특징(서버없어도됨.)

타임리프 - 기본기능

타임리프 depend 설정

1
2
3
 implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

타임리프 사용 선언

html 태그 에 넣기

1
<html xmlns:th="http://www.thymeleaf.org">

기본 표현식

  • 간단한 표현:
    • 변수 표현식: ${…}
    • 선택 변수 표현식: *{…}
    • 메시지 표현식: #{…}
    • 링크 URL 표현식: @{…}
    • 조각 표현식: ~{…}
  • 리터럴
    • 텍스트: ‘one text’, ‘Another one!’,…
    • 숫자: 0, 34, 3.0, 12.3,…
    • 불린: true, false
    • 널: null
    • 리터럴 토큰: one, sometext, main,…
  • 문자 연산:
    ◦ 문자합치기:+

◦ 리터럴 대체: |The name is ${name}|

  • 산술 연산:
    • Binary operators: +, -, *, /, %
    • Minus sign (unary operator): -
  • 불린 연산:
    • Binary operators: and, or
    • Boolean negation (unary operator): !, not
  • 비교와 동등:
    ◦ 비교:>,<,>=,<=(gt,lt,ge,le)

◦ 동등 연산: ==, != (eq, ne)

  • 조건 연산:
    • If-then: (if) ? (then)
    • If-then-else: (if) ? (then) : (else)
    • Default: (value) ?: (defaultvalue)
  • 특별한 토큰:
    • No-Operation: _

텍스트 - text, utext

escape

  • th:text
  • [[…]]

HTML 엔티티
웹브라우저는 <를HTML테그의 시작으로 인식한다.

<를테그의시작이아니라문자로표현할수 있는 방법이 필요한데, 이것을 HTML 엔티티라 한다.

그리고 이렇게 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 이스케이프(escape)라 한다.

타임리프가 제공하는 th:text , [[…]] 는 기본적으로 이스케이스(escape)를 제공한다.

unescape

이스케이프 기능을 사용하지 않으려면 어떻게 해야할까?

타임리프는 다음 두 기능을 제공한다.

  • th:utext
  • [(…)]

예시

  • BasicController
1
2
3
4
5
6
7
8
9
10

@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("/text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "Hello Spring!");
return "basic/text-basic";
}
}
  • Basic/text-basic.html
1
2
3
4
5
6
7
8
9
10
11
12
13
  <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
<li>th:text 사용 <span th:text="${data}"></span></li> <li>컨텐츠 안에서 직접 출력하기 = [[${data}]]</li>
</ul>
</body>
</html>

변수 - SpringEL

스프링이 내부에서 객체를 접근할때 활용하는 표현식

타임리프에서도 그대로 사용함

  • Object

  • ${user.username} = userA

  • ${user[‘username’]} = userA
    이방식은 문자로 해서 property꺼내기쉬운방법

  • ${user.getUsername()} = userA

  • List

  • ${users[0].username} = userA

  • ${users[0][‘username’]} = userA
    이방식은 문자로 해서 property꺼내기쉬운방법

  • ${users[0].getUsername()} = userA

  • Map

  • ${userMap[‘userA’].username} = userA

  • ${userMap[‘userA’][‘username’]} = userA
    이방식은 문자로 해서 property꺼내기쉬운방법

  • ${userMap[‘userA’].getUsername()} = userA

지역 변수 선언

th:with 를 사용하면 지역 변수를 선언해서 사용할 수 있다. 지역 변수는 선언한 테그 안에서만 사용할 수 있다.

1
2
3
<div th:with="first=${users[0]}">
<p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>

기본 객체들

타임리프는 기본 객체들을 제공한다. 하지만 스프링 부트 3.0부터 제공하지 않는다

1
2
3
4
5
${#request} 
${#response}
${#session}
${#servletContext}
${#locale}

3.0부터는 하위와 같이 spring에서 직접 model에 넣어줘서 사용해야한다.

  • HTTP 요청 파라미터 접근: param
    • 예) ${param.paramData}
  • HTTP 세션 접근: session
    • 예) ${session.sessionData}
  • 스프링 빈 접근: @
    • 예) ${@helloBean.hello(‘Spring!’)}
    • 참고로 bean으로 등록된 객체를 사용할수있는 문법도있다
      ${@helloBean.hello('Spring!')} 접근방식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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";
}
@Component("helloBean")
static class HelloBean {
public String hello(String data) {
return "Hello " + data;
} }
  • html
1
2
3
4
5
6
7
8
9
10
11
12
<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>

유틸리티 객체와 날짜

타임리프 유틸리티 객체들

  • #message : 메시지, 국제화 처리
  • #uris : URI 이스케이프 지원
  • #dates : java.util.Date 서식 지원
  • #calendars : java.util.Calendar 서식 지원
  • #temporals : 자바8 날짜 서식 지원
  • #numbers : 숫자 서식 지원
  • #strings : 문자 관련 편의 기능
  • #objects : 객체 관련 기능 제공
  • #bools : boolean 관련 기능 제공
  • #arrays : 배열 관련 기능 제공
  • #lists , #sets , #maps : 컬렉션 관련 기능 제공
  • #ids : 아이디 처리 관련 기능 제공, 뒤에서 설명

날짜

타임리프에서 자바8 날짜인 LocalDate , LocalDateTime , Instant 를 사용하려면 추가 라이브러리가 필요하다. 스프링 부트 타임리프를 사용하면 해당 라이브러리가 자동으로 추가되고 통합된다.

타임리프 자바**8** 날짜 지원 라이브러리

thymeleaf-extras-java8time

  • Controller
1
2
3
4
5
6
@GetMapping("/link")
public String link(Model model) {
model.addAttribute("param1", "data1");
model.addAttribute("param2", "data2");
return "basic/link";
}
  • html
    • 하위에는 get parameter 붙이는법, url 경로 동적 변경도 포함되어있다.
    • th:href="@{/hello}" 가장 단순한 구조
    • parameter 는 @{/hello(param1=${param1}, param2=${param2})} 으로 활용한다.
1
2
3
4
5
6
<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>

리터럴

리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.

예를 들어서 다음 코드에서 “Hello” 는 문자 리터럴, 10 , 20 는 숫자 리터럴이다.

1
2
String a = "Hello"
int a = 10 * 20

타임리프는 반드시 작은 따옴표로 감싸야한다

  • 문자: ‘hello’
  • 숫자: 10
  • 불린: true , false
  • null: null

그런데문자를항상 ‘로감싸는것은너무귀찮은일이다.

공백없이쭉이어진다면하나의의미있는 토큰으로 인지해서 다음과 같이 작은 따옴표를 생략할 수 있다.

1
<span th:text="hello">

자주하는 오류

오류

<span th:text="hello world!"></span>
문자 리터럴은 원칙상 ‘ 로 감싸야 한다.

중간에 공백이 있어서 하나의 의미있는 토큰으로도 인식되지 않는다.

수정

<span th:text="'hello world!'"></span>

이렇게 ‘ 로 감싸면 정상 동작한다.

리터럴 대체**(Literal substitutions)**

<span th:text="|hello ${data}|">

마지막의 리터럴 대체 문법을 사용하면 마치 템플릿을 사용하는 것 처럼 편리하다.

연산

타임리프 연산은 자바와 크게 다르지 않다.

HTML안에서 사용하기 때문에 HTML 엔티티를 사용하는 부분만 주의하자.

  • 비교연산: HTML 엔티티를 사용해야 하는 부분을 주의하자,
    > (gt), < (lt), >= (ge), <= (le), ! (not), == (eq), != (neq, ne)
  • 조건식: 자바의 조건식과 유사하다.
  • Elvis 연산자: 조건식의 편의 버전
  • No-Operation: _ 인 경우 마치 타임리프가 실행되지 않는 것 처럼 동작한다. 이것을 잘 사용하면 HTML 의 내용 그대로 활용할 수 있다. 마지막 예를 보면 데이터가 없습니다. 부분이 그대로 출력된다.

예시

  • Controller
1
2
3
4
5
6
  @GetMapping("/operation")
public String operation(Model model) {
model.addAttribute("nullData", null);
model.addAttribute("data", "Spring!");
return "basic/operation";
}
  • html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<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 &gt; 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 == 1 = <span th:text="1 == 10"></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>
</ul>

</body>
</html>

속성값 설정

타임리프 태그 속성(Attribute)
타임리프는 주로 HTML 태그에 th:* 속성을 지정하는 방식으로 동작한다. th:* 로 속성을 적용하면 기존

기존 속성을 대체한다. 기존 속성이 없으면 새로 만든다.

속성 설정

th:* 속성을 지정하면 타임리프는 기존 속성을 th:* 로 지정한 속성으로 대체한다. 기존 속성이 없다면 새로 만든다.

1
<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" />

1
2
3
4
5
6
7
8
9
<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/>

반복

타임리프에서 반복은 th:each 를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.

반복 기능

<tr th:each="user : ${users}">
반복시 오른쪽 컬렉션( ${users} )의 값을 하나씩 꺼내서 왼쪽 변수( user )에 담아서 태그를 반복 실행합니다.

th:each 는 List 뿐만 아니라 배열, java.util.Iterable , java.util.Enumeration 을 구현한 모든 객체를 반복에 사용할 수 있습니다. Map 도 사용할 수 있는데 이 경우 변수에 담기는 값은 Map.Entry 입니다.

반복 상태 유지(재밋는 기능)

1
<tr th:each="user, userStat : ${users}">

반복의 두번째 파라미터를 설정해서 반복의 상태를 확인 할 수 있습니다.
두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명( user ) + Stat 가 됩니다. 여기서는 user + Stat = userStat 이므로 생략 가능합니다.

반복 상태 유지 기능

index : 0부터 시작하는 값
count : 1부터 시작하는 값
size : 전체 사이즈
even , odd : 홀수, 짝수 여부( boolean ) first , last :처음, 마지막 여부( boolean ) current : 현재 객체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<tr th:each="user : ${users}">
<td th:text="${user.username}">username</td>
<td th:text="${user.age}">0</td>
</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>

조건부 평가

타임리프의 조건식

  • if
  • unless(if의 반대)
  • switch

if, unless

타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.
만약 다음 조건이 false 인 경우 <span>...<span> 부분 자체가 렌더링 되지 않고 사라진다.

<span th:text="'미성년자'" th:if="${user.age lt 20}"></span>

1
2
3
4
5
6
7
8
9
<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>

switch

타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.

* 은 만족하는 조건이 없을 때 사용하는 디폴트이다.

1
2
3
4
5
6
7
8
9
<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>

주석

  • 표준 HTML 주석은 html파일에 반영은 된다.렌더링은 안됨
  • 타임리프 파서 주석은 html파일에 아예 반영 안된다. 당연히 렌더링도안된다.
  • 타임리프 프로토타입 주석(특이함. 타임리프가 SSR될때만 내용보임)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->

<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->

<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->

<h1>3. 타임리프 프로토타입 주석</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->

블록

  • <th:block> 은 HTML 태그가 아닌 타임리프의 유일한 자체 태그다.

  • 여러 태그를 가진 구성요소를 반복문 한번에 돌림 (each만으로 해결어려울때)

1
2
3
4
5
6
7
8
9
<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>

자바스크립트 인라인(javascript inline)

타임리프는 자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다. 자바스크립트 인라인 기능은 다음과 같이 적용하면 된다.

자바스크립트와 타임리프 문법 동시 사용가능

<script th:inline="javascript">

  • 사용전
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<!-- 자바스크립트 인라인 사용 전 -->
<script>
//var username = "user.username" 해야하는데 이경우는 var username = userA로되어 error발생
var username = [[${user.username}]];
var age = [[${user.age}]];

//자바스크립트 내추럴 템플릿
var username2 = /*[[${user.username}]]*/ "test username";

//객체
//user.ToString이 호출되어버림
//var user = BasicController.User(username=UserA, age=10);
var user = [[${user}]];
</script>
  • 사용후
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
//var username = "userA" 정상동작
var username = [[${user.username}]];
var age = [[${user.age}]];

//자바스크립트 내추럴 템플릿
//var username2 = "UserA";
var username2 = /*[[${user.username}]]*/ "test username";

//객체
//자동으로 json형태로 바꿈
//var user = {"username":"UserA","age":10};
var user = [[${user}]];
</script>


<!-- 자바스크립트 인라인 each -->

<script th:inline="javascript">

//var user1 = {"username":"userA","age":10};
//var user2 = {"username":"userB","age":20};
//var user3 = {"username":"userC","age":30};
[# th:each="user, stat : ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]

</script>

템플릿 조각

웹 페이지를 개발할 때는 공통 영역이 많이 있다.

예를 들어서 상단 영역이나 하단 영역, 좌측 카테고리 등등 여러 페이지에서 함께 사용하는 영역

타임리프는 이런 문제를 해결하기 위해 템플릿 조각과 레이아웃 기능을 지원한다.

th:fragment="name" name을 지정해서 쓰면된다.

insert, replace 쓸수있다. 용도에 맞게 쓰자

  • footer.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!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>

템플릿 레이아웃 1

완성할때 내가 원하는 요소들을 템플릿에 집어넣어서 완성함

훨씬 복잡한 태그들을 넘길수있다.

레이아웃 개념을 두고, 그 레이아웃에 필요한 코드 조각을 전달해서 완성하는 것으로 이해하면 된다.

  • Base.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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>

common_header({::title},{::link}) 이 부분이 핵심이다. ::title 은 현재 페이지의 title 태그들을 전달한다.
::link 는 현재 페이지의 link 태그들을 전달한다.

  • layoutMain.html
1
2
3
4
5
6
7
8
9
10
11
<!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>
  • 결과
1
2
3
4
5
6
7
8
9
10
11
12
13
<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>

템플릿 레이아웃2

Html 자체를 th:replace 한다!

layoutFile.html 을 보면 기본 레이아웃을 가지고 있는데, <html>th:fragment 속성이 정의되어 있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.

  • layoutFile.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!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>
  • layoutExtendMain
1
2
3
4
5
6
7
8
9
10
11
12
13
<!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>
CATALOG
  1. 1. 참조
  2. 2. 왜?
  3. 3. 간단한 jsp VS thymeleaf 차이
    1. 3.1. jsp
    2. 3.2. thymeleaf
  4. 4. 타임리프 - 기본기능
    1. 4.1. 타임리프 depend 설정
    2. 4.2. 타임리프 사용 선언
    3. 4.3. 기본 표현식
    4. 4.4. 텍스트 - text, utext
      1. 4.4.1. escape
      2. 4.4.2. unescape
      3. 4.4.3. 예시
    5. 4.5. 변수 - SpringEL
      1. 4.5.1. Object
      2. 4.5.2. List
      3. 4.5.3. Map
      4. 4.5.4. 지역 변수 선언
    6. 4.6. 기본 객체들
    7. 4.7. 유틸리티 객체와 날짜
      1. 4.7.1. 타임리프 유틸리티 객체들
      2. 4.7.2. 날짜
    8. 4.8. Url Link
    9. 4.9. 리터럴
      1. 4.9.1. 자주하는 오류
      2. 4.9.2. 리터럴 대체**(Literal substitutions)**
    10. 4.10. 연산
      1. 4.10.1. 예시
    11. 4.11. 속성값 설정
    12. 4.12. 반복
    13. 4.13. 조건부 평가
      1. 4.13.1. 타임리프의 조건식
      2. 4.13.2. if, unless
      3. 4.13.3. switch
    14. 4.14. 주석
    15. 4.15. 블록
    16. 4.16. 자바스크립트 인라인(javascript inline)
    17. 4.17. 템플릿 조각
    18. 4.18. 템플릿 레이아웃 1
      1. 4.18.1. 템플릿 레이아웃2