Front-End/thymeleaf

Thymeleaf 공부하기 (2) : 속성 값 설정, 반복, 조건, 주석, 블록, 자바스크립트 인라인, 템플릿 조각, 템플릿 레이아웃

TerianP 2022. 10. 11.
728x90

1. 속성 값 설정

타임리프는 주로 HTML 태그에 th:'속성명' 을 지정하는 방식으로 동작한다.
th:'속성명' 로 속성을 적용하면 기존 속성을 대체한다. 만약 기존 속성이 없으면 새로 만든다.
checkbox 같은 경우 th:checked="true, false" 로 사용한다
<h1>속성 설정</h1>
<input type="text" name="mock" th:name="userA" />

<h1>속성 추가</h1>
- th:attrappend class 속성 뒤에 붙는다 = <input type="text" class="text" th:attrappend="class=' large'" /><br/>
- th:attrprepend class 속성 앞에 붙는다 = <input type="text" class="text" th:attrprepend="class='large '" /><br/>
- th:classappend class 속성에 자연스럽게 추가 = <input type="text" class="text" th:classappend="자연스럽게"><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/>

 

2. 반복문

타임리프에서 반복은 th:each 를 사용한다. 추가로 반복에서 사용할 수 있는 여러 상태 값을 지원한다.
index : 0부터 시작하는 값
count : 1부터 시작하는 값
size : 전체 사이즈
even , odd : 홀수, 짝수 여부( boolean )
first , last :처음, 마지막 여부( boolean )
current : 현재 객체
<h1>기본 테이블</h1>
<table border="1">
  <tr>
    <th>username</th>
    <th>age</th>
  </tr>
  <tr th:each="user : ${characters}">
    <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 : ${characters}">
    <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>

 

 

3. 조건문

if, unless : 타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.
만약 다음 조건이 false 인 경우 ${...} 부분 자체가 렌더링 되지 않고 사라진다
switch : th:switch 와 th:case 를 사용한다. th:case="*" 인 경우 어떤 조건에도 만족하지 않는 기타 에 해당하는 내용을 출력한다

 

<table border="1">
  <tr>
    <th>count</th>
    <th>username</th>
    <th>age</th>
  </tr>
  <tr th:each="user, userStat : ${characters}">
    <td th:text="${userStat.count}"></td>
    <td th:text="${user.username}">username</td>
    <td>
      <span th:text="${user.age}">0</span>
      <span th:text="'100보다 작거나 같다'" th:if="${user.age lt 100}"></span>
      <span th:text="'100보다 크거나 같지 않다'" th:unless="${user.age ge 100}"></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 : ${characters}">
    <td th:text="${userStat.count}">1</td>
    <td th:text="${user.username}">username</td>
    <td th:switch="${user.age}">
      <span th:case="20">[[${user.username}]]</span>
      <span th:case="1000">[[${user.username}]]</span>
      <span th:case="*">[[${user.username}]]</span>
    </td>
  </tr>
</table>

 

 

4. 주석

1. 표준 HTML 주석 :  자바스크립트의 표준 HTML 주석은 타임리프가 렌더링 하지 않고, 그대로 남겨둔다.
<!-- 내용 -->


2. 타임리프 파서 주석 : 타임리프에서 사용하는 진짜 주석이다. 렌더링에서 주석 부분을 제거한다.
<!--/* 내용 */-->

3. 타임리프 프로토타입 주석 : 얘는 약간 특이한데, HTML 주석에 약간의 구문을 더했다. HTML 파일을 웹 브라우저에서 그대로 열어보면 HTML 주석이기 때문에 이 부분이 웹 브라우저가 렌더링하지 않는다. 타임리프 렌더링을 거치면 이 부분이 정상 렌더링 된다.
=> 즉 html 파일 자체를 열때는 주석처리되어서 보이지만, 타임리프로 랜더링해서 보여줄 때는 내용을 보여주게 된다
<!--/*/<span th:text="${...}"></span>/*/-->
<h1>예시</h1>
<span th:text="${data}">html data</span>
<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->
<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->
<!--/*  내용 */-->
<!--*/-->
<h1>3. 타임리프 프로토타입 주석</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->

 

5. 블록

<th:block th:each="user : ${characters}">
  <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>

 

6. 자바스크립트 인라인 : 특히 중요

1) 자바스크립트 인라인 사용하기

