본문 바로가기

[Thymeleaf] 타임리프 기본 기능 1 - 텍스트 , 표준 표현식 구문

@xuv22025. 6. 28. 14:18

저번에 타임 리프가 뭔지 알아 봤으니까, 이번에는 타임리프의 기능에 대해서 알아보자 !

타임리프는 HTML을 기반으로 하고 있기 때문에, HTML에 기능이 몇개 정도 추가 되었다고 생각하면 된다.

참고로 타임리프를 사용하기 위해서는 <html xmlns:th="http://www.thymeleaf.org"> 태그가 필요한 점을 까먹지 말자.


흐름을 찾아가기 위한 컨트롤러

package hello.thymeleaf.basic;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.Data;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/basic")
public class BasicController {

    @GetMapping("text-basic")
    public String textBasic(Model model) {
        model.addAttribute("data", "Hello <b>Spring!</b>");
        return "basic/text-basic";
    }

    @GetMapping("text-unescaped")
    public String textUnescaped(Model model) {
        model.addAttribute("data", "Hello <b>Spring!</b>");
        return "basic/text-unescaped";
    }

    @GetMapping("/variable")
    public String variable(Model model) {
        User userA = new User("userA", 10);
        User userB = new User("userB", 20);

        List<User> list = new ArrayList<>();
        list.add(userA);
        list.add(userB);

        Map<String, User> map = new HashMap<>();
        map.put("userA", userA);
        map.put("userB", userB);

        model.addAttribute("user", userA);
        model.addAttribute("users", list);
        model.addAttribute("userMap", map);

        return "basic/variable";
    }

    @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;
        }
    }

    @GetMapping("/date")
    public String date(Model model) {
        model.addAttribute("localDateTime", LocalDateTime.now());
        return "basic/date";
    }

    @GetMapping("/link")
    public String link(Model model) {
        model.addAttribute("param1", "data1");
        model.addAttribute("param2", "data2");
        return "basic/link";
    }

   


    @Data
    static class User {
        private String username;
        private int age;

        public User(String username, int age) {
            this.username = username;
            this.age = age;
        }
    }
}

Text 출력하기

HTML 콘텐츠에 데이터를 출력하기 위해서는 th:text 를 사용하자.

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

 

HTML 태그의 속성이 아닌 콘텐츠 영역 안에 직접 데이터를 출력하고 싶으면 [[..]] 를 사용하자.

[[${data}]]

 

사용 예시

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


Escape 처리

HTMl 문서는 <나 > 등 특수문자를 기반으로 정의한다.

그래서 항상 특수문자를 사용할때는 주의해야한다.

위 결과 예시를 볼 때, 우리는 글자를 bold 처리 하기 위한 <b> 태그를 정의 했을 뿐인데, 컴파일 후 웹 소스 코드를 보면 다음과 같다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>컨텐츠에 데이터 출력하기</h1>
<ul>
  <li>th:text 사용 <span>Hello &lt;b&gt;Spring!&lt;/b&gt;</span></li>
  <li>컨텐츠 안에서 직접 출력하기 = Hello &lt;b&gt;Spring!&lt;/b&gt;</li>
</ul>

</body>
</html>

우리의 목적과는 다르게 <b> 태그 부분이 그대로 나오고, < > 부분이 &lt 와 &gt 로 나오는 것을 볼 수 있다.

 

HTML 엔티티

기본적으로 웹 브라우저는 <를 태그의 시작으로 인식하기 때문에, 우리는 이런 기호를 문자로 표현할 수 있는 방법이 필요하다.

이 방법을 HTML 엔티티라고 하고, 이렇게 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 Escape 처리 라고 한다.

타임리프의 th:text 혹은 [[..]]등은 기본적으로 이스케이프를 지원한다.

 


Unescape 처리

이스케이프가 필요 없는 상황이 있을 수 있다.

다음과 같이 작성하면 언익스케이프 처리를 할 수 있다

th:text => th:utext
[[..]] => [(..)]

 

<!DOCTYPE html>
<html xmlns:th = "http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>text vs utext</h1>
<ul>
  <li>th:text 사용 <span th:text="${data}"></span></li>
  <li>th:utext 사용 <span th:utext="${data}"></span></li>
</ul>

<h1><span th:inline="none">[[...]] vs [(..)]</span></h1>
<ul>
    <li><span th:inline="none">[[...]] = </span>[[${data}]]</li>
    <li><span th:inline="none">[(..)] = </span>[(${data})]</li>
</ul>

</body>
</html>

 


변수 - SpringEL

타임 리프에서 변수 사용시 변수 표현식을 쓴다.

${data}

그리고 이 변수 표현식에는 SpringEL 이라는 스프링이 제공하는 표현식이 사용 가능하다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>SpringEL 표현식</h1>
<ul>Object
    <li>${user.username} = <span th:text="${user.username}"></span></li>
    <li>${user['username']} = <span th:text="${user['username']}"></span></li>
    <li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List
    <li>${users[0].username} = <span th:text="${users[0].username}"></span></li>
    <li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span>
    </li>
    <li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span>
    </li>
</ul>
<ul>Map
    <li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
    <li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
    <li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
</ul>

<h1>지역 변수 - (th:with)</h1>
<div th:with="first=${users[0]}">
    <p>처음 사람의 이름은 <span th:text="${first.username}"></span></p>
</div>

</body>
</html>

변수를 사용하는 방법이 약 3가지 정도가 있는데, 결국 모두 프로퍼티 접근법으로 귀결 된다.

