본문 바로가기
Old Posts/Java

[Java] 프리마커(Freemarker) - 자바 템플릿 엔진(Java Template Engine)

by A6K 2021. 7. 14.

프리마커는 아파치 오픈소스로 공개되어 있는 자바 템플릿 엔진(Java Template Engine)이다. 

템플릿 엔진(Template Engine)이란?

템플릿 엔진(Template Engine)이란 데이터 모델에서 정보를 얻어 템플릿 양식에 맞게 문서를 만들어주는 소프트웨어를 말한다. 템플릿 엔진의 사용자는 템플릿 정의 언어를 이용해서 문서화할 템플릿을 생성하고 템플릿 엔진에 입력한 다음 데이터 모델을 차례로 템플릿 엔진에 입력하여 문서를 만들어낸다. 

출처 : Apache Freemarker

템플릿 엔진 사용의 대표적인 예는 '웹 템플릿 엔진(Web template engine)'이다. 웹 페이지의 전체적인 아웃라인을 템플릿으로 만들어놓고, 페이지에 보여줄 상세한 데이터를 이용해 구체적인 HTML 문서를 만들어주는 형태로 템플릿 엔진이 사용된다.

웹 템플릿 엔진을 이용하면 웹 페이지의 표현을 담당하는 View 코드와 웹 페이지에 보여줄 데이터를 다루는 로직에 대한 코드를 분리해서 관리할 수 있다는 장점을 얻을 수 있다. View 코드를 담당하는 개발자는 데이터베이스의 연결이나 비즈니스 로직에 관계없이 템플릿을 이용해 어떻게 화면을 구성할지 어떻게 정보를 보여줄지에 집중할 수 있고, 데이터 로직을 담당하는 웹 페이지의 구체적인 표현방식에서 자유로워질 수 있다.

아파치 프리마커(Apache Freemarker)

