2013년 7월 11일 목요일

Apache Camel 기반 이메일 전송 라이브러리


1. 들어가며

"기업 통합 패턴과 메일러 애플리케이션"이란 이전 글에서 메일 발신을 기업 통합 패턴의 관점에서 해석하고 간단한 메일러 배치 애플리케이션을 개발하는 과정을 보였다. 이 메일러 애플리케이션은 독립된 애플리케이션으로 데이터베이스와 메일 서버를 통합한다. 그럼 애플리케이션들 사이의 통합이 아닌 애플리케이션 내부의 기능들의 통합에도 기업 통합 패턴을 이용할 수 있을까? 이번 글은 이 문제를 다루어 보려고 한다.


2. 상황

어떤 기업에서 고객 관리 애플리케이션을 개발하고 있다고 가정해 보자. 이 애플리케이션의 주요 기능은 고객 관리 기능이다. 그런데 필요한 경우 고객에게 안내 이메일을 전송해야 한다. 그런데 이메일 전송에 필요한 고객의 정보들은 이미 애플리케이션에서 관리하고 있다. 그러므로 현재 개발 중인 고객 관리 애플리케이션의 입장에서는 애플리케이션으로부터 전달 받은 고객 정보를 이용하여 메일을 전송하는 라이브러리가 있으면 된다. 그런데 안내 이메일의 특성상 메일 본문이 자주 변경될 수 있다. 그리고 메일 발신 대상 고객마다 별도의 메일 본문을 생성하기에는 안내 내용이 너무 크다. 즉 이럴 경우 메일 전송 요청을 위해 불필요한 저장 공간이 사용된다. 그러므로 메일 전송 라이브러리는 고객 정보와 안내 메일의 본문을 분리할 수 있어야 한다.


3. 요구 분석

개발하고자 하는 고객 관리 애플리케이션의 메일 전송 라이브러리는 다음과 같은 요구를 충족해야 한다.

  • 1) 메일 발신 기능이 필요하다.
  • 2) 메일 발신에 필요한 정보는 애플리케이션에서 모두 전달한다.
  • 3) 메일 본문은 별도의 템플릿을 관리해야 한다.
  • 4) 애플리케이션에서 사용할 수 있는 라이브러리로 개발돼야 한다.

여기에 나열된 요구들은 고객을 관리하는 애플리케이션들에게서 흔하게 볼 수 있는 요구들이다.


4. 기업 통합 패턴 설계

기업 통합 패턴의 관점에서 메시지는 애플리케이션으로부터 메일 전송 라이브러리를 거쳐 메일 서버로 전달된다. 이를 위해 애플리케이션에서 메시징 시스템으로 데이터를 전달하기 위한 "메시지 엔드포인트"가 필요하다. 즉 애플리케이션에서 메시징 시스템을 액세스하는 얇은 API 계층이 필요하다. 그리고 이 라이브러리 내에는 메일 본문 템플릿에 고객 정보를 보태는 필터가 있어야 한다. 그리고 메일 전송 컴포넌트가 필요한데, 이 컴포넌트는 이전에 블로그에 올린 "에서도 사용된 컴포넌트이다. 이런 컴포넌트들을 포함한 아키텍처를 기업 통합 패턴의 EIP(Enterprise Integration Patterns) 다이어그램으로 그리면 다음과 같이 그릴 수 있을 것이다.

이 다이어그램은 애플리케이션과 메시징 시스템을 연결하는 "메시지 엔드포인트", 메일 전송과 관련된 고객 정보와 메일 본문을 결합하는 "내용 보탬이(Content Enricher), 메일 서버에게 메일을 전달하는 메일 컴포넌트의 메일 엔드포인트로 구성된다.


5. 사용 기술

메일 전송 애플리케이션을 개발하기 위해서 두 기술이 필요하다. 첫째, 메일 전송 기술이다. 이 기술은 Camel 메일 컴포넌트를 활용할 수 있다. 둘째, 템플릿 활용 기술이 필요하다. 이 기술 중 널리 알려진 기술은 Apache Velocity 템플릿 엔진이다. Camel은 Velocity 컴포넌트를 통해 이 기술을 이용할 수 있게 해준다. 각 Camel 컴포넌트에 대한 설명은 다음 웹사이트를 참조한다.

