본문 바로가기
Java/web

[Thymeleaf] 타임리프 기본 문법 예제 포함

by Jayson Jeong 2022. 8. 2.

1. Thymeleaf

   1) Thymeleaf란

     타임리프는 서버사이드렌더링 자바 템플릿 엔진으로 Spring에서 권장하는 템플릿 엔진이다.

     타임리프 라이브러리는 확장성이 매우 좋다. 

      * 타임리프 문법을 적용한 html을 브라우저에서 돌릴 수 있을 정도(다만, 브라우저에서 실행 시 타임리프의 문법은

     적용되지 않는다.)

 

     타임리프는 html 태그에 속성을 추가해 페이지에 동적으로 값을 추가하거나 동적 렌더링이 가능하게 해준다.

2. Thymeleaf 적용

  1) Thymeleaf 설정

Thymeleaf를 사용하기에 앞서 타임리프 설정 정보를 등록해야 함.

필자는 순수 자바 코딩 방식으로 Spring 과 연계하기 위해 TemplateResolver, TemplateEngine, ViewResolver를 설정하기로 함.

라이브러리, 적용방식에 따른 차이가 있을 수 있으므로 본인이 편한 방식으로 적용하면 된다.

TemplateResolver 설정

- SpringResourceTemplateResolver

@Bean
public SpringResourceTemplateResolver templateResolver() {
    SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver ();
    templateResolver.setPrefix("classpath:templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML5");
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setCacheable(isCache);
    
    return templateResolver;
}

 

- ClassLoaderTemplateResolver

@Bean
public ClassLoaderTemplateResolver templateResolver() {
    ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
    templateResolver.setPrefix("/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode(TemplateMode.HTML);
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setCacheable(true);
    templateResolver.setUseDecoupledLogic(false);
   
    return templateResolver;
}

 

- ServletContextTemplateResolver

@Bean
public ServletContextTemplateResolver templateResolver(){
    ServletContextTemplateResolver templateResolver = new ServletContextTemplateResover();
    templateResolver.setPrefix("/WEB-INF/views/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML5");
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setCacheable(false);
    
    return templateResolver;
}

SpringTemplateEngine 설정

-SpringTemplateEngine

@Bean
public SpringTemplateEngine templateEngine() {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver());
    templateEngine.setEnableSpringELCompiler(false);
    templateEngine.setTemplateEngineMessageSource(messageSource); //messageSoruce 적용시
    
    templateEngine.addDialect(layoutDialect()); //단일 등록 시
    //or 
	templateEngine.setAdditionalDialects(
                new HashSet<IDialect>(
                        Arrays.<IDialect>asList(
                                new LayoutDialect(new GroupingStrategy())
                                //여러 개 등록 시 추가 등록 가능
                        )
                )
        );
        
    return templateEngine;
}

ViewResolver 설정

-ThymeleafViewResolver

@Bean
public ThymeleafViewResolver viewResolver() {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setViewNames(new String[]{"thymeleaf/**"});
    viewResolver.setTemplateEngine(templateEngine());
    viewResolver.setCharacterEncoding("UTF-8");
    viewResolver.setOrder(0);
    
    return viewResolver;
}

WebMvcConfigurationSupport를 상속받아 설정

 -WebMvcConfiguration

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Override
    protected void configureViewResolvers(ViewResolverRegistry registry) {
        super.configureViewResolvers(registry);
        registry.beanName();

        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setCacheable(false);
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setUseDecoupledLogic(false);

        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setEnableSpringELCompiler(false);
        templateEngine.setTemplateResolver(templateResolver);
        templateEngine.setAdditionalDialects(
                new HashSet<IDialect>(
                        Arrays.<IDialect>asList(
                                new LayoutDialect(new GroupingStrategy())
                        )
                )
        );
        
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setViewNames(new String[]{"thymeleaf/**"});
        viewResolver.setTemplateEngine(templateEngine);
        viewResolver.setCharacterEncoding("UTF-8");

        registry.viewResolver(viewResolver);
    }
}

