본문 바로가기

Tip/Spring

[타임리프] From JSP to Thymeleaf


  개요


웹 개발자와 퍼블리셔가 작업을 하면서 JSP 와 HTML 코드를 어떻게 관리해야 두번 작업 안하게 할까? 라는 고민에서 시작되었다. 보통 작업을 하면 HTML 관련 프로젝트는 퍼블리셔가 담당하고 이것을 개발자가 JSP 등의 웹 개발 언어로 개발하면서 두개의 프로젝트로 관리되어 작업을 해왔다. 그러면서 자연스럽게 웹개발자와 퍼블리셔가 프로젝트를 분리하지 않고 하나의 프로젝트에서 관리할 수 있는 방법은 없을까? 하는 생각이 들었다. 


퍼블리셔 : 로컬에서 서버없이 화면을 제작/수정하고자 함

웹개발자 : 서버를 통해 전달한 데이터를 포함한 온전한 화면의 개발


이쯤에 접한것이 바로 타임리프였다.  타임리프는 자바 라이브러리로 웹환경뿐만 아니라 서버없이도 동작하는 XML/XHTML/HTML5 템플릿 엔진이다. 타임리프면 위의 문제(?)를 해결해 줄 수 있지 않을까? 라는 생각이 들었다. 


보통 JSP 파일을 서버없이 브라우저에서 확인할 경우 아래와 같은 JSP 관련 문법들로 인해 원하는 화면이 출력되지 않을 수 있다. 

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 

.....  생략 .....


개발중이나 개발이후에 퍼블리셔에게 변경요청이 들어오면, 퍼블리셔는 개발자와 같은 웹서버환경 구축하여 수정을 하거나, HTML을 수정한 후 개발자에게 토스하는 방식으로 수정작업이 이루어질 수 있다. (이때 드는 개발자 시간은 어쩌란 말인가?)


타임리프의 가장 큰 장점은 바로 웹환경에서의 데이터 처리는 물론이고 서버 실행 없이도 화면을 브라우저에서 열고 변경할 수 있다는 점이다. 이러면서 자연스레 개발자 시간도 벌 수 있다는 것이다. 


타임리프는 아래와 같이 HTML, XML 등의 마크업언어의 태그에 속성을 추가하여 랜더링 하는 방식을 취한다. 그렇기 때문에 아래와 같은 코드가 포함된 파일을 브라우저에서 열었을때 브라우저 입장에서는 아무 상관이 없는 코드가 되버리는 것이다. 그렇기 때문에 서버 실행없는 로컬환경에서도 처음 퍼블리셔가 의도했던 화면을 확인할 수 있게 되는 것이다. 

 <div th:switch="${user.role}"

  <p th:case="'admin'">User is an administrator</p>

  <p th:case="#{roles.manager}">User is a manager</p> 

</div>


위의 장점도 있지만  HTML/XHTML/HTML5 /XML 등에 제한되어 있어 다른 용도(코드 생성 등의)로 인해 템플릿엔진이 필요한 경우에는 타임리프 보다는 FreemarkerVelocity 를 선택할 수 있을 것이다. 타임리프에 대한 자세한 설명은 링크를 참고하기를 바란다. 


이 문서에서는 기존의 JSP 로 되어 있는 코드를 타임리프로 바꾸면서 진행했던 사항들을 정리한 것이다. 좀 더 자세히 말하자면 JSP + Tiles2 로 되어 있는 것을 타임리프 + Tiles2 로 바꾼 작업이 맞을 것이다. 그러면 필요한 파일을 하나씩 나누어서 살펴보도록 하자. 




  pom.xml


Thymeleaf + Tiles2

### xml

<!-- thymeleaf -->

<dependency>

<groupId>org.thymeleaf</groupId>

<artifactId>thymeleaf</artifactId>

<version>2.0.17</version>

</dependency>

<dependency>

<groupId>org.thymeleaf</groupId>

<artifactId>thymeleaf-spring3</artifactId>

<version>2.0.17</version>

</dependency>

<dependency>

<groupId>org.thymeleaf</groupId>

<artifactId>thymeleaf-testing</artifactId>

<version>2.0.0</version>

</dependency>

<dependency>

<groupId>org.thymeleaf.extras</groupId>

<artifactId>thymeleaf-extras-tiles2</artifactId>

<version>2.0.0</version>

</dependency>

타임리프 관련된 라이브러리가 필요하다. 필자가 사용한 버전은 2.0.17 이고 타임리프에 레이아웃 프레임워크인 Tiles 를 붙이기 위해 코드 아래쪽에 타임리프를 확장한 형태의 Tiles 관련 라이브러리도 필요하다. 




  servlet-context.xml


JSP + Tiles2

### xml

<!-- tiles -->

<beans:bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">

<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView" />

</beans:bean>


<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">

<beans:property name="definitions">

<beans:list>

