본문 바로가기
SSR/Thymeleaf

Thymeleaf (1)

by oncerun 2022. 1. 3.
반응형

자바의 웹 애플리케이션 레거시 프로젝트의 특징은 JSP를 활용하여 프론트, 백엔드 구분 없이 개발자가 전부 웹 개발을 진행한다는 점이다.

 

요즘은 전문적인 웹 클라이언트 사이드 렌더링 기술을 적극적으로 활용하는 웹 클라이언트 프레임워크, 혹은 라이브러리인 리액트나, 뷰, 앵귤러를 많이 사용한다.

 

간단한 웹 애플리케이션을 개발할 때 해당 웹 클라이언트 프레임워크 기술을 적용하여 개발하면 베스트이지만  웹 클라이언트 프레임워크는 정말 전문적인 영역으로 쉽게 접근할 수 없다는 점이다. 성능 최적화, 컴포넌트화, 타입 스크립트, 모듈화, 번들링.. 점점 쉽게 다가갈 수 없는 환경으로 이루어지기 때문에 기존 JSP로 서버사이드에서만 개발하던 개발자들이 해당 기술을 활용하려면 시간과 노력이 필요하다. 

 

간단한 웹 애플리케이션 혹은 어드민 페이지를 만들기 위해 백엔드 개발자는 JSP, Thymeleaf, velocity.. 자바 진영의 여러 템플릿 엔진들이 존재한다.

 

대가 속도를 중시하면 Apache Velocity를 사용하고, 스프링과 통합을 원하면 Thymeleaf,  SI에서 흔하게 접할 수 있는 JSP 등등이 있지만 이중 JSP의 강세는 많이 하락했다. 속도적으로나 통합적으로나 어느 하나 강점이 없기 때문이다.

 

그래서 나도 혹시 개인적인 요청 혹은 간단한 서비스 애플리케이션을 만들기 위해 타임리프를 공부해볼 생각이다. 템플릿 기술이 대부분 간단하고 배우기 쉽기 때문에 오랜 시간이 걸리지 않는다고 판단했다.

아 물론 하이브리드 앱개발을 위해서 앵귤러 기반으로 작동하는 모바일 앱을 만들고 있는데 앵귤러의 구조가 백엔드랑 무척 비슷해서 재밌게 개발하고 있는 중이다...

 

우선 타임리프의 공식홈페이지에서는 다음과 같이 정의한다.

 

타임리프는 웹 혹은 독립적인 환경에서 작동하는 모던한 서버 사이드 자바 템플린 엔진입니다. 

 

다음과 같은 특징을 가지고 있다고 홍보한다.

 

  • Server-Side-Rendering
  • Natural template
  • Integrations galore

 

Server-Side-Rendering

 

백엔드 서버에서 HTML을 동적으로 렌더링 하는 용도로 사용된다. 이와 반대되는 개념으로 Client-Side-Rendering인 클라이언트에서 동적으로 렌더링 하는 React, Vue와 같은 프레임워크들이 존재한다.

 

 

Natural template

 

타임리프를 경험해보신 분들은 아시겠지만 타임리프는 순수 HTML을 유지하는 특징을 가지고 있다.

마치 브라우저에서 타임리프로 만든 html 파일을 파일 경로로 오픈해도 html이 잘 나오는 것을 확인할 수 있고, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다.

JSP를 포함한 다른 뷰 템플릿들은 해당 파일을 그대로 웹 브라우저에서 열어도 정상적인 HTML 결과를 확인할 수 없다.

오직 서버를 통해 jsp화면이 렌더링 되고 응답 결과를 받아야 한다.

 

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

 

Integrations galore

 

많은 개발툴, Spring 등등 MVC 구조를 사용하는 JAVA에 많은 통합이 되어 있습니다. 스프링의 다양한 기능을 편리하게 사용할 수 있도록 지원합니다.

 

선언

jsp를 사용하기 위해  "<%@ page contentType="text/html;charset=UTF-8" language="java" %>"과 같은 코드를 붙였다면 타임리프는 다음과 같이 선언해야 합니다.

 

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

 

기본 표현식

 