형태가 대부분 모델로 넘어온 데이터의 키값(객체).필드명 인데, 이 때 필드명은 getXXX가 실행 된다고 보면 된다.

지역 변수도 th:with 를 통해 사용 가능한데, 이름이 지역 변수인 만큼, 스코프는 해당 변수를 선언한 태그 내에서만 사용가능하다.

 

 


기본 객체

타임리프는 기본으로 제공하는 객체들이 존재한다.

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

 

기본 객체는 쉬우니 넘어가고, 편의 기능을 보자

URL에 쿼리 파라미터로 넘어오는 파라미터를 쉽게 조회 할 수 있고, 세션 데이터, 그리고 ${@{스프링빈}} 문법을 통해 스프링 빈도 직접 조회할 수 있다.

 


유틸리티 객체와 날짜

컨트롤러에서 모델에 LocalDateTime.now() 객체를 넘기면 해당 객체의 다음과 같은 데이터들을 조회 할 수 있다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<h1>LocalDateTime</h1>
<ul>
  <li>default = <span th:text="${localDateTime}"></span></li>
  <li>yyyy-MM-dd HH:mm:ss = <span th:text="${#temporals.format(localDateTime,'yyyy-MM-dd HH:mm:ss')}"></span></li>
</ul>
<h1>LocalDateTime - Utils</h1>
<ul>
  <li>${#temporals.day(localDateTime)} = <span th:text="${#temporals.day(localDateTime)}"></span></li>
  <li>${#temporals.month(localDateTime)} = <span th:text="${#temporals.month(localDateTime)}"></span></li>
  <li>${#temporals.monthName(localDateTime)} = <span th:text="${#temporals.monthName(localDateTime)}"></span></li>
  <li>${#temporals.monthNameShort(localDateTime)} = <span th:text="${#temporals.monthNameShort(localDateTime)}"></span></li>
  <li>${#temporals.year(localDateTime)} = <span th:text="${#temporals.year(localDateTime)}"></span></li>
  <li>${#temporals.dayOfWeek(localDateTime)} = <span th:text="${#temporals.dayOfWeek(localDateTime)}"></span></li>
  <li>${#temporals.dayOfWeekName(localDateTime)} = <span th:text="${#temporals.dayOfWeekName(localDateTime)}"></span></li>
  <li>${#temporals.dayOfWeekNameShort(localDateTime)} = <span th:text="${#temporals.dayOfWeekNameShort(localDateTime)}"></span></li>
  <li>${#temporals.hour(localDateTime)} = <span th:text="${#temporals.hour(localDateTime)}"></span></li>
  <li>${#temporals.minute(localDateTime)} = <span th:text="${#temporals.minute(localDateTime)}"></span></li>
  <li>${#temporals.second(localDateTime)} = <span th:text="${#temporals.second(localDateTime)}"></span></li>
  <li>${#temporals.nanosecond(localDateTime)} = <span th:text="${#temporals.nanosecond(localDateTime)}"></span></li>
</ul>
</body>
</html>

참고로 오늘 내 생일 ㅎㅎ

 


URL 링크

URL 을 생성할 때는 @{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>
    <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>
</body>
</html>

현재 도메인을 기준으로 /hello 경로로 이동한다.

여러 파라미터도 편리하게 조정할 수 있다.

() = 쿼리 파라미터

{} = 경로 변수

${} = 모델의 값


문자 리터럴

리터럴이란 소스 코드 상에 고정된 값을 의미한다.

타임 리프에는 다음과 같은 리터럴들이 존재한다.

- 문자 : 'hello'

- 숫자 : 10

- boolean : ture, false

- null : null

 

우리가 주의해야할 점은 타임리프에서 항상 문자 리터럴은 작은 따옴표로 감싸야한다는 것이다!!!!!

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

이딴식으로 쓰면 의미 없는 토큰으로 인식해서 출력도 안되고 심지어 예외까지 터진다 ㅋㅋㅋ

 

하지만 모든 문자를 항상 작은 따옴표로 감싸는건 조금 귀찮으니까 타임리프에서 기능을 제공하는데 밑에서 보자.

<!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 ' + ${data} = <span th:text="'hello ' + ${data}"></span></li>
    <li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>

</ul>

</body>
</html>

+ 기호를 써서 자바 문자처럼 잘 붙이는 방법도 있겠으나, | 이 안에 문자 입력 | 처럼 리터럴 대체 문법을 쓰면 || 안에 내용을 모두 문자로 인식하여 편리하게 사용 가능하다.


연산

연산은 자바랑 비슷해서 크게 어려운 내용은 없고, 아까 말한 HTML 엔티티로 익스케이프 되는 부분만 조심하면 된다.

<!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 == 10 = <span th:text="1 == 10"></span></li>
            <li>1 != 10 = <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>${data}?: _ = <span th:text="${nullData}?: _">데이터가 없습니다</span> </li>
        </ul>
    </li>
</ul>

</body>
</html>

조건식은 마치 삼항연산자 같다.

 

Elvis 연산자 (?:)

데이터가 있으면 해당 데이터를 출력하고, 없거나 null 이면 설정해둔 디폴트 값 출력('데이터가 없습니다').

 

No-Operation( _ )

데이터가 없어서 _ 가 선택된 경우 설정해둔 HTML 기본 설정을 활용할 수 있다. 

즉, _가 선택되면 타임리프가 무효화 된다고 생각하면 된다.

xuv2
@xuv2 :: xuvlog

폭싹 늙었수다

목차