<beans:value>/WEB-INF/tiles/tiles.xml</beans:value>

</beans:list>

</beans:property>

</beans:bean>



Thymeleaf + Tiles2

### xml

<beans:bean id="templateResolver"

class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">

<beans:property name="prefix" value="/WEB-INF/views/th/" />

<beans:property name="suffix" value=".html" />

<beans:property name="characterEncoding" value="UTF-8" />

<beans:property name="templateMode" value="HTML5" />

<!-- Template cache is true by default. Set to false if you want -->

<!-- templates to be automatically updated when modified. -->

<beans:property name="cacheable" value="false" />

</beans:bean>


<beans:bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">

<beans:property name="templateResolver" ref="templateResolver" />

<beans:property name="additionalDialects">

<beans:set>

<beans:bean class="org.thymeleaf.extras.tiles2.dialect.TilesDialect" />

</beans:set>

</beans:property>

</beans:bean>


<beans:bean id="tilesViewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver">

<beans:property name="viewClass"

value="org.thymeleaf.extras.tiles2.spring.web.view.ThymeleafTilesView" />

<beans:property name="templateEngine" ref="templateEngine" />

</beans:bean>


<beans:bean id="tilesConfigurer"

class="org.thymeleaf.extras.tiles2.spring.web.configurer.ThymeleafTilesConfigurer">

<beans:property name="definitions">

<beans:list>

<beans:value>/WEB-INF/tiles/tiles-thymeleaf.xml</beans:value>

</beans:list>

</beans:property>

</beans:bean>


타임리프만 적용하게 되면 (STS + Thymeleaf) 코드의 양이 많지 않은데 Tiles 도 같이 적용하다보니 코드가 조금 늘어났다. 


tiles-thymeleaf.xml

### xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE tiles-definitions PUBLIC

        "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"

        "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

<tiles-definitions>

<definition name="home" template="layout">

<put-attribute name="body" value="home" type="thymeleaf"/>

</definition>


... 생략 ...

</tiles-definitions>




  javascript


Thymeleaf + Tiles2


1. 선언부 변경

### javascript

<script th:inline=" javascript">

     /*<![CDATA[*/

     ... 중략 ...

     /*]]>*/

</script>


2. javascript 내 EL사용시

### javascript

<script>

var item = ${item};

var item = /*[[${item}]]*/{};

</script>


타임리프는 HTML 의 속성값을 가지고 랜더링하는 방식이라서 javascript 사용시에는 선언부를 비롯하여 약간의 추가 작업이 필요하다. 또한 javascript 내에서 EL 사용시에도 추가적인 코드가 필요하다. 코드는 위와 같다. 




  include


JSP + Tiles2

### html

<%@ include file="/WEB-INF/views/common/header.jsp"%>


Thymeleaf + Tiles2

### html

<div th:include="common/header" th:remove="tag"></div>


파일을 인클루드 하는 방식에서도 차이가 있다. 타임리프는 필요한 부분에 태그를 추가하고 th:include 속성을 이용하여 파일을 해당 문서에 포함시킬 수 있다. th:remove="tag" 를 이용하여 랜더링시 해당 태그를 없앨 수도 있다. 또한 spring security 와의 연동도 가능하다. 관련한 내용은 링크를 참고하기 바란다. 




  결론


이상으로 JSP 를 타임리프로 바꾸는 작업을 진행해 보았다. 레이아웃 프레임워크인 Tiles이 포함되어 있어서 서버 실행없이 온전한 화면을 확인하고 수정할 수 있는 타임리프의 장점을 살릴 수 없었던 것은 많이 아쉬웠다.  보통 Tiles 의 공통 레이아웃에 공통 스크립트나 스타일을 추가하다 보니 원래 화면만 브라우저에서 확인했을 경우에 화면이 깨지는 경우가 많았다. 결국 추가 작업이 많이 필요해 보었다. 


Tiles 를 들어내고 타임리프만의 파일을 구성해야 하나 라는 생각이 들긴하였지만 화면들 각각에 들어갈 공통 스크립트나 스타일에 대해 거부감이 먼저 들었다. 추후에는 JSP에서 타임리프로의 변경이 아니라 타임리프로 시작하여 프로젝트 초반에 적용이 된다면 JSP 와의 큰 차이가 없어서 쉽게 적용될 수 있을 것이라 생각든다. 관련해서 좋은 방법이 있으면 나중에라도 공유가 되었으면 좋겠다. 


추가) 찾아보니 Thymol 을 이용하여 th:include 를 포함시켜 로컬에서 확인할 수 있다고 하는데 한번 확인해 봐야겠다. 





  참고


https://github.com/thymeleaf/thymeleaf-extras-tiles2

http://blog.springsource.org/2012/10/30/spring-mvc-from-jsp-and-tiles-to-thymeleaf/

https://github.com/thymeleaf/thymeleaf-extras-springsecurity3