Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Thymeleaf 사용 소감

최근에 Thymeleaf를 프로젝트에서 사용해 봤다. Thymeleaf에 대해서 들어본 지 한 1,2년 정도 된 것같은데 실제로 써본 것은 이번이 처음이다. 아주 다양한 케이스에 하드하게 사용해 본건 아니다.(사용한 버전은 2.0.x이다.)

Thymeleaf

Thymeleaf는 Tiles, FreeMarker, SiteMesh처럼 자바에서 사용할 수 있는 뷰 템플릿 엔진이다. 스프링소스에서 만든건지는 모르겠지만 스프링소스에서 열심히 밀고 있기는 하고 Spring MVC와 통합이 잘 되어 있다.

Thymeleaf의 가장 큰 특징이라면 네츄럴 템플릿 기능을 들 수 있는데 쉽게 말하면 템플릿 코드자체가 그냥 HTML이기 때문에 뷰 파일을 WAS없이도 브라우저에서 직접 띄워볼 수 있다는 점이다. 이는 Thymeleaf가 다른 템플릿 엔진처럼 전용 문법(브라우저가 해석하지 못하는)을 사용하지 않고 HTML 엘리먼트에 속성으로 적어줌으로써 동작하기 때문에 Thymeleaf는 이를 해석해서 뷰 파일을 만들어주고 브라우저는 모르는 속성은 그냥 무시하므로 브라우저에서도 동작을 하게 된다.

<td th:text="${name}">Oranges</td>

위와 같이 작성하는데 th는 thymeleaf에 대한 속성이다. 브라우저에서 이 코드를 보았을 때는 <td>Oranges</td>와 다름 없으므로 그냥 그대로 보이고 Thymeleaf는 name이라는 변수 값으로 <td>안의 값을 대체해버린다. 그래서 Thymeleaf에서 작성할 때는 예시용 코드를 위처럼 같이 넣어주는 것이 일반적이다.

뷰 파일을 작업할 때 그러니까 JavaScript나 CSS 작업등을 할 때 WAS를 띄워야 하는 불편함을 둘째치더라도 변경한 정적 파일이 서버에 적용되는데 까지의 지연 시간(JRebel을 쓰면 훨 낫다.)은 무척 괴로운 일이기 때문에 이 기능은 무척 장점으로 보였고 처음 이 컨셉으로 Thymeleaf를 보았을 때 다음에 꼭 써봐야지 하면서 맘에 들었다. 솔직히 그때는 "이게 바로 내가 기다리던 템플릿 엔진이야"라는 정도의 반응이었다.

단점