<!-- 자바스크립트 인라인 사용 전 -->
<script>
    var username1 = [[${user.username}]];
    var age1 = [[${user.age}]];
    //자바스크립트 내추럴 템플릿
    var username2 = /*[[${user.username}]]*/ "test username";
    //객체
    var user1 = [[${user}]];
</script>
<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
    var username3 = [[${user.username}]];
    var age2 = [[${user.age}]];
    //자바스크립트 내추럴 템플릿
    var username4 = /*[[${user.username}]]*/ "test username";
    //객체
    var user2 = [[${user}]];
</script>

인라인이 아닌 username1 변수의 경우 에러가 발생하지만 인라인으로 설정된 username3 과 user2 는 에러가 발생하지 않는다.

 

2) 인라인 자바스크립트 each

자바스크립트 안에서 thymeleaf 의 each 반복문을 돌릴 수 있다
[# th:each="반복변수: ${반복대상}"]
~~~ 반복 내용 ~~~~ 
[/](끝)
<script th:inline="javascript">
    [# th:each="user : ${characters}"]
    var user[[${userStat.count}]] = [[${user}]];
    [/]
</script>

username 이 \uCF00\uB9AC\uAC74 이런식으로 나오지만, 한글로 잘 담긴다

 

7. 템플릿 조각 : 진짜 중요1

1) 템플릿 조각 사용 방법

태그와 함께 삽입할 템플릿 조각의 경로 지정 ~{경로 :: 템플릿 조각의 이름}템플릿 조각의 이름은 th:fragment="이름" 에서 이름 부분에 해당한다

insert : insert 는 말 그대로 삽입!! 때문에 불러오는 쪽에서 쓰이는 태그 + 템플릿 조각의 태그 까지 모두 들어간다.
-> th:insert="~{경로 :: 템플릿 조각이름}"

replace : replace 는 말 그대로 대체!! 때문에 불러오는 쪽에서 쓰이는 태그를 없애고, 템플릿 조각의 태그가 그 자리를 대체한다.
-> 일반 표현식 : th:replace="~{경로 :: 템플릿 조각이름}"
-> 단순 표현식(단순히 경로+이름만 으로 조각을 넣어도 되는 경우) th:replace="경로 :: 템플릿 조각이름" 

파라미터 사용 : 파라미터가 있는 템플릿 조각의 경우 경로+조각명(파라미터) 를 넣어주면 넣어진 파라미터의 값이 그대로 템플릿 조각에게 넘어가고, 템플릿 조각에서 사용할 수 있게 된다.
-> th:replace="~{경로 :: 템플릿 조각명('파라미터1', '파라미터2'...)}"

 

2) 코드

footer

<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>

 

main

<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>

 

8. 템플릿 레이아웃 - 기본

템플릿 조각이 조각의 일부를 가지고와서 사용했다면 레이아웃은 좀 더 확장된 개념이다.
레이아웃은 일종의 배경그림을 만들어두고, 각 페이지마다 배경그림은 동일하게 세부적인 내용만 내가 수정해서 사용하는 것이다.
즉 <head> 에 속한 JS, CSS 를 갖고 레이아웃을 만들고, 이를 가져와서 각 페이지에 맞게 수정해서 사용하는 것이다.

1) 템플릿 레이아웃 사용방법 

- 템플릿 레이아웃은 템플릿 조각과 동일하게 태그와 함께 사용한다. 이때 단순한 값을 넘기는 것이 아닌 태그를 넘길수도 있다. 이때 태그는 ~{::태그} 를 사용한다. 단순한 값은 '값' 을 사용한다
- 미리 생성된 레이아웃에 맞춰서 넘어온 태그의 내용이
=>  replace 의 경우 레이아웃에 맞춰  대체된다.
=> insert 의 경우 레이아웃에 맞춰 추가된다.

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

 

2) 코드

layout

- insert_body 와 replace_body 를 잘 기억해두자

- replace title 도 기억해두자

<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>
<body>
    <ul th:fragment="insert_body(data)">
        <li th:text="${data}"></li>
    </ul>
    <ul th:fragment="replace_body(data)">
        <li th:text="${data}"></li>
    </ul>
</body>

 

Main

- th:replace layout 에 title 과 link 태그가 모두 넘어간다 => 태그를 넘길때는 {::태그명} 을 사용한다

- div 의 경우 하나는 insert, 하나는 replace 이다. 이에 따라서 각 내용이 추가, 대체 될 것이다

