제육's 휘발성 코딩
반응형

타임리프 소개

https://www.thymeleaf.org/

  • 서버 사이드 HTML 렌더링 (SSR) : 백엔드 서버에서 HTML을 동적으로 렌더링하는 용도로 사용된다.
  • 네츄럴 템플릿 : 순수 HTML을 최대한 유지하는 특징 (정적, 동적 둘다 사용 가능)
  • 스프링 통합 지원 : 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원

타임리프 표현식

<html xmlns:th="http://www.thymeleaf.org">
  • html 태그에 타임리프를 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
  • gradle에 타임리프 라이브러리 추가

간단한 표현

  • 변수 표현식: ${...}
  • 선택 변수 표현식: *{...}
  • 메시지 표현식: #{...}
  • 링크 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

image

  • 타임리프는 HTML의 콘텐츠에 데이터를 출력할 때 th:text를 사용한다.
  • 컨텐츠 안에서 직접 출력하고 싶은 경우 [[${data}]]를 사용한다.
  • 타임리프는 [[]]를 해석하기 때문에 th:inline="none"으로 해석하지 말라는 옵션을 지정할 수 있다.

Escape vs Unescape

  • html은 <,>를 태그의 시작으로 인식한다. 해당 문자들을 문자로 표현할 수 있는 방법이 필요한데, 이를 HTML 엔티티라 한다. 즉, 특수 문자를 HTML 엔티티로 변경하는 것을 Escape라고 한다. 타임리프에는 th:text, [[...]]에서 기본적으로 이스케이프를 제공한다.
  • 즉, HTML 엔티티를 사용하지 않아야 원하는 태그를 사용할 수 있고 이것을 Unescape라고 한다. 타임리프에서는 th:utext, [()]를 제공하여 unescape를 사용한다. 실제 서비스를 개발하다보면 escape를 사용하지 않아서 HTML이 정상 렌더링이 되지 않는 현상이 발생하므로 필요할 때만 unescape를 사용하자.

SpringEL

image

  • 변수 표현식에는 스프링 EL이라는 스프링이 제공하는 표현식을 이용할 수 있다.
<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
    <p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>
  • th:with를 사용하면 지역 변수를 선언해서 해당 태그 안에서 사용할 수 있다.

타임리프 객체

image

  • ${#request}, ${#response}, ${#session}, ${#servletContext},${locale}등의 객체를 제공한다. 하지만 #requestHttpServletRequest 객체를 그대로 제공하기 때문에 데이터를 조회하려면 request.getParameter("data")처럼 예전 방식으로 접근해야 한다. 이런 불편한 부분들을 해결하기 위해 편의 객체도 제공한다.
  • 편의 객체
    • HTTP 요청 파라미터 접근 : param
    • HTTP 세션 접근 : session
    • 스프링 빈 접근 : @

유틸리티 객체와 날짜

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expression-utility-objects

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

URL 링크

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>URL 링크</h1>
<ul>
    <li><a th:href="@{/hello}">basic url</a></li>
    <!-- http://localhost:8080/hello?param1=data1&param2=data2 -->
    <li><a th:href="@{/hello(param1=${param1}, param2=${param2})}">hello query param</a></li>
    <!-- http://localhost:8080/hello/data1/data2 -->
    <li><a th:href="@{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}">path variable</a></li>
    <!-- http://localhost:8080/hello/data1?param2=data2-->
    <li><a th:href="@{/hello/{param1}(param1=${param1}, param2=${param2})}">path variable + query parameter</a></li>
</ul>
</body>
</html>
  • 타임리프에서 URL을 생성할 때는 @{...} 문법을 사용하면 된다.

리터럴

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body> <h1>리터럴</h1>
<ul>
    <!--주의! 다음 주석을 풀면 예외가 발생함-->
    <!--    <li>"hello world!" = <span th:text="hello world!"></span></li>-->
    <li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li> <li>'hello world!' = <span th:text="'hello world!'"></span></li>
    <li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li> <li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>
</ul>
</body>
</html>
  • 리터럴이란 소스 코드 상에서 고정된 값을 말하는 용어
  • String a = "Hello" : 문자 리터럴 , 10 : 숫자 리터럴
  • 타임리프에서 문자리터럴은 항상 ``(백틱)으로 감싸야 한다. 단, 공백 없이 이어져있다면 토큰으로 인식해준다. 공백이 있는경우 백틱보다는|...|`을 사용하자.

연산

image

  • HTML 엔티티 : > : gt, <: lt, >= : ge, <= : le, ! : not, == : eq , != : neq,ne
  • No-operation 인 _를 사용하면 타임리프가 실행하지 않는 것처럼 동작하게 만들어서 HTML의 값을 그대로 출력할 수 있다.

속성 값 설정

  • 타임리프는 th:*를 사용하는데, 기존 속성을 대체해서 사용할 수 있다. 즉, <input type="text" name="mock" th:name="userA" /> 라는 문장이 있을 때 name="mock"th:name="userA"로 대체한다.

반복

<!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>
  • 타임리프에서 반복은 th:each를 사용한다. th:each="user, userStat : ${users}"에서 두 번째 파라미터는 현재 user의 상태 (status)에 대한 정보를 얻을 수 있다.

조건부 평가

<!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>
  • th:if, th:unless 조건을 통해 해당 조건을 만족하지 않으면 해당 라인을 적용시키지 않는다.`
  • th:switch 에서 th:case를 통해 조건을 넣으며 *은 해당 조건이 없을 때 적용될 값을 의미한다.

블록

image

  • div가 여러 개 즉, 여러 블록을 하나의 블록으로 처리하고 싶을 때 th:block을 사용하여 처리할 수 있다. 렌더링 결과에는 th:block은 사라져 있다.

자바스크립트 인라인

image

  • <script th:inline="javascript">를 사용하면 타임리프에서 자바스크립트의 인라인 기능을 사용할 수 있다. 해당 기능을 사용하면 문자열은 ""를 객체는 Json 타입으로 변환해서 적용시켜 준다. 즉, 자바스크립트 타입 관리를 직접 해준다.
<!-- 자바스크립트 인라인 each --> <script th:inline="javascript">
      [# th:each="user, stat : ${users}"]
      var user[[${stat.count}]] = [[${user}]];
      [/]
</script>
  • [#]...[/]를 사용해서 자바스크립트 안에서 each를 사용할 수 있다.

템플릿 조각

image

  • 웹 페이지를 개발할 때는 공통 영역이 많이 있다. 예를 들어 상단 영역이나 하단 영역, 좌측 카테고리 등 여러 페이지에서 함께 사용하는 영역들이 있다. 타임리프에서는 이런 부분을 지원해주는 템플릿 조각, 레이아웃 기능이 있다.
  • th:조건= "~{/path :: 템플릿 조각명}" 으로 사용
  • ::은 해당 영역을 템플릿 조각으로 가져와서 사용한다는 의미이다.
  • th:insert : 현재 태그 div 내부에 추가
  • th:replace : 현재 태그 div 를 대체
  • :: 템플릿 조각명 ('파라미터1', '파라미터2', ...) : 파라미터를 전달해서 동적으로 조각을 렌더링 할 수도 있다.

 

템플릿 레이아웃

image

  • 공통으로 사용하지만 각 페이지마다 필요한 정보가 필요한 경우 th:fragment로 지정해서 원하는 태그를 넣어줄 수 있다.

image

  • 동일한 방식으로 html태그에 th:fragment를 통해 html 전체에 적용할 수도 있다.

본 포스팅은 인프런 - 김영한님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 참고하였습니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^