• 간단한 표현:

  ◦ 변수 표현식: ${...}

  ◦ 직접 출력

간단하게 Model에서 데이터를 꺼내는 방법

 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
  <li>th: text 사용하기 <span th:text="${data}"></span></li>
  <li>속성이 아니라 직접 출력하기 <span>[[${data}]]</span></li>
</ul>
</body>
</html>

 Tip

HTML 엔티티

웹 브라우저는 <, >와 같은 특수기호를 태그의 시작으로 인식한다. 만약 Model의 data 내부에 특수기호가 존재하는 경우 마크업 문자가 아니라 단순 문자열로 표현할 방법이 필요하다.  이러한 특수문자를 문자열로 나타내기 위해 HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는데 이를 이스케이프라고 한다.

그리고 타임리프가 제공하는 th:text, [[${...}]]는 기본적으로 이스케이프를 제공한다.

 

만약 이스케이프를 사용하지 않기 위해서는 다음과 같이 처리해야 한다.

   @GetMapping("/text-unescaped")
    public String textUnescaped(Model model) {
        model.addAttribute("data", "<b>Hello Thymeleaf</b>");
        return "basic/text-unescaped";
    }
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
  <li>th: utext 사용하기 <span th:utext="${data}"></span></li>
  <li>Unescaped 직접 출력하기 <span>[(${data})]</span></li>
</ul>
</body>
</html>

 

◦ SpringEL

타임리프에서 변수를 사용할 때 변수 표현식을 사용하는데 이 표현식에는 스프링이 제공하는 SpringEl이라는 표현식을 사용할 수 있다.

JSP에 익숙한 사람들이라면 EL표현식에 친숙할 것이다.

<!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>
</body>
</html>

 

  ◦ 변수 선언

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

 - 지역변수는 선언한 태그(div) 내부에서만 사용할 수 있다.

 

  ◦ 기본 객체

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

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

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

이런 점을 해결하기 위해 편의 객체도 제공한다.

    @GetMapping("/basic-objects")
    public String basicObjects(HttpSession session) {
        session.setAttribute("sessionData", "session data!");
        return "basic/basic-objects";
    }


    @Component("helloBean")
    static class HelloBean{
        public String hello(String data) {
            return "hello" + data;
        }
    }

HTTP 요청 파라미터 접근: param 예) ${param.paramData}

HTTP 세션 접근: session 예) ${session.sessionData}

스프링 빈 접근: @ 예) ${@helloBean.hello('Spring!')}

 

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

 

request, response, session 객체들은 Tomcat사용 시 tomcat이 만들어준 인스턴스의 주소 값이 보일 것이다.

만약 기본 객체를 제공하지 않는다면 Model에 HttpServletRequest, HttpServletResponse를 담아서 사용해야 할 것이다.

 

특이한 점은 "param"이라는 변수이다.

HTTP 요청에서 들어오는 요청 파라미터에 값을 담아두기 때문에 Controller에서  Model에 담는 코드를 작성하지 않고도 접근할 수 있다.

GET방식의 URL 파라미터 전송방식이나, POST 요청의 파라미터 전송방식 모두 지원한다.

  ◦ 유틸리티 객체

타임리프는 문자, 숫자, 날짜를 편리하게 다루는 Utils 객체를 지원한다.

Tutorial: Using Thymeleaf

 

Tutorial: Using Thymeleaf

1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide a

www.thymeleaf.org

 

자바 8의 LocalData, LocalDataTime, Instant를 사용하려면 추가 라이브러리가 필요하며, 스프링 부트를 사용하면 해당 라이브러리는 통합되어있다.

 

타임리프 자바 8 날짜 지원 라이브러리  "thymeleaf-extras-java8time"

자바 8 날짜용 유틸리티 객체  "#temporals

 

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

 

반응형

'SSR > Thymeleaf' 카테고리의 다른 글

Thymeleaf (5)  (0) 2022.01.05
Thymeleaf (4)  (0) 2022.01.05
Thymeleaf (3)  (0) 2022.01.04
Thymeleaf (2)  (0) 2022.01.04

댓글