LeeDiculous
article thumbnail
Published 2022. 12. 29. 21:53
Thymeleaf 백엔드 ⏎/thymeleaf

타임리프 특징

서버 사이드 HTML 렌더링 (SSR)

타임리프는 백엔드 서버에서 HTML을 동적으로 렌더링 하는 용도로 사용됩니다.

Natural Template

타임리프는 순수 HTML을 최대한 유지하는 특징이 있습니다.

타임리프로 작성한 파일은 HTML을 유지하기 때문에 웹 브라우저에서 파일을 직접 열어도 내용을 확인할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있습니다.

JSP를 포함한 다른 뷰 템플릿들은 해당 파일을 열면, JSP 소스코드와 HTML이 뒤죽박죽 섞여서 웹 브라우저에서 정상적인 HTML 결과를 확인할 수 없습니다. 오직 서버를 통해서 JSP가 렌더링 되고 HTML 응답 결과를 받아야 화면을 확인할 수 있습니다.

반면에 타임리프로 작성된 파일은 해당 파일을 그대로 웹 브라우저에서 열어도 정상적인 HTML 결과를 확인할 수 있습니다. 물론 이 경우 동적으로 결과가 렌더링 되지는 않습니다. 하지만 HTML 마크업 결과가 어떻게 되는지 파일만 열어도 바로 확인할 수 있습니다.

이렇게 순수 HTML을 그대로 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 Natural Template이라고 합니다.

스프링 통합 지원

타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원합니다.


위에서 타임리프의 특징에 대해서 알아보았습니다. 이제 제가 회사 프로젝트를 살펴보면서 몰랐던 기능이나 익숙하지 않았던 내용에 대해 알아보겠습니다.

타임리프 기능

반복문 사이에 두 번째 파라미터

th:each="board, index : ${boardList}"

반복 시 오른쪽 컬렉션(${boardList})의 값을 하나씩 꺼내서 왼쪽 변수(board)에 담아 태그를 반복 실행합니다.

하지만 위 코드를 보면 중간에 index라는 파라미터가 있습니다. 이는 반복의 두번째 파라미터를 설정해서 반복의 상태를 확인할 수 있게 해주는 것입니다.

(두번째 파라미터는 생략 가능합니다. 생략 시 지정한 변수명(board) + Stat가 됩니다.)

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

리터럴(Literals)

리터럴은 소스 코드상에 고정된 값을 말하는 용어입니다. 예를 들어 다음 코드에서 "Hello"는 문자 리터럴, 10, 20은 숫자 리터럴입니다.

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

타임리프는 다음과 같은 리터럴이 있습니다.

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

타임리프에서 문자 리터럴은 항상 작은따옴표(')로 감싸야 합니다. 하지만 이는 너무 귀찮은 일입니다. 공백 없이 쭉 이어진다면 하나의 의미있는 토큰으로 인지해서 작은 따옴표를 생략할 수 있습니다.

<span th:text="hello">

또 하나의 방법으로 리터럴 대체(Literal substitutions)가 있습니다.

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

위처럼 | | 기호로 리터럴 대체 문법을 사용하면 편리하게 사용할 수 있습니다.

URL 링크

타임리프에서 URL을 생성할 때는 @{...} 문법을 사용하면 됩니다.

1. <li><a th:href="@{/hello}"></a></li>
2. <li><a th:href="@{/hello(param1=${param1}, param2=${param2})}"></a></li>
3. <li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}"></a></li>
  1. 단순한 URL입니다.
  2. /hello?param1=data1&param2=data로 ()부분이 쿼리 파라미터 처리됩니다.
  3. /hello/data1/data2로 URL 경로 상에 변수가 있으면 ()부분은 경로 변수로 처리됩니다.

기본 객체

타임리프는 기본 객체들을 제공합니다.

  • ${#request}
  • ${#response}
  • ${#session}
  • ${#servletContext}
  • ${#locale}

#request의 경우 HttpServletRequest 객체가 그대로 제공되기 때문에 데이터를 조회하려면 request.getParameter("data")처럼 불편하게 접근해야 합니다. 이를 해결하기 위한 객체도 제공합니다.

  • HTTP 요청 파라미터 접근 : param
    • ${param.paramData}
  • HTTP 세션 접근 : session
    • ${session.sessionData}
  • 스프링 빈 접근 : @
    • ${@helloBean.hello('spring!')

날짜 형식 변경

  • String -> Date
<span th:text="${#temporals.format(user.createDate, 'yyyy-MM-dd')}"></span>
  • Date -> Date
<span th:text="${#dates.format(user.createDate, 'yyyy-MM-dd')}"></span>

th:include

찾아보니 Thymeleaf 3.1부터 th:include는 deprecated 되었다고 합니다.thymeleaf.org 

그래서 대체할 수 있는 th:insert와 th:replace에 대해 알아보겠습니다.

<div th:insert="~{template/fragment/footer :: copy}"></div>
th: insert를 사용하면 현재 태그(div) 내부에 추가합니다.

<div th:replace="~{template/fragment/footer :: copy}"></div>
th:replace를 사용하면 현재 태그(div)를 대체합니다.

template/fragment/footer :: copy는 template/fragment/footer.html 템플릿에 있는 th:fragment="copy"라는 부분을 템플릿 조각으로 가져와서 사용한다는 의미입니다.

th:block

<th:block>은 HTML 태그가 아닌 타임리프의 유일한 자체 태그입니다. 개발자가 원하는 속성을 지정할 수 있는 단순한 속성 컨테이너입니다. 각 요소가 둘 이상 필요한 반복 테이블을 만들 때 유용하게 사용할 수 있습니다.

<table>
  <th:block th:each="user : ${users}">
    <tr>
        <td th:text="${user.login}">...</td>
        <td th:text="${user.name}">...</td>
    </tr>
    <tr>
        <td colspan="2" th:text="${user.address}">...</td>
    </tr>
  </th:block>
</table>

속성 추가

  • th:attrappend : 속성 값의 뒤에 값을 추가한다.
  • th:attrprepend : 속성 값의 앞에 값을 추가한다.
  • th:classappend : class 속성에 자연스럽게 추가한다.
<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>속성 설정</h1>
    <input type="text" name="mock" th:name="userA">
    <h2>속성 추가</h2>
    - 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>
</body>
</html>

<!-- 결과 -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>속성 설정</h1>
    <input type="text" name="userA">
    <h2>속성 추가</h2>
    - th:attrappend = <input type="text" class="textlarge"><br>
    - th:attrprepend = <input type="text" class="largetext"><br>
    - th:classappend = <input type="text" class="text large"><br>
</body>
</html>

  • 타임리프에 대해 새로운 부분을 배울 때마다 이 게시물에 하나씩 추가하려 합니다. 아직 모르는 부분도 많지만, 차근차근 배워나가며 자신 있게 사용하는 모습을 꿈꿉니다. - 2022/12/12
  • 속성 추가 항목 추가 - 2022/12/13
profile on loading

Loading...