3. Thymeleaf 문법

  1)  기본문법


   - th:text

<div th:text = "${user.name}"></div>  //컨트롤러에서 전달받은 객체의 값을 text로 렌더링
<div th:utext = "${data}"></div>      //태그의 속성을 적용해서 출력

<div>[[${user.name}]]</div>  //th:text 기능을 직접 데이터 출력
<div>[(${data})]</div>  //th:utext 기능을 직접 데이터 출력

 

  - th:href

    <a>태그에 링크 경로를 동적으로 입력할 때 또는 오브젝트에 저장된 데이터를 가져올때 사용한다.

<a th:href = "@{/home}">
=> /home

<a th:href = "@{/home(param1 = ${value1}, param2 = ${value2})}">
 => /home?param1=value1&param2=value2

<a th:href = "@{/home/{param1}/{param2}(param1 = ${value1}, param2 = ${value2})}">
 => /home/value1/value2
 
<a th:href = "@{/home/{param1}(param1 = &{value1}, param2 = ${value2})}">
 => /hello/value1?param2=value2

 

  - th:src

    <img> 태그에 이미지 경로를 동적으로 입력할 때, 또는 오브젝트에서 경로가 저장된 데이터를 가져와서 사용한다.

<img th:src="@{/images/home}+${imagePath}">

 

  - th:with

    변수형태의 값을 재정의하는 것으로 값이 있다면 정의하고 없다면 다른 값으로 정의한다

<th:block th:with="value=${#strings.defaultString(user.name, '')}">

<div th:with = "dataFlag = ${(data == null || data.getId() == '') ? false : true}">
  <input type = "text" th:value = "${dataFlag? data.getBool() : ''}">

 

  - th:attr

    태그 안의 attribute를 타임리프로 정의할 때 사용한다.

<div th:attr = "testValue = ${user.name}"></div>
 -> testValue = 홍길동
 
<div th:attr = "bool = ${user.check == '1' ? 'true' : 'false'}">

 

  - th:value

    input에 데이터를 넣을 때 사용한다. 

<input type = "hidden" th:value = ${value.name}>

 

  - th:block 

   block은 타임리프 문법을 어느 곳에서든 사용할 수 있도록 한다.

   타임리프의 유일한 자체 태그로 여러 태그를 반복할 경우 사용한다.

   <th:block> 태그는 렌더링 시 제거된다.

<th:block th:if = "${item != null}">
  <a th:href="@{${item.link}}" th:text="${item.name}" 
	th:attr="current = ${item.link == other.link && item.target != '2' ? 'page' : ''}"></a>
</th:block>

</th:block th:each = "item : ${itemList}>
  <a th:href = "@{${item.link}}" th:text = "${item.name}"></a>
</th:block>

  2) 조건


   - th:if / th:unless

<th:if = "${user == null}"> //조건이 만족할 시 렌더링
<th:unless = "${user != null}"> //조건이 만족하지 않을 시 렌더링
<th:if = "${menu.type == '2'}"> //type이 2일경우 렌더링
<th:if = "${Object != null}"> //null이 아닐경우 렌더링

 

  - th:switch / th:case

<th:switch = "${user.age}">
<th:case = "10">
<th:case = "20">
<th:case = "*"> //케이스에 없을 경우

  3) 반복


  - th:each

<div th:each = "data : ${dataList}">
  <div th:text = "${data.name}"></div>
  <div th:text = "${data.type}"></div>
</div>

   4) Object


  - List<E>

<div th:text = "${nameList[3]}"></div>
 -> 리스트의 인덱스로 데이터를 가져 올 수 있음
 
<div th:with = "user = ${userList[1]}" th:text = "${user.name}"></div>
 -> 객체 리스트의 경우에 with를 이용해 지역변수에 담아서도 사용 가능
 
<th:block th:each = "user : ${userList}" >
  <div th:text = "${user.name}"></div>
