• 스프링에서 JSP 위주로 View를 개발하는 것처럼 스프링 부트는 Thymeleaf라는 템플릿 엔진 이용

1. Thymeleaf 기초 문법

 1) 인텔리제이 설정

  • Thymeleaf를 이용하기 위해 html 파일의 네임스페이스에 Thymeleaf를 지정하는 것
  • 네임스페이스를 지정하면 'th:'와 같은 Thymeleaf의 모든 기능을 사용할 수 있게됨
<!-- hello.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
</body>
</html>
  • 'th:'로 시작하는 기능을 사용할 수는 있지만 Model에 담긴 데이터를 사용할 때 '해당 변수를 찾을 수 없다'는 에러가 발생할 수 있어 인텔리제이의 Setting > Thymeleaf 검색 > Unresolved references in Thymeleaf expression variables' 체크 해제

 

  - Thymeleaf 출력

  • Thymeleaf는 Model로 전달된 데이터를 출력하기 위해 HTML 태그 내에 'th:,,'로 시작하는 속성을 이용하거나 인라인 이용
  • SampleController에서 ex1()을 추가해서 '/ex/ex1'이라는 경로 호출할 때 동작하도록 구성
// SampleController
package org.zerock.b01.controller;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Arrays;
import java.util.List;

@Controller
@Log4j2
public class SampleController {

    ...

    @GetMapping("/ex/ex1")
    public void ex1(Model model) {
        List<String> list = Arrays.asList("AAA", "BBB", "CCC", "DDD");

        model.addAttribute("list", list);
    }
}
  • templates > ex 디렉토리 생성 > ex1.html 추가하여 결과화면 생성
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <h4>[[${list}]]</h4>
  <hr/>
  <h4 th:text="${list}"></h4>
</body>
</html>

 

  - th:with를 이용한 변수 선언

  • Thymeleaf를 이용하는 과정에서 임시로 변수를 선언해야 하는 상황에서 'th:with'를 이용해서 간단히 처리 가능
  • 'th:with'로 만드는 변수는 '변수명 = 값' 형태로, ','를 이용해 여러 개 선언 가능
<!-- hello.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <h1 th:text="${msg}"></h1>

    <!-- 임시 변수 선언 -->
    <div th:with="num1 = ${10}, num2 = ${20}">
        <h4 th:text="${num1 + num2}"></h4>
    </div>
    
</body>
</html>

 

 2) 반복문과 제어문 처리

  • 크게 두 가지 방법
    • 반복이 필요한 태그에 'th:each'를 적용하는 방법
    • <th:block>이라는 별도의 태그를 이용하는 방법
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <!-- 반복이 필요한 태그에 'th:each'를 적용하는 방법 -->
    <ul>
        <li th:each="str: ${list}" th:text="${str}"></li>
    </ul>
    
    <!-- <th:block>이라는 별도의 태그를 이용하는 방법 -->
    <ul>
        <th:block th:each="str: ${list}">
            <li>[[${str}]]</li>
        </th:block>
    </ul>

</body>
</html>
  • 결과는 동일

 

  - 반복문의 status 변수

  • Thymeleaf는 th:each를 처리할 때 현재 반복문의 내부 상태에 변수를 추가해서 사용할 수 있음
  • 일명 status 변수라고 하며 index / count / size / first / last / odd / even 등을 이용해 자주 사용하는 값 출력 가
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    ...

    <ul>
        <li th:each="str,status: ${list}">
            [[${status.index}]] -- [[${str}]]
        </li>
    </ul>

</body>
</html>

  - th:if / th:unless / th:switch

  • Thymeleaf는 제어문의 형태로 th:if / th:unless / th:switch를 이용할 수 있음
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    ...

    <ul>
        <li th:each="str,status: ${list}">
            <span th:if="${status.odd}">ODD -- [[${str}]]</span>
            <span th:unless="${status.odd}">EVEN -- [[${str}]]</span>
        </li>
    </ul>

</body>
</html>

  • '? ' 사용하여 더 편하게 이항 혹은 삼항 처리 가
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    ...

    <!-- 이항 연산자로 사용 -->
    <ul>
        <li th:each="str,status: ${list}">
            <span th:text="${status.odd}?'ODD ---' + ${str}"></span>
        </li>
    </ul>
    
    <!-- 삼항 연산자로 사용 -->
    <ul>
        <li th:each="str,status: ${list}">
            <span th:text="${status.odd}?'ODD ---' + ${str} : 'EVEN ---' + ${str}"></span>
        </li>
    </ul>
    

</body>
</html>

  • th:switch는 th:case와 같이 사용해서 Switch 문을 처리할 때 사용할 수 있음
<!-- ex1.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    ...
	
    <!-- 3으로 나눈 나머지의 결과에 따라 각각의 결과를 출력 -->
    <ul>
        <li th:each="str,status: ${list}">
            <th:block th:switch="${status.index % 3}">
                <span th:case="0">0</span>
                <span th:case="1">1</span>
                <span th:case="2">2</span>
            </th:block>
        </li>
    </ul>

