루비 프로젝트에서 사용할 수 있는 다양한 템플릿 엔진
May 22nd, 2008
레일스의 컴포넌트 중 실제 사용자에게 보여지는 뷰를 담당하는 부분은 액션팩 중 액션뷰 라이브러리다. 그리고 이 액션뷰 라이브러리는 다양한 템플릿 엔진을 사용한다. 이 글에서 말하고자 하는 것은 루비에서 사용할 수 있는 템플릿 엔진이 생각보다 많고, 이를 상황에 따라 선택해서 사용할 수 있다는 사실이다. [RailsConf 1일째] Scalability와 View에 대한 고민들에서도 비슷한 내용을 이야기한 적이 있다.
레일스 2.0 - 템플릿 엔진 지원 강화와 멀티뷰
레일스 2.0에서 뷰 파일의 명명법이 바뀌었다. 기존 방식은 확장자명이 응답의 mime type에 따라 rhtml, rxml, rjs를 붙여주는 방식이었다. 2.0에서는 여기에 어떤 렌더링 엔진을 사용할지 결정할 수 있게 하였다. 바뀐 방식은 다음과 같다.
- 메서드_이름.마임_타입.템플릿_엔진
예를 들어 edit 액션에 xml 요청에 대한 뷰 파일은 edit.xml.으로 시작하는 모든 파일이 된다. 이 파일은 edit.xml.erb, edit.xml.liquid, edit.xml.builder 등 다양하게 존재할 수 있다. 기존 뷰 파일을 2.0 스타일로 쉽게 바꿔줄 수 있는 방법도 있으니 참고 바란다.
그리고 여기에 더해 예전에는 하나의 mime type에 대한 응답은 항상 한가지라는 가정이 있었지만, 이제는 그 가정이 깨져서 mime type은 text/html인 iphone 포맷 등을 정의해서 사용할 수 있다. 이게 레일스 2.0에서 말하는 멀티뷰다. 이 때 사용되는 뷰 파일을 edit.iphone.haml 등이 된다. (참고: iPhone on Rails - Creating an iPhone optimised version of your Rails site using iUI and Rails 2 , Give your Rails 2.0 application an iPhone UI)
아래 코드로 쉽게 서드파티 렌더링 엔진을 등록할 수 있다.
- ActionView::Base::register_template_handler :liquid, LiquidView
이렇게하면 liquid라는 확장자가 붙은 뷰 파일은 모두 LiquidView에서 렌더링한다.
지금부터 루비에서 사용할 수 있는 템플릿 엔진들을 하나씩 구경해보자. 물론 가장 널리 쓰이는 것은 ERB와 Builder다.
ERB - 기본 렌더링 엔진
ERB는 Embedded RuBy의 약자다. php처럼 템플릿에서 특정 태그(<% %>)와 함께 루비 코드를 사용할 수 있게 한다.
- Hello, <% print "World" %>.
ERB라는 용어는 루비를 임베딩해서 사용하는 모든 방식을 통칭하는 말이기도 하지만 대부분 표준 라이브러리에 포함된 ERB를 지칭하기도 한다. 물론 다른 구현체도 있다. 특히나 속도라는 측면에서 매력적인 Erubis는 주목할만 하다.
ERB의 장점은 가장 익숙한 방식이라는 점, 쉽게 사용할 수 있다는 것이고, 단점은 HTML 안에서 제약없이 루비 코드를 넣을 수 있으므로, 잘못 만들면 유지보수하기 힘든 스파게티 코드가 될 수도 있다는 것이다. 잘못된 코드는 작성한 사람뿐 아니라 그렇게 작성하게 만든 환경까지 연대책임을 져야한다는게 내 생각이다.
Builder - XML에 최적화
Builder는 루비의 블럭 문법을 활용해서 xml을 그리는 라이브러리다. 자세한 사용법은 Creating XML with Ruby and Builder을 참고하자.
- xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/" do
xml.channel do
xml.title "Feed Name"
xml.link url_for :only_path => false, :controller => 'posts'
xml.pubDate CGI.rfc1123_date @posts.first.updated_at if @posts.any?
xml.description "Feed Description"
end
end
위 예제는 RSS 2.0 피드를 만들기 위해 builder 템플릿 코드의 일부다(출처). Slugger - 스프링노트 블로그 프론트엔드에서는 ATOM 피드를 만들기 위해 Builder를 사용하고 있다. 위 코드에서도 볼 수 있듯이 데코레이션이 없는 데이터 중심의 XML을 만들기 위해서는 builder가 최적이다.
MasterView - 디자이너를 위한 템플릿
마스터뷰(MasterView)는 기존 WYSIWYG HTML 에디터에서도 깨지지 않는 형식을 유지해서, 디자이너 또는 HTML 코딩을 담당하는 파트와의 협업을 월활하게 하자는 것이 목표다. (X)HTML을 사용자 속성이나 태그로 확장하자는 것이 기본 아이디어다.
- <html mv:generate="layouts/product.rhtml">
<title>Hello</title>
<body>
<div mv:replace="@content_for_layout">
<div mv:generate="product/new.rhtml">
<form></form>
</div>
</div>
</body>
</html>
코드에서 볼 수 있듯이 기존 HTML 코드들은 모두 유지하면서 사용자 속성을 추가해서 확장하는 방식이다. 하지만 개발자에게 더 번거러울수도 있다는 단점이 있다. 별도의 태그 라이브러리를 숙지해야한다.
Haml - 간결함만이 살 길
Haml은 DRY를 신봉하는 루비 개발자가 좋아할만한 문법이다. 장황한 HTML 대신 간결한 문법으로 표현할 수 있는게 특징이다. 단점은 물론 배워서 익숙해져야한다는 사실!
- !!!
%html
%head
%link{ :rel => "shortcut icon", :href => "/favicon.ico", :type => "image/x-icon"}/
%title= "Wallpaper Love Factory :: "+controller.controller_name.capitalize
= stylesheet_link_tag 'wallpaper'
%body
.header#header
= render :partial => 'layouts/header'
.menu#menu= render :partial => 'layouts/menu'
.content#content= yield
너무 압축을 한 나머지 Haml에 익숙한 팀이 아니라면 한 눈에 읽기는 어려워보인다. 파이선처럼 공백 들여쓰기도 주의해야한다(코드 출처: Cheating on ERB with HAML).
Markaby - 루비 DSL
Markaby는 MARKup As ruBY를 줄인 말이다. 이름에서 알 수 있듯 루비로 마크업을 하자는 취지, 즉 루비로 HTML을 만드는 일종의 DSL이다. 문법은 Builder와 유사하다.
- html do
head do
title action_name
stylesheet_link_tag 'scaffold'
end
body do
p flash[:notice], :style => "color: green"
self << content_for_layout
end
end
기존 루비 코드(특히나 레일스 태그 핼퍼)와 매우 잘 어울리는 모습이다. 루비 개발자에게는 가독성도 최고다. 모든 것을 루비로를 주장하는 개발팀이라면 Markaby는 고려할만하다. 하지만 좀 과하다싶은 마음도 드는 것이 사실이다. 비슷한 라이브러리인 Erector도 있다.
Liquid - 테마 기능에 적합한 템플릿 엔진
Liquid는 Shopify라는 쇼핑몰 사이트 빌더 애플리케이션에서 추출한 것이다. 요구는 보안상 안전함을 잃지않으면서 템플릿을 사용자가 직접 정의해서 사용할 수 있도록 하는 것이다. 일종의 Sandbox를 구현하는 것이다. 따라서 Liquid 템플릿에 전달되는 데이터와 템플릿에서 호출할 수 있는 메서드들은 매우 엄격하게 관리된다. 그래서 사용자가 정의할 수 있는 테마(스킨) 기능을 구현하는데 가장 적합한 템플릿 엔진이라고 하는 것이다.
- <ul id="products">
{% for product in products %}
<li>
<h2>{{product.title}}</h2>
Only {{product.price | format_as_money }}
<p>{{product.description | prettyprint | truncate: 200 }}</p>
</li>
{% endfor %}
</ul>
하지만 템플릿에 데이터를 전달하는 방법은 쉽지 않다. 모든 부분을 Drop이나 Filter로 만들어줘야하고 설계도 잘 되어야한다. 꽤 많은 노력과 시간 코드가 필요한 일이다. 참고로 슬러거에서도 Liquid를 도입했다(039 슬러거 테마 지원 기능 추가).
Amrita2 - 데이터를 그대로 그려라
- <table>
<tr><th>name</th><th>author</th></tr>
<tr id="table1">
<td id="name" /><td id="author" />
</tr>
</table>
그리고 이 템플릿이 다음과 같은 데이터와 합쳐지면,
- data = {
:table1=>[
{ :name=>"Ruby", :author=>"matz" },
{ :name=>"perl", :author=>"Larry Wall" },
{ :name=>"python", :author=>"Guido van Rossum" },
]
}
아래처럼 렌더링된다.
- <table>
<tr>
<th>name</th>
<th>author</th>
</tr>
<tr>
<td>Ruby</td>
<td>matz</td>
</tr>
<tr>
<td>perl</td>
.......
잘 정의된 데이터 모델을 XHTML로 렌더링하기에 적합해보인다. 하지만 이런 템플릿을 실제 프로덕션 애플리케이션에 원활하게 적용할 수 있을까? XSLT등을 이용해 한번 더 가공이 필요하지는 않을까 걱정이 도기도 한다. 그런데 깔끔함 하나는 마음에 든다.
DRYML - 반복이 싫다
DRYML은 Hobo의 일부다. DRY라는 이름처럼 반복되는 부분을 태그라이브러리로 정의하고 이 태그를 재사용하자는 취지다.
예를 들어 아래처럼 mytag를 정의하고,
- <def tag="box" attrs="title">
<div class="box">
<h2><%= title %></h2>
<tagbody/>
</div>
</def>
실제 코드에서 아래처럼 사용한다.
- <box title="My Box"><p>I'm in a box</p></box>
한번 태그들과 렌더링을 위한 CSS를 잘 정의해놓고 계속 조합해서 사용하는 곳이라면 적합할 것같다. Hobo가 애플리케이션 빌더이기 때문에 이런 DRYML이 매우 적합해보인다. 그런데, 사용하기가 쉽지만은 않아보인다. 얼마나 태그라이브러리를 잘 만들어 관리하느냐가 관건일 것 같다.
당신의 선택은?
여기서 소개하지 않은 템플릿 엔진도 있을 수 있다. 결론은 요구에 가장 적합한 엔진을 골라서 최고의 생산성을 내자는 것이다. 완벽하게 어느 상황에서는 이 템플릿 엔진을 선택하세요라고 말하기도 쉽지 않다. 내 추천은 아래와 같다.
- 잘 정의된 데이터 구조가 있다면 Amrita2
- 일반적인 XML을 생성하려면 Builder
- 디자이너가 계속 WYSIWYG 에디터를 통해 html을 관리하기 원한다면 MasterView
- 사용자 테마 기능이 필요하다면 Liquid
- 루비에 중독되었다면 Markaby
- 정말 깨끗한 템플릿을 원한다면 Haml
- 재사용하는 태그세트가 많다면 DRYML
- 뭘 써야할지 모르겠다면 ERB
다음번에는 직접 렌더링 엔진을 하나 만들어볼까?
참고




December 13th, 2007 at 01:21 AM (myRuby.net) 네. 만들어주세요. :)
December 13th, 2007 at 05:18 PM 꽃띠앙// 근데 아이디어가 없어요 ^^
December 31st, 2007 at 06:35 AM (myRuby.net) liquid 라는 템플릿 엔진은 django의 그것과 매우 유사하네요. 저걸 쓰면, django 프로젝트와도 호환이 가능한 템플릿을 만들 수 있겠네요.
September 18th, 2008 at 10:30 AM