</div>
 -> 타임리프 반복문을 이용해 차례대로 객체를 담아올 수 있음

 

  - Map<K,V>

<th:block th:with = "entry : ${valueList}">
  <div th:if="${entry.key != 'A'} th:text="${entry.value}></div>
</th:block>

 

  - List<Map<K,V>>

<th:block th:with="value = ${valueList[4]}>
  <div th:text="${value.get('NAME')}"></div>
</th:block>
 -> list의 인덱스 순서로 map 데이터를 가져올 수 있다.

   5) Layout


  - xmlns:layout / layout:decorator

<html lang="ko" xmlns:th="http://www.thymeleaf.org" 
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" 
  layout:decorate="~{board/layout/basic}"> //layout:decorate를 이용해 공통양식의 경로를 지정한다

 

  - th:fragment

    웹페이지에서 네비게이션바 등의 header영역과 관련사이트, 저작권 표시 등 footer영역처럼 반복되는 영역들이 

    존재하는데, 이 공통 영역들을 th:fragment로 정의하여 한 번 만들어 놓은 HTML을 반복해서 사용할 수 있게 한다.

    th:fragment로 조각화 하고 사용하고자 하는 HTML에서 th:replace = "~{파일경로 :: fragment이름}"을 통해 삽입한다.

<th:block th:fragment="footerFragment">
  <footer id="footer">
    <div>관련 사이트</div>
    <button type="button" title="선택된 관련 사이트 새창으로 열기">이동</button>
  </footer>
</th:block>

 

  - th:replace

    fragment로 조각화한 공통 소스를 HTML에 삽입하는 역할을 한다

    ::를 기준으로 앞에는 조각화한 fragment의 html 경로를, 뒤에는 명명한 fragment의 이름을 넣어준다

<div th:replace="~{layout/fragments/footer :: footerFragment}"></div>
<th:block th:replace="~{layout/fragments/footer :: footerFragment}" ></th:block>

 

  - th:insert

    th:replace와 동일한 기능을 하지만 차이가 있다.

    th:replace는 태그가 입력된 제거되고 fragment로 완전 대체되지만

    th:insert는 입력된 태그 안에 fragment를 삽입할 때 사용한다.

    형식은 replace와 동일하다.

<div th:insert="~{layout/fragments/footer :: footerFragment}"></div>
<th:block th:insert="~{layout/fragments/footer :: footerFragment}"></th:block>

 

  - th:remove

    브라우저로 html을 열었을 때는 보이지만 타임리프 엔진에 의한 렌더링이 됐을때는 보이지 않게 하고 싶을 때 사용한다.

<th:block th:remove="tag" th:utext="${name}" th:text="${name}"></th:block>

   6) Form


  - th:action

    <form> 태그 사용시 해당 경로로 요청을 보낼 때 사용한다.

    ajax와 비슷한 기능을 수행한다.

<form name="performForm" method="get" th:action="@{performList.do}" th:object="${perform}">

 

  - th:object

    <form> 태그에서 해당 경로로 요청을 보낼때 데이터를 object에 담아서 보낸다.

    mapping된 controller에서는 요청을 수행할 때 th:object를 통해 받은 object를 사용할 수 있다.

<form th:action="@{/event/search.do}" th:object="${user}" method="post">
->
@RequestMapping(value="/event/search.do")
	public String searchUser(User user, HttpServletRequest request, HttpServletResponse response)

 

  - th:field

    th:field를 사용해서 HTML 태그에 멤버 변수를 mapping 할 수 있다

<input type="text" id="title" name="title" title="제목입력" th:field="*{title}">

7) Javascript


  - th:inline

    <javascript> 태그 안에서 타임리프 문법, 타임리프 변수를 사용할 수 있도록 한다.

<script type="text/javascript" th:inline="javascript">

var nameList = /*[[ ${nameList} ]]*/;  //주석 부분이 알아서 제거됨.

</script>