결론부터 얘기하면 써보니 별로라서 다음에 선택권이 있다면 쓰지 않을것 같다. 물론 아주 하드하게 오랫동안 써본건 아니라서 여기에 쓰는 단점들에 대한 대안들이나 해결책들이 존재할 것으로 생각하지만(이런 것들을 알려주시면 감사~) 그 해결책들이 아주 깔끔하고 근본적으로 해결되는게 아니라면 내 생각이 크게 달라지진 않을것 같다.

  • 네츄럴 템플릿을 이용해서 WAS없이 뷰페이지를 작업할 수 있는게 가장 큰 장점인데 실제로는 이렇게 작업할 수 없다. 불가능한 것은 아니지만 별 의미가 없어서 그냥 WAS를 켜놓고 작업하는게 낫다.(느린게 짜증나면 JRebel로...) Thymeleaf는 자체 파싱엔진을 가지고 있기 때문에 HTML코드가 완전한 XML이어야 하고 이는 설정에서 HTML5 모드로 하더라도 마찬가지다. 즉, 태그의 속성으로 템플릿문법을 사용하므로 범위를 명확히 하기 위해서 XML을 사용하고 있는 듯 하다. HTML도 XML기반이기는 하지만 훨씬 관대하기 때문에 XHTML strict 모드가 유행하던 몇년전 외에는 그렇게 사용하지 않는다. 그래서 평소 습관처럼 브라우저에 HTML작업을 하고 WAS에서 띄우면 거의 100% 컴파일 오류를 볼 것이다. 실수로 닫는태그를 안써준 것은 당연하고 <img>태그나 <input>태그등의 단독 태그를 모두 찾아서 닫아주어야 컴파일 오류를 피할 수 있다. 별거 아닌 작업이라고 말할 수도 있지만 HTML에서 닫는태그빠지거 찾는건 나에겐 무척 고통스럽고 경험상 컴파일 오류메시지가 그렇게 정확히 찝어주진 않는다. 일반적인 경우처럼 HTML 작업해주는 사람이 따로 있다면 이는 오로지 서버사이드에서 닫는 태그를 붙혀주어야 할 일이고 UI를 변경할 때마다 해야할 것이다.(HTML작업하는 사람한테 가서 태그는 꼭 닫아달라고 하던지...) (참고로 이 엄격한 XML 파싱을 끌 수도 있다고 들었는데 Thymeleaf가 구문을 정확히 파악해서 처리하는 지를 확실히 보장하기 어려운 듯 하다.)
  • 템플릿을 사용한다는 것은 서버의 변수를 넣어서 동적으로 만들기 위함도 있지만 페이지의 공통부분은 헤더, 푸터등을 별도의 템플릿으로 뽑아서 재사용하는 것도 포함된다. Thymeleaf에서는 이를 include로 불러오게 되는데 당연히 HTML에는 그러한 기능이 없으므로 인쿨루드는 동작하지 않는다. include를 쓰지 않으면서 템플릿 엔진을 쓴다는 건 당연히 말도 안되고 이 문제를 해결하기 위한 Thymol 자바스크립트 라이브러리가 있어서 이 라이브러리를 쓰면 일단 인클루드문제는 해결할 수 있어보인다.(써보진 않았다.) 하지만 긴급한 상황에서 대안은 될 수 있지만 계속되는 작업에서 Thymol을 HTML에 넣었다가 실제 Thymeleaf를 사용할 때는 빼는 작업을(당연히 실제로는 필요없는 것이므로) 반복해야 하는 것은 별로 좋은 해결책으로 보이지 않는다.1
  • 템플릿을 쓰면 많이 쓰는 것 중 하나가 컬렉션의 데이터를 반복해서 뿌려주는 반복구문이다. Thymeleaf를 처음부터 WAS에서만 쓰고 HTML파일만 따로 브라우저에서 해본적은 없지만 구조상 반복구문을 사용한 경우 HTML을 단독으로 열면 1번 반복한 것처럼 나올 것이다. 뷰 페이지를 작업하면 처음에는 1개로 작업하지만 5개일때 10개일때의 스타일을 잡아줘야 하는데 HTML입장에서는 반복구문이 전혀 아니므로 이런 건 볼 수 없고 그냥 각 부분은 10번이고 20번이고 복사해서 테스트한 뒤에 지워야 한다.(서버에 연결되어 있다면 Mock객체에 데이터만 추가하면 되겠지.) 사실 이건 앞에 얘기한 거에 비하면 아주 큰 문제까진 아니긴 하다.2
  • 숙달이 되면 나아질 지 모르겠지만 태그에 속성으로 템플릿구문을 넣는 구조에서 오는 제약이 상당히 있어서 작성하다보면 원하는 HTML 구조를 어떻게 만들어야 하는지 고민이 상당히 될때가 있다. 고민만 되면 다행이겠지만 마땅한 해결책도 없다.
<div class="row">
  <div class="span3">Something</div>
  <div class="span3">Something</div>
  <div class="span3">Something</div>
  <div class="span3">Something</div>
</div>
<div class="row">
  <div class="span3">Something</div>
  <div class="span3">Something</div>
  <div class="span3">Something</div>
  <div class="span3">Something</div>
</div>

예를 들어 위와 같은 HTML 구조를 만든다고 해보자. 리스트에 있는 데이터를 4개씩 Row를 구분해서 출력해 주고 싶다고 할 때 일반적인 템플릿엔진에서는 다음과 같이 작성할 것이다.