필자는 Eclipse 개발 환경에서 m2eclipse(maven plugin)을 사용하는 것을 좋아한다. 왜냐면 사용하는 라이브러리들을 자동으로 포함시켜 주기 때문이다. 위 두 Camel 컴포넌트 라이브러리와 관련 jar들은 다음 두 엘리먼트를 pom.xml 의 의존 엘리먼트 영역에 추가하면 maven repository에서 다운받을 수 있다.


6. 메시지 라우팅 구현

이제 간단하게 메시징 흐름 설계(?)를 마쳤으므로 본격적으로 구현(?)해 보자. 다음 클래스는 위 EIP 다이어그램에 따라 메시지 라우팅을 Camel 도메인 특화 언어(DSL, Domain Specific Language) 중 Java DSL로 정의한 MailSenderBuilder .java이다.

위 클래스는 Apache Camel의 RouteBuilder 인터페이스를 구현하여 메시지 라우팅을 정의한다. 이 클래스는 메일 서버의 접속 정보는 설정자(setter)를 통해 주입 받는다. Spring 프레임워크를 고려하여 설정자를 노출한 것이다. 위의 소스의 라우팅 정의에서 to("velocity 로 시작하는 메시지 소비자(내용 보탬이)의 정의를 보면, 메시지 라우팅은 Apache Velocity 엔진 컴포넌트를 이용하고 있음을 알 수 있다. 단 한 줄로 Velocity 기능을 추가했다! Velocity 템플릿인 letter.vm 파일의 위치는 클래스 패스 아래 camel/example/client/template/letter.vm 이다. 참고로 템플릿 파일의 위치는 메일 접속 정보를 설정자를 주입하는 것처럼, 설정자를 외부에서 주입되게 바꾸는 것은 어렵지 않다. 메일 전달 기능도 smtp 소비자 엔드포인트 한 줄로 추가한다! 발신되는 메일의 형식이 HTML 형식의 이메일임을 지정하기 위해 URI의 입력 파라미터로 "contentType=text/html"을 지정했다.


7. 메시지 엔드포인트 구현

Apache Camel Context 객체는 자체가 컨테이너이면서 메시징 시스템이다. 그러므로 메시지 엔드포인트가 애플리케이션이 Camel의 메시징 시스템으로 데이터를 전달할 수 있게 API를 노출해야 한다. 이를 위해 구현된 메시지 엔드포인트 클래스가 MailSender.java이다.

MailSender 클래스는 semd 메소드를 애플리케이션에게 노출한다. 이 메소드는 header 파라미터로 이메일 전송 정보를 입력 받고, model 파라미터로 메일 본문의 입력할 고객 정보를 입력 받는다.


8. 메일 템플릿

메시지 라우팅 정의에서 언급했듯이 메일의 본문은 HTML로 작성해야 한다. HTML 템플릿은 Velocity의 템플릿 파일인 "letter.vm"로 저장된다. 이 파일은 메일을 전송하기 전에 생성하거나 수정한다. 이 파일은 메시지 라우팅 정의에 따라 클래스 패스 아래 camel/example/client/template/letter.vm에 놓인다. 다음은 필자가 개콘의 황해를 흉내 낸 안내 메일 템플릿이다.

위 파일에서 ${body.name} 부분은 MailSender.send 메소드의 model 파라미터에 "name" 키로 put한 값으로 대체된다. 동일한 방법으로 대체하고자 하는 곳에 ${} 안에 "body." 을 접두사로 하는 키 값을 지정한다.


9. 테스트

벌써 개발이 완료되었다. 이제 테스트를 해보자. 테스트는 JUnit을 활용한다. 다음은 MailSender 클래스를 테스트하는 MailSenderTest.java이다.

필자가 개발한 메일 전송 라이브러리는 Camel 프레임워크를 사용하므로 setup 메소드에서 Camel Context를 생성하고, 메시지 라우팅 로직을 추가하고, 테스트하고자 하는 MailSender 객체를 생성하고, ProducerTemplate 객체를 생성하여 MailSender 객체에 전달한다. 그리고 test 메소드에서 메일 발신과 관련된 정보와 고객 정보를 추가하여 MailSender 객체의 send 메소드를 호출한다. 소스가 보이는 것처럼 복잡하지 않다.


10. 테스트 결과

필자는 테스트 결과로 다음과 같은 메일을 수신했다.




11. 타 구현과의 비교

인터넷을 검색해 보면 이메일 전송에 Apache Velocity 템플릿 엔진을 이용하는 방법에 대하여 설명한 사이트들이 꽤 나온다. 이 사이트들은 주로 Spring 프레임워크를 이용하여 구현하고 있다. 왜냐면 Java Mail API를 직접 사용하는 것보다 Spring API를 이용하는 것이 코드 량을 줄여 주기 때문이다. 이런 사이트들은 Spring 프레임워크를 활용함으로 Bean 정의와 Spring 프레임워크의 메일 전송 API, Velocity API를 호출하는 방법을 설명한다. 즉 프레임워크의 사용과 코딩 방법을 중심으로 설명한다. 이를 확인할 수 있도록 참고 사이트에 구현 사이트 중 한 곳의 링크를 걸어놓았다.

이에 반해 필자는 동일한 문제에 대해 기업 통합 패턴의 시각에서 데이터의 흐름 즉 메시지 라우팅의 관점으로 문제를 접근했다. 그리고 메시지 라우팅을 수립하고 나서 실제 코딩을 진행했다. 이 접근 방법의 장점은 문제에 대한 해결을 미시적인 구현에 집중하는 것이 아니라 전체적인 데이터의 흐름을 볼 수 있게 해준다는 점이다. 또 이렇게 파악된 메시지 흐름을 Camel DSL로 간결하게 표현함으로 코딩의 량을 극적으로 줄일 수 있었다. 메일 전송에 구현된 로직의 코딩은 실제 60줄도 채 안된다. 그리고 추가적인 장점으로 메시지 라우팅은 컴포넌트들 사이를 느슨한 결합(loose coupling)으로 정의한 것이므로, 이곳에서 구현된 메일 전송 라이브러리는 향후 추가 또는 변경되는 요구들에 대해서도 신속하게 대응할 수 있게 되었다.


12. 맺음말

이 글에서는 애플리케이션 내부에서의 기능 통합으로 볼 수 있는 메일 전송 라이브러리를 개발해 보았다. 기업 통합 패턴을 이용하여 문제를 접근하였고, 구현은 Apache Camel 프레임워크를 이용하여 구현하였다. 제시된 요구대로 Velocity 템플릿 엔진을 이용하여 메일의 본문을 별도 파일로 분리함으로 프로그램과 메일 본문 사이의 단단한 결합(tight coupling)을 제거하였다. 그러면서도 소스는 아주 간결하게 구현하였다. 이 과정을 통해 기업 통합 패턴이 애플리케이션이 활용하는 라이브러리에도 잘 활용될 수 있음을 보여주었다.

여기에 구현된 메일 전송 라이브러리는 기업 통합 패턴 중 "내용 보탬이(Content Enricher)" 패턴의 전형적인 예이다. 그리고 이 내용 보탬이 패턴을 Apache Camel 프레임워크가 제공하는 Velocity 컴포넌트를 이용하여 간결하게 구현하였다. 이렇게 일반적인 애플리케이션 내부의 통합 문제에 있어서도 Apache 프레임워크를 이용하면 다른 어떤 프레임워크를 사용하는 것보다 더 간결하게 프로그램 할 수 있다. 물론 Apache Camel 프레임워크를 잘 활용하기 위해서는 기업 통합 패턴을 잘 알아야 한다.

애플리케이션을 개발(통합)한다는 것은 각 기능들을 구현, 조립 또는 상속하여 상위의 서비스를 제공하는 것을 말한다. 이 기능들의 개발(통합)은 거시적으로는 기업 통합(Enterprise Integration)의 형태로 미시적으로는 애플리케이션 통합(Application Integration)의 형태로 진행된다. 그리고 기업 통합 패턴은 거시적이든 미시적이든 애플리케이션 개발(통합)의 모든 경우의 분석과 설계에 유용하고, Apache Camel 프레임워크는 이 모든 경우의 구현에 훌륭한 도구이다.


참고 사이트

댓글 없음:

댓글 쓰기