</body>
</html>

 

 3) Thymeleaf 링크 처리

  • Thymeleaf에서는 @로 링크를 작성하면 링크 처리됨
<a th:href="@{/hello}">Go to /hello</a>

 

  - 링크의 쿼리 스트링 처리

  • 링크를 'key=value'의 형태로 필요한 파라미터를 처리해야 할 때 상당히 편리
  • 쿼리 스트링은 '()'를 이용해서 파라미터의 이름과 값을 지정
<a th:href="@{/hello(name='AAA', age=16)}">Go to /hello</a>

  • 한글이나 공백에 대한 URL 인코딩 처리가 자동으로 이루어짐
<a th:href="@{/hello(name='한글 처리', age=16)}">Go to /hello</a>
  • 링크를 만드는 값이 배열과 같이 여러 개일 때는 자동으로 같은 이름의 파라미터로 처리
<a th:href="@{/hello(types=${{'AA', 'BB', 'CC'}}, age=16)}">Go to /hello</a>

 

 

2. Thymeleaf의 특별한 기능들

 1) 인라인 처리

  • Thymeleaf에서 상황에 따라 동일한 데이터를 다르게 출력해 주는 인라인 기능은 자바스크립트를 사용할 때 편리한 기능
  • 다양한 종류의 데이터를 Model로 담아서 전달하는 메서드를 SampleController에 추가
// SampleController
package org.zerock.b01.controller;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Controller
@Log4j2
public class SampleController {

    ...
    
    class SampleDTO {
        private String p1,p2,p3;
        
        public String getP1() {
            return p1;
        }
        public String getP2() {
            return p2;
        }
        public String getP3() {
            return p3;
        }
    }
    
    @GetMapping(("/ex/ex2"))
    public void ex2(Model model) {
        log.info("ex/ex2................");
        
        List<String>strList = IntStream.range(1,10)
                .mapToObj(i -> "Data"+i)
                .collect(Collectors.toList());
        
        model.addAttribute("list", strList);
        
        Map<String, String> map = new HashMap<>();
        map.put("A","AAAA");
        map.put("B","BBBB");
        
        model.addAttribute("map", map);
        
        SampleDTO sampleDTO = new SampleDTO();
        sampleDTO.p1 = "Value -- p1";
        sampleDTO.p2 = "Value -- p2";
        sampleDTO.p3 = "Value -- p3";
        
        model.addAttribute("dto", sampleDTO);
    }
}
  • ex2 화면 구성
<!-- ex2.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

  <div th:text="${list}"></div>
  <div th:text="${map}"></div>
  <div th:text="${dto}"></div>

  <script th:inline="javascript">
    const list = [[${list}]]
    const map = [[${map}]]
    const dto = [[${dto}]]
    
    console.log(list)
    console.log(map)
    console.log(dto)
  </script>

</body>
</html>
  • html은 기존처럼 출력되고, <script> 부분은 자바스크립트에 맞는 문법으로 만들어지는 것을 확인

 

 2) Thymeleaf의 레이아웃 기능

  • <th:block>을 이용하면 레이아웃을 만들고 특정한 페이지에서 필요한 부분만 작성하는 방식으로 개발 가능
  • 별도의 라이브러리 필요하므로 build.gradle에서 추가
// build.gradle
dependencies {
	
    ...

	implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.1.0'
}

 

  • templates > layout 폴더 생성 > 레이아웃을 위한 layout1.html 작성
<!-- layout1.html -->
<!DOCTYPE html>
<!-- thymeleaf의 layout 작성을 위한 네임스페이스 지정 -->
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Layout Page</title>
</head>
<body>

<div>
  <h3>Sample Layout Header</h3>
</div>

<!-- layout:fragment 속성을 이용하면 나중에 다른 파일에서 해당 부분만 개발 가능 -->
<div layout:fragment="content">
  <p>Page content goes here</p>
</div>

<div>
  <h3>Sample Layout Footer</h3>
</div>

<th:block layout:fragment="script">
  
</th:block>

</body>
</html>

 

  • Samplecontroller에 레이아웃 예제를 위한 ex3()을 추가
// SampleController
package org.zerock.b01.controller;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Controller
@Log4j2
public class SampleController {

    ...

    @GetMapping("/ex/ex3")
    public void ex3(Model model) {
        model.addAttribute("arr", new String[]{"AAA", "BBB", "CCC"});
    }
}

 

  • layout1.html에서 layout:fragment 속성으로 처리했던 content와 script부분을 다른 파일인 ex3에서 개발할 수 있음
<!-- ex3 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout1.html}">

<div layout:fragment="content">
    <h1>ex3.html</h1>
</div>

<!-- ex3 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layout/layout1.html}">

<div layout:fragment="content">
    <h1>ex3.html</h1>
</div>

<script layout:fragment="script" th:inline="javascript">
	const arr = [[${arr}]]
</script>

+ Recent posts