<head th:replace="template/layout/baselayout :: common_header(~{::title},~{::link})">
    <title>넘어가서 replace 되는 타이틀</title>
    <link rel="stylesheet" th:href="@{/css/넘어가서 replace 되는 링크1}">
    <link rel="stylesheet" th:href="@{/themes/넘어가서 replace 되는 링크2}">
</head>
<body>
메인 컨텐츠
<div th:insert="template/layout/baselayout :: insert_body('추가되는 내용')"></div>
<div th:replace="template/layout/baselayout :: replace_body('대체되는 내용')">
    원본 내용~~~~!!!!
</div>
</body>

 

link 를 통해 넘어간 링크1, 링크2 가 replace 되었다. 추가로 insert_body 는 div 와 함께 그대로 추가 되었고, replace_body 는 div 를 대체되었다

 

 

9. 템플릿 레이아웃 - 고급

 

1) 템플릿 레이아웃 고급편

코드를 자세히 확인하면 <html> 에 th:fragment 속성이 정의되어 있다. 이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것으로 이해하면 된다.
즉 html 자체를 th:replace 를 사용해서 변경하는 것이다. 결국 layout.html 에 필요한 내용만을 전달하고 폼은 layout.html 으로 변경한다.

 

2) 코드

layout

<!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>

 

layoutMain

<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layout :: layout(~{::title}, ~{::section})}"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <title>layout으로 넘어가는 타이틀</title>
</head>
<body>
<section>
<h3>레이아웃 기능을 잘 사용하면 html 전체를 혹은 body 전체를 replace 가능하다</h3>
<h3>특히 각페이지의 동일한 부분은 그대로 살리고, 다른 부분만 변경하고 싶을 때 편리하다</h3>
<h3>아래의 section 부분과 위쪽의 title 부분이 넘어가서 title 과 아래의 section 부분만 레이아웃 폼에 맞게 변경된다</h3>
    <ul>
        <li>각 페이지에 맞게 변경되는 부분1</li>
        <li>각 페이지에 맞게 변경되는 부분2</li>
    </ul>
</section>
</body>
</html>


Thymeleaf 문법 정리

문법 설명
th:'html속성명' 기존의 속성을 대체, 만약 해당 속성이 없다면 추가
th:checked="true, false" true 인 경우 체크박스 체크
false 인 경우 checked 속성 자체를 제거 => 체크 없음
th:each="반복에 사용하는 변수 : ${반복대상}" 반복문 사용법
index
count
size
even, oadd
first, last
current
변수명.stat
index : 0부터 시작하는 값
count : 1부터 시작하는 값
size : 전체 사이즈
even , odd : 홀수, 짝수 여부( boolean )
first , last :처음, 마지막 여부( boolean )
current : 현재 객체
변수명.stat : 반복의 상태
th:if="${조건식}", th:unless="${조건식}" ${,,,,} 에 해당하는 조건에 맞게 출력
th:switch="${조건에 사용되는 변수}"
th:case="조건"
th:case="*"
swtich 조건식
case="*" 의 경우 어떤 조건에도 맞지 않는 경우 사용한다
<!--/* 내용 */--> 타임리프에서 사용하는 주석으로 렌더링에서 주석 부분을 제거
<th:block></th:block> 해당 태그 내부를 하나의 블록으로 잡는다 => 여러개의 태그라도 하나의 태그로 만드는 느낌?
<script th:inline="javascript"> thymeleaf 가 지원하는 JS
- var name = [[${.....}]] 으로 값을 바로 담을 수 있다
- var object = [[${...}]] 으로 객체를 담을 수 있다
   => 이때 담겨진 객체는 JSON 형태로 변환되어 담긴다
th:insert="~{경로 :: 템플릿 조각이름}" insert 는 말 그대로 삽입!! 때문에 불러오는 쪽에서 쓰이는 태그 + 템플릿 조각의 태그
th:replace="~{경로 :: 템플릿 조각이름}" 불러오는 쪽에서 쓰이는 태그를 없애고, 템플릿 조각의 태그가 그 자리를 대체
th:replace="경로 :: 템플릿 조각이름"  replace 단순 표현식
th:replace="~{경로 :: 템플릿 조각명('파라미터1', '파라미터2'...)}" 템플릿 조각 파라미터 사용하기
th:replace="~{경로 :: 템플릿 조각이름(~{::태그명})}" 템플릿 조각에 태그를 넘겨서 레이아웃에 맞춰 해당 태그를 변경할 수 있다

댓글