오늘은 어디로 갈까...

메일 템플릿에 대한 단상(斷想) 본문

낙서

메일 템플릿에 대한 단상(斷想)

剛宇 2009. 4. 10. 12:58
 요즘 메일 템플릿 변경 작업을 하고 있는데, 기분이 별로이다.
 이곳의 메일 구조는 메일 전송 테이블에 전송 데이터를 넣으면, 데몬이 모니터링하고 있다가 발송하는 구조이다. 뭐, 이런 구조야 흔하디 흔한것이라 별로 반감은 없는데, 문제는 메일 내용을 생성하는 부분이다. 테이블에 URL을 입력하고 데몬이 URL을 호출하여 랜더링된 HTML을 받는다. 즉, 일종의 HttpClient가 웹서버를 호출해서 응답 페이지를 받는것이다. 물론 전혀 문제될게 없는 구조이다. 그럼에도 불구하고, 본인의 성격이 괴팍한것인지, 왠지 마음에 안든다. 굳이 웹페이지를 호출할 필요 없이, 템플릿 엔진을 사용해서 간단하게 구현하면 될것인데, 왜 이렇게 구현해놨을까 하는 쓸데없는 생각은 마음을 갉아먹고 정신을 혼미하게 만들었다. 그래서 스스로를 위로하기 위해서 대충 만들어보도록 하겠다.
 아주 간단히 치환을 하는정도에서 끝내려면, MessageFormat 클래스로 끝낼수도 있지만, 모양(?)이 안나는 관계로 템플릿 엔진을 사용해보도록 하자. 물론 템플릿 엔진을 만드는것도 하나의 유희(遊戱)일 수도 있지만, 시간 관계상 기존에 만들어놓은것을 가져다 쓰자.
 자바 세계에서 유명한 템플릿 엔진은 velocity(http://velocity.apache.org/)와 FreeMaker(http://www.freemarker.org/) 정도가 있겠다. 여기서는 FreeMaker를 사용하겠다. 특별한 이유는 없고, 본인이 FreeLancer라서 동류(?)인 FreeMaker가 마음에 드는것 뿐이다.

 FreeMarker는 자바(Java)로 만들어진 템플릿 엔진(template engine)이다. 원래는 서블릿(servlet) 어클리케이션에서, 뷰(View)인 동적 HTML 웹 페이지를 생성하기 위해 디자인되었다. 

 여기서는 당연히(?) 서블릿과 전혀~ 관계없이, 발송할 메일 HTML을 만들기 위해 사용해보겠다.
 일단 다운로드(http://www.freemarker.org/freemarkerdownload.html) 한다음 클래스패스에 freemarker.jar를 추가해주자.

1. Configuration 인스턴스(instance) 생성
 - 가장 먼저해야할 일은 Configuration 인스턴스를 생성하는 것이다.
 - Configuration 클래스에, 템플릿 파일이 저장된 위치, 템플릿과 데이터 모델을 결합시키는데 도움을 주는 ObjectWrapper 등을 저장할 수 있다.
Configuration cfg = new Configuration();
// 템플릿 파일이 저장되어 있는 경로
cfg.setClassForTemplateLoading(TestFreeMarker.class, "");
// 템플릿과 데이터 모델을 어떻게 결합시킬지 지정하는 부분
cfg.setObjectWrapper(new DefaultObjectWrapper());


 - 템플릿 위치를 지정하는 기본인 방법은 아래와 같다.
   void setDirectoryForTemplateLoading(File dir);
   void setClassForTemplateLoading(Class cl, String prefix);   
   void setServletContextForTemplateLoading(Object servletContext, String path);

- 만약 템플릿 위치가 한 군데가 아니라, 여러군데 나눠저 있으면 아래와 같이 MultiTemplateLoader를 생성한 후 설정해주면 된다.
import freemarker.cache.*;

Configuration cfg = new Configuration();

FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates"));
FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates"));
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "");
TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2, ctl };
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);

cfg.setTemplateLoader(mtl);