{{#each list}}
  {{#if @index % 4 == 0}}
    <div class="row">
  {{/if}}
  <div class="span3">{{this.name}}</div>
  {{#if @index % 4 == 0}}
    </div>
  {{/if}}
{{/each}}

이는 전용 템플릿구문이 있고 HTML의 여는 태그와 닫는 태그를 그냥 문자열처럼 다루기 때문에 아주 유연하게 원하는 구조를 만들어 낼 수 있다. 하지만 Thymeleaf에서는 HTML 태그의 속성으로 템플릿구문을 작성하므로 Thymeleaf를 여는태그와 닫는태그를 한꺼번에 다루기 때문에 한참을 고민해도 이 문제를 해결할 수가 없었다.

<div class="row" th:each="item: ${list}">
  <div class="span3" th:text="${name}">Something</div>
</div>

Thymeleaf에서는 위처럼 작성해야 하는데 list<div>태그가 연결되어 있기 때문에 중간에 div태그를 새로 만든다거나 하기가 좀처럼 쉽지 않다. 이 문제를 Stackoverflow에 질문해서 가능한 해결책을 찾기는 했지만 Thymeleaf에 대한 인상이 좋아질 정도의 해결책은 아니고 앞으로도 이와 비슷한 문제는 많이 만날 것이라고 생각한다. (추가로 Angular.js도 Thymeleaf처럼 태그에 속성으로 작성을 하고 있지만 Thymeleaf같은 뷰템플릿의 기능은 아니고 JavaScript와 연동되기 때문에 이러한 답답함은 많이 안느껴지는듯 하다.)

  • 좀 사용해 본 결과 사용자가 그리 많지 않은 느낌이다. 대부분의 문제는 유저커뮤니티만 활성화 되어 있으면 해결가능하다고 보기 때문에 이 문제가 가장 큰 문제로 다가온다. 검색을 해보면 튜토리얼이라던지 소개라던지 하는 자료들이 대부분이고 다양한 활용사례나 문제해결들에 대한 글은 많지 않다. 앞에서 스택오버플로우에 질문을 올렸을 때도 2일 지났을때 읽은 사람이 5명밖에 없었다. 문제가 생겼을때 유저 커뮤니티(꼭 그룹스나 IRC만을 얘기하는 건 아니다)에 도움을 받지 못하는 것 상당히 문제다. 결국은 Thymeleaf 사이트가서 가이드 문서를 찾아가면서 직접 연구해 보는것 외에는 아직 큰 답이 없어 보인다.

장점

가장 큰 장점이라고 생각했던(다른 템플릿 엔진에서도 그리 불편하진 않았기 때문에) 네츄럴 템플릿이 기대 이하였기 때문에 딱히 Thymeleaf만의 장점이라고 하면 잘 생각나진 않지만 그래도 가장 큰 장점은 Spring MVC와 통합이 잘 되다는 장점이 있고 스프링소스에서 꽤 밀고 있는 부분에 기대를 걸어볼 수 있지만 스프링소스가 밀었지만 잘 통하지 않았던 것들도 꽤 있으므로 이는 시간을 두고 볼 일이다.

결론

특별한 장점을 느끼지 못한 이상 기존해 사용하던 템플릿 엔진을 대안으로 쓸 것 같다. 이 글은 Thymeleaf에 대한 초기 인상을 기록하기 위한 것이므로 아직 대안을 테스트해보거나 심도있게 고민해 보진 않았다. 장점을 안적다시피해서 너무 안좋게 얘기한것같기는 하지만 뭐 인상이 그런것이고 다른 템플릿엔진에서도 되는 것은 딱히 장점이라고 생각하지 않기 때문이다. 어쨌든 언어를 떠나서 가장 괜찮은 템플릿 엔진은 (HTML 작업에 대한 협업이 큰 고려사항이 아니라면) Jade인듯.. ㅎㅎㅎ

  1. benelog님의 제보에 따라 <script th:remove="all" type="text/javascript" src="../../js/thymol.js"></script>와 같이 작성하면 Thymeleaf에서는 자동으로 빠지므로 넣었다 뺐다 할 필요없습니다. [Back]
  2. 마찬가지로 benelog님이 알려주셨는데 이러한 부분도 <tr th:remove="all">와 같이 추가해 놓으면 넣었다 뺐다 할 필요는 없습니다. [Back]
2013/08/12 00:54 2013/08/12 00:54