아파치 프리마커(Apache Freemarker)는 텍스트 형태로 데이터를 출력해주는 자바 템플릿 엔진(Java Template Engine) 라이브러리다. 자세한 내용은 프리마커 홈페이지에서 찾아볼 수 있다. (링크 : https://freemarker.apache.org

아파치 프리마커는 현재 자바 진영에서 가장 많이 사용되는 템플릿 엔진 중 하나다. 

프리마커는 2015년 9월 2일부터 아파치 프로젝트로 관리되고 있다. 아파치 라이센스 2.0(Freemarker 라이센스)이 적용되는 Free Software로 아파치 소프트웨어 재단(Apache Software Foundation)의 관리를 받고 있기 때문에 유지보수가 끊길 염려가 적은 오픈소스다. 여러해 동안 전세계 수 많은 사용자들에게 사용되고 있어 탄탄한 유저 커뮤니티와 웹 문서들의 도움을 받을 수 있다는 장점이 있다.

프리마커에서 사용할 템플릿은 FTL(Freemarker Template Language)이라는 언어로 작성된다. FTL은 간단한 데이터 추출에서부터 데이터 모델에 대한 조건문과 반복문, 매크로와 메소드 등 자바같은 범용 프로그래밍언어에서 찾아볼 수 있는 다양한 프로그래밍적 요소들을 제공하고 있다. 이런 기능들을 이용해서 간단한 템플릿 기능만을 사용하려는 초급 사용자에서부터 복잡하지만 효율적으로 템플릿 엔진을 사용하려는 고급 사용자들까지도 만족하면서 쓸 수 있는 기능들을 제공한다. 프리마커는 FTL에 대한 사용자 매뉴얼을 제공하고 있으니 구체적인 내용은 사용자 매뉴얼을 읽어보길 바란다. (링크 : FTL의 사용자 매뉴얼)

프리마커를 이용해서 템플릿과 데이터 모델을 분리하게 되면 템플릿 작성자와 데이터 로직 개발자가 독립적으로 일을 할 수 있게 된다. 위에서 언급했던 것처럼 데이터 로직 개발자는 HTML이나 CSS같은 데이터의 구체적인 표현에서 자유로워지고, 템플릿 개발자들은 DBMS 연결 등의 데이터 처리 로직에서 자유로워진다.

프리마커(Freemarker) 템플릿(Template)과 데이터 모델(Data Model)

간단한 예제를 통해서 프리마커의 템플릿과 데이터 모델의 처리에 대해서 알아보자. 웹 페이지를 프리마커로 생성하는 프로그램을 생각해보자. 아마도 웹 페이지는 다음과 같은 HTML로 구성될 것이다.

<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <h1>Welcome Dave!</h1>
        <p> welcome to our home page Dave. </p>
    </body>
</html>

웹 페이지에 로그인한 사용자의 이름이 'Dave'인 경우 환영 메시지에 로그인한 사용자의 이름을 출력해준다. 만약 'Tom'이 로그인했다면 'Dave'라는 글자가 위치한 곳에 'Tom'이라는 글자가 출력되면된다.

로그인한 사용자의 데이터에 따라서 변하는 부분을 프리마커에서는'${...}'라는 문법을 사용해서 가져오게 되는데 이 문법을 'interpolation'이라고 한다. interpolation이라는 단어는 "써넣음"이라는 뜻을 가지고 있다. Interpolation을 이용해서 로그인한 사용자의 이름을 출력하도록 템플릿을 작성하면 다음과 같다.

<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <h1>Welcome ${name}!</h1>
        <p> welcome to our home page ${name}. </p>
    </body>
</html>

템플릿에 있는 '${name}'이라는 부분이 나중에 로그인한 사용자의 이름으로 치환된다. 나머지 부분은 로그인한 사용자의 이름과 상관없이 동일한 문자열 그대로 결과 문서에 복사된다.

이제 프리마커의 데이터 모델을 살펴보자. 프리마커의 데이터 모델은 트리 형태의 구조를 가지고 있다. 템플릿 엔진은 템플릿에 있는 Interpolation의 중괄호 사이에 있는 경로(혹은 표현식(Expression))를 이용해서 데이터 모델의 루트부터 원하는 정보를 따라 내려간다.

예를 들어 위 트리와 같은 데이터 모델이 있다고 하자. 템플릿에 쓰인 '${name}'은 루트의 'name'이라는 이름을 갖는 차일드 노드의 값으로 치환된다. 만약 '${dept.dept_id}'라고 템플릿에 쓰여 있다면 템플릿 엔진은 데이터 모델의 루트부터 'dept' 노드를 찾고 'dept_id' 노드를 찾아서 값을 치환해 줄 것이다. 이처럼 Dot('.') 문자는 트리의 노드를 따라 내려가는데 사용된다.

따라서 템플릿 엔진에 데이터를 제공하는 프로그래머는 프리마커 엔진이 데이터 모델을 트리 구조처럼 따라갈 수 있도록 모델링해줘야 한다.

 

프리마커(Freemarker) 사용법

새로운 언어나 라이브러리를 배울 때 무작정 따라하는 것만큼 좋은 것은 없다. 프리마커 템플릿 엔진을 이용하는 간단한 자바 애플리케이션을 작성해보자.

프리마커(Freemarker) 다운로드

프리마커를 사용하기 위해서는 프리마커의 다운로드 페이지를 들어가서 필요한 라이브러리를 다운로드해 프로젝트에 포함시켜주면 된다. (링크 :  다운로드 페이지)

만약 메이븐 프로젝트로 개발하고 있다면 다음 의존성(Dependency)를 추가해주면 된다. 

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>

데이터 모델(Data model) 만들어주기

아까 언급했듯이 프리마커 템플릿 엔진은 트리 형태로 구성된 데이터 모델을 사용한다. 템플릿으로 출력할 데이터 모델을 트리 형태로 구성해야 템플릿 엔진이 적당한 값들을 찾아서 치환해준다. 가장 간단한 방법은 해시맵(HashMap)을 통해 트리 형태의 데이터 모델을 만드는 것이다.

다음 코드를 참고해보자.

import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

public class TestFreemarker {

    public static void main(String []args) throws Exception {

        // Data Model 만들기
        Map<String, Object> root = new HashMap<>();
        root.put("key1", "value1");
        Map<String, Object> key2 = new HashMap<>();
        key2.put("key3", "value3");
        key2.put("key4", "value4");
        root.put("key2", key2);

        // Template Loader 
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        FileTemplateLoader loader = new FileTemplateLoader(new File("/tmp"));
        cfg.setTemplateLoader(loader);

        StringWriter writer = new StringWriter();

        // /tmp 디렉토리에 있는 "template2.ftl" 파일을 로드
        Template template = cfg.getTemplate("template2.ftl");

        // Map 객체를 이용해서 템플릿 처리
        template.process(root, writer);

        System.out.println(writer);
    }
}

Map 컬렉션을 이용해서 데이터 모델을 구성하면 프리마커가 루트부터 Map을 키 값으로 찾아 내려가면서 트리형태로 찾아 내려간다. root 변수가 가리키는 HashMap에 저장한 데이터 모델을 트리형태로 표현하면 다음과 같다.

이 데이터 모델은 템플릿을 로드해서 Template 객체의 process() 메소드에 의해 처리된다.

템플릿 로드

프리마커는 파일에 정의되어 있는 템플릿을 로드해서 Template 객체로 만들 수 있는 FileTemplateLoader 클래스를 지원한다. FileTemplateLoader 클래스는 템플릿 파일이 위치한 디렉토리 경로를 인자로 받아 생성된다.

이후 cfg.getTemplate() 메소드에서 입력한 템플릿 파일을 FileTemplateLoader가 생성할 때 입력했던 디렉토리에서 찾게 된다. 이런 방법으로 프리마커는 여러 템플릿을 번갈아가면서 쓸 수 있는 기능을 제공한다. cfg.getTemplate("template.ftl") 메소드는 FileTemplateLoader 생성시 입력했던 디렉토리에서 "template.ftl" 파일을 찾아서 로드하고 대응되는 Template 객체로 돌려준다. 

이 Template 객체는 "template.ftl" 파일의 템플릿을 해석할 수 있는 객체로 process() 메소드에서 데이터 모델을 인자로 받아 Writer 클래스에 완성된 텍스트를 써준다.

템플릿 파일 작성

위에서 본 자바 프로그램은 파일 형태로 저장되어 있는 템플릿 파일을 로드하는 동작만 수행했다. 구체적인 템플릿 내용은 템플릿 파일에 FTL 언어를 이용해 작성해야한다.

"/tmp/template.ftl" 파일에 다음 내용을 써보자.

key1 : ${key1}
key3 : ${key2.key3}
key4 : ${key2.key4}

위에서 잠깐 언급했던 대로 Interpolation에 데이터 모델의 트리 구조를 따라 내려갈 수 있는 경로 정보를 썼다.  ${...} 부분이 결과 문서에서 데이터 모델의 값으로 치환된다. (템플릿 문법에 대한 자세한 내용은 Freemarker의 매뉴얼을 참고하자)

결과 확인

템플릿 파일을 작성하고 작성했던 자바 프로그램을 실행하면 다음 결과를 얻을 수 있다.

key1 : value1
key3 : value3
key4 : value4

만약 템플릿에 쓰여진 경로를 데이터 모델에서 찾을 수 없다면 freemarker.core.InvalidReferenceException이 발생한다. 다행히 프리마커에서는 데이터 모델에서 값을 찾을 수 없을 경우, 예외를 발생시키는 대신 기본값으로 사용할 값을 적을 수 있는 문법을 제공한다. ${key5!"default"} 라고 적어 넣으면 key5 경로가 데이터 모델에 존재하지 않을 경우, "default"라는 문자열로 치환한다.

 

 

코딩없이 템플릿 만들어보기

템플릿 작성자의 경우 불필요하게 코딩하기 싫은 경우가 있다. 프리마커(Freemarker)에서는 "온라인 프리마커 템플릿 테스터(Online Freemarker Template Tester)" 페이지를 제공하고 있다. 자바 코딩 없이 템플릿과 데이터 모델을 입력해서 테스트해볼 수 있다.

- Online FreeMarker Template Tester

Data model 부분에 데이터 모델에 대한 정보를 넣고, Template 부분에 출력하고 싶은 템플릿을 입력한다음 [Evaluate] 버튼을 눌러서 템플릿을 실행시키면 결과를 얻을 수 있다.

기본적인 개념은 이 포스트에서 다룬 내용과 같다. 하지만 프리마커는 블로그 포스트 하나로 다룰 수 없을 정도로 다양한 기능을 제공하고 있다. 더 많은 내용은 프리마커 매뉴얼을 참고하기 바란다.

Reference

댓글