2. 데이터 모델(Data-Model) 생성
 - 간단한 데이터를 생성해보자.
(root)
  |
  +- user = "whatagreat"
  |
  +- blog
      |
      +- url = "http://blog.naver.com/whatagreat"
      |
      +- name = "케로?"  
Map<String, Object> root = new HashMap<String, Object>();
root.put("user", "whatagreat");
Map<String, Object> blog = new HashMap<String, Object>();
root.put("blog", blog);
blog.put("url", "http://blog.naver.com/whatagreat");
blog.put("name", "케로?"); 



3. 템플릿(template) 가져오기
 - 사용할 템클릿을 불러오겠다. Configuration 클래스의 getTemplate() 메소드를 사용하면 된다.
Template temp = cfg.getTemplate("test.ftl"); 

 - test.ftl
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>당신의 블로그는
  <a href="${blog.url}">${blog.name}</a>
</body>
</html>


4. 템플릿에 데이터 모델 적용하기
 - 불러온 템플릿에 데이터 모델을 적용하고 출력하면 된다.
Writer out = new OutputStreamWriter(System.out);
temp.process(root, out);
out.flush(); 


5. 실행
 - 전체 소스
package test.free;

import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

public class TestFreeMarker {

	public static void main(String[] arsg) throws Exception {
		Configuration cfg = new Configuration();
		cfg.setClassForTemplateLoading(TestFreeMarker.class, "");
		
		cfg.setObjectWrapper(new DefaultObjectWrapper());  
		
		Map<String, Object> root = new HashMap<String, Object>();
		root.put("user", "whatagreat");
		Map<String, Object> blog = new HashMap<String, Object>();
		root.put("blog", blog);
		blog.put("url", "http://blog.naver.com/whatagreat");
		blog.put("name", "케로?"); 
		
		Template temp = cfg.getTemplate("test.ftl");
		Writer out = new OutputStreamWriter(System.out);
		temp.process(root, out);
		out.flush();  
	}
}

 - 이제 모든 준비가 끝났으니 실행보자. 아무런 문제가 없다면, 아래처럼 결과가 나올것이다.
 * 실행결과
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome whatagreat!</h1>
  <p>당신의 블로그는
  <a href="http://blog.naver.com/whatagreat">케로?</a>
</body>
</html> 


6. FreeMarker 문법
 - 템플릿 작성에 필요한 간단한 문법을 알아보자.
주석
<#--주석-->

변수
선언 : <#assing x=0>
출력 : ${x}

조건문
<#if condition></#if>
<#if user = "whatagreat"></#if>
<#if condition><#elseif condition2><#else></#if>


반복문
<#list [대상] as [별칭]>
<#list user.blogList as blog>
 <li>${blog}
</#list>

include
<#include "/copyright_footer.html" >

built-in(내장 함수들)
대문자로 바꾸고 싶으면 upper_case를 쓰면 된다.
예) ${user?upper_case}
http://www.freemarker.org/docs/ref_builtins.html <- 여기에 가보면 많이 나온다.
출처 : http://www.freemarker.org/docs/ref_builtins.html


7. 공백 벗겨내기.
 - 템플릿 엔진을 사용할때, 가끔 문제가 되는데 공백 문제이다. 현재 우리가 사용할 용도에서는 전혀 문제가 없지만, 소개한 김에 적도록 하겠다.
 - t, rt, lt 사용해서 줄단위 공백을 제거한다. <#t> <#rt> <#lt>
 - ftl 파라메터 strip_text, strip_whitespace를 사용한다. <#ftl strip_whitespace=true>
 - compresss를 사용해서 지정한 구강의 공백을 제거한다. <#compresss></#compress>
출처 :  http://freemarker.sourceforge.net/docs/dgui_misc_whitespace.html
 
 



점심 시간이 끝난관계로 나머지 부분은 다음시간에 알아보도록 하겠습니다. 제목과는 다르게 FreeMarker 소개글이 되어버렸지만, 너그러운 아량으로 무시해주시기 바랍니다. ^^;