서론
Java 서버를 개발하면서 View Template Engine에 대해서 매번 고민하게 된다.
spring boot를 써보면,
- 더 이상 JSP를 쓰지 말아야 할 것처럼 말한다
- boot에서 jsp를 쓸 경우 war로 만들어야 하며, WAS에 따라서 지원하지 않을 수 있다 - Tomcat만 고집한다면야…
- Velocity는 boot에서 지원하지 않는다
- 너무 오랫동안 업데이트가 없다면서 뺐는데, 2017-08-06에 Velocity Engine 2.0이 나왔다
- Thymeleaf3는 여전히 느린 모양이다.
- Freemarker가 그나마 가장 무난하다.
위와 같이 알고 있던 중에 최신 spring 문서를 뒤져보다가, script views
라는 게 있다는 걸 알게 되었다.
Handlebars, Mustache, React, Kotlin Script templating 등등의 많은 라이브러리가 있었는데, 그중에서 Mustache를 살펴보려 한다.
Mustache
Mustache는 Simple하다.
얼마나 Simple하냐면, 설명도 Logic-less templates.
가 끝이다.
별다른 로직이 존재하지 않아서 배우는 것도 간단하다.spring-boot
에서 starter
를 지원하기 때문에 설정도 간단하게 할 수 있다.
하지만 가장 중요한 것은 Logic-less
라는 점이다.
MVC 패턴으로 개발을 하다 보면, view에 로직을 구현하려는 경우를 종종 보게 된다.
web view를 작성하다 보면 필연적으로 JavaScript를 사용하게 되고,
client에서 필요한 로직을 JavaScript로 구현하게 된다.
개인적으로 client의 역할과 view의 역할은 분리해야 한다고 생각하며,
view에 로직이 구현되어 있는 경우, OOP의 5원칙 중에 SRP를 어겼다고 생각한다.
때문에 Mustache의 Logic-less
라고 하는 점은 매우 끌리는 장점 중 하나이다.
Mustache 배워보기
기본적으로 Mustache의 Manual을 따르며 하나씩 spring-boot와 연계해보겠다.
프로젝트 의존성 설정
spring-boot 버전 : 2.0.0.RELEASE
간편하게 만들기 : https://start.spring.io/
1 | dependencies { |
application.yml
기본설정은 spring.mustache.suffix=.mustache
다.
이를 .html
로 바꿨다. (취향)
1 | spring.mustache.suffix: .html |
model
Online Fake Rest Api를 사용했다
1 |
|
controller
API | URI | viewName |
---|---|---|
목록 | /users |
/users/list |
상세 | /users/{id} |
/users/detail |
spring.mustache.prefix=classpath:/templates/
가 기본설정이다.
1 |
|
view
src/main/resources/templates/users/detail.html
User 객체의 속성을 보여준다
1 | <div class="container"> |
변수
콧수염 두 개({{id}}
)로 표현한다
HTML Escape
콧수염 안의 문자열은 기본으로 HTML Escape가 적용된다
1 | model.addAttribute("company", "<b>GitHub</b>") |
1 | - {{company}} |
1 | - <b>GitHub</b> |
unescape를 하기 위해서는
- 괄호를 3번 중첩 시키거나 :
{{{ compnay }}}
&
를 사용한다 :{{& company }}
1 | - {{{company}}} |
1 | - <b>GitHub</b> |
섹션
섹션은 콧수염과 샵으로 표현한다({{ #post }}
)
섹션에 들어가는 key 타입에 따라서 표현이 달라진다
False, Empty List
key의 값이 false이거나 empty list인 경우, 해당 섹션은 표시되지 않는다
1 | Shown. |
Non-Empty Lists
key가 비어있지 않은 list인 경우 섹션 내의 내용의 반복한다
GET /users
요청
list.html
1 | <div class="container"> |
결과
1 | <div class="container"> |
Lambda
lambda식을 실행할 수 있다
1 | {{#user}} |
1 |
|
결과
1 | <!-- 이름이 Leanne Graham인 경우 --> |
lambda식은 아래 interface를 구현해서 만들 수 있다.
1 | public interface Lambda { |
frag.execute()
: 내부의 표현식을 실행한 후 String으로 반환한다.frag.context()
: 상위 오브젝트(여기서는 User)를 가져온다.frag.context(int n)
: n번째 상위 오브젝트를 가져온다.
이러한 기능을 제공하나, 행여나 view에서 serivce logic을 작성하지는 말자.
False값이 아닌 경우
list도 아니고, false도 아니라면 이를 block을 가진 context(즉 object)로 간주한다
1 | model.addAttribute("user", new User("abc")); |
1 | {{#user}} |
결과
1 | Hello, abc! |
Inverted Section
섹션에 논리 부정 연산자(!
)가 붙은 격이다
1 | model.addAttribute("users", Collections.emptyList()); |
1 | {{#users}} |
결과
1 | No users... |
주석
{{! ignore me }}
Partials
{{>users/list-item}}
과 같이, 외부 문서를 불러올 수 있다.
GET /users
src/main/resources/templates/users/list.html
1 | <div class="container"> |
src/main/resources/templates/users/list-item.html
1 | <tr> |
Delimter
콧수염({{ }}
) 을 다른 기호로 바꿀 때 사용한다
콧수염을 <% %>
로
1 | {{ name }} |
Spring Boot Property
기본으로 설정되어 있는 Property와 기본값은 다음과 같다
1 | # HttpServletRequest의 attribute를 Controller에서 생성한 동일한 이름의 model attribute로 덮어 쓸 수 있는지 여부 |
마무리
script 기반의 view template engine인 mustache에 대해서 알아보았다.
여타 다른 엔진에 비해서 제공하는 기능은 훨씬 적다.
하지만 간단하고 배우기 쉬우며, spring boot와 통합도 제공한다.
2015년 기준으로 Freemarker나 Velocity에 견줄 수 있을 정도의 속도가 나왔다고 한다.
물론 이런 종류의 테스트는 최적화를 위해서는 더 다양한 케이스를 테스트해 봐야겠지만, 생각보다 훨씬 빠르다는 것에 다시 놀랐다.
logic-less의 view를 구현하고, client의 logic을 node나 전담 front-server에서 구현할 수 있다면, 꽤 괜찮은 솔루션이라 생각된다.