1. 메일 서버
메일 서버는 메일 전송 에이전트(MTA, Message Transfer Agent)로 기능하는 서버를 말하는데, 발신자인 메일러로부터 SMTP 프로토콜로 전송을 요청 받은 이메일을 여러 단계의 내부 큐들을 거치면서 수신자에게 다시 SMTP 프로토콜로 전송한다. 요청자로부터 이메일 전송을 요청 받은 메일 서버 즉시 요청 수신 확인을 요청자에게 응답하고, 수신자 시스템이 수신할 때까지 비동기적으로 메일의 전송을 최대한 보장한다.
그러므로 메일 서버 메일 전송을 위해 특화된 메시징 시스템이라고 볼 수 있다. 다시 말해 메일 서버 내부적으로 큐를 사용하고, 메일을 비동기적으로 전송하고, 메일은 메시지 구조의 데이터이므로 메시징 시스템이다. 그러나 독자적인 메일 전송 프로토콜(SMTP)을 사용하여 메일 전송 업무에 특화된다.
기업은 메일 전송에 다양한 요청 방법들을 필요로 하므로, 전송 환경도 다양하게 구축된다. 예를 들어 메일의 전송 요청은 데이터베이스로부터 일수도 있고, 파일로부터 일수도 있고, RPC(Remote Procedure Call)) 또는 전문 통신으로부터 일수도 있고, 메시지 큐로부터 일수도 있고, 웹 페이지로부터 일수도 있고, 아웃룩과 같은 메일 클라이언트로부터 일수 있다. 실시간으로 메일의 전송이 요청될 수도 있고, 특정 시간에 배치 형식으로 대량의 메일들이 요청될 수도 있다. 어떤 경우는 메일의 전송을 대행시키고, 어떤 경우는 메일 서버로 직접 전송을 요청한다.
2. 기업 통합 패턴
기업은 전형적인 기업 통합의 문제 중 하나인 메일러의 문제를 해결하기 위해 어떤 접근 방법을 사용할 수 있을까? 각 요청 방식에 대해 별도의 개발자나 개발팀에서 서로 각각의 방식으로 메일러를 개발할 수 있다. 이 경우 최악으로는 네 종류의 메일러 애플리케이션들이 만들어 질 수 있다. 또 실시간 처리인가 배치 처리인가를 고려한다면 다시 두 종류의 애플리케이션들로 또 나뉘어 지게 된다. 이와 같은 개발 접근 방법은 개발 비용도 많이 소요될 뿐만 아니라, 개발 이후 유지보수 비용도 상당히 많이 소요될 것이다. 그러므로 전체적으로 기업 통합의 관점에서 메일러 애플리케이션을 바라볼 수 있어야 한다.
"기업 통합 패턴(Enterprise Integration Patterns)"은 기업 통합 시 등장하는 상황을 65개의 패턴으로 정리하고 각 패턴에 대한 적용 방법과 장단점, 그리고 다른 패턴들과의 연관성들을 설명하는 패턴 언어이다. 기업 통합 패턴은 필자의 번역으로 곧 번역서로 출판될 것이다.
기업 통합 패턴에서 제시하는 패턴 중에 "정규 데이터 모델" 패턴이 있다. 이 패턴을 적용하는 경우, 메일러는 수용하는 데이터를 정규 데이터 형식으로 표준화하고, 각 애플리케이션들은 이 정규 데이터 모델의 구조를 따르게 메일러에게 요청할 데이터의 형식을 수정한다. 그런데 이런 접근 방법은 문제가 있다. 왜냐면 메일러를 이용하려는 애플리케이션은 메일러보다 먼저 개발됐을 수도 있고, 메일러가 요구하는 정규 데이터 모델로 애플리케이션을 수정하기가 어려울 수 있기 때문이다. 예를 들어 패키지로 도입된 애플리케이션인 경우 메일러의 정규 데이터 모델을 수용하기가 현실적으로 불가능 할 수도 있다.
그러므로 필요할 때마다 개발되는 애플리케이션도 문제이고, 메일러가 정규 데이터 모델을 강요하는 것도 문제가 된다. 그러나 정규 데이터 모델은 필요하다. 그렇지만 정규 데이터 모델 하나만으로는 기업에서 메일러의 통합을 완수하지 못한다. 그러므로 메일러는 수정될 수 없거나, 수정되기 어려운 애플리케이션들을 위해 "채널 어댑터"로서도 역할 해야 한다. 즉 애플리케이션의 접속 형식에 맞추어 접근할 수 있어야 한다. 이 경우 메일러는 필요한 "메시지 변환"을 자체적으로 수행해야 하고, 요청 메일을 폴링하는 "폴링 소비자"이거나, 병렬 처리가 필요한 경우 "경쟁 소비자"이어야 한다. 다시 말해 메일러 개발에 기업 통합의 접근 방법을 적용함에 있어, 이상적인 메일러의 구조인 정규 데이터 모델을 수용하기 어렵다면, 기존 애플리케이션들의 특성을 수용하는 통합 방법을 선택해야 한다.
기업 통합 패턴은 메일러를 개발 또는 도입하기 위해, 애플리케이션, 메일러, 메일 서버의 기능을 구별하게 하고, 메시지의 흐름과 변환을 전체적으로 고려하는 일관된 방법을 제공한다. 기업 통합 패턴을 일관되게 적용하면, 애플리케이션들을 통합의 관점에서 좀더 유연하게 만들 수 있게 되고, 개발 기간의 단축은 물론 유지보수성도 좋아지게 된다. 그리고 이런 유연성은 느슨한 결합(loosely coupling)의 철학을 기반으로 하고 있다.
기업 통합 패턴을 적용하여 메일러를 개발한다고 해서 개발의 범위가 결코 주는 것은 아니다. 다만 일관된 접근 방법에서 얻을 수 있는 개발 생산성과 유지보수성이 좋아지는 것이다.
3. 통합 프레임워크
기업 통합 패턴을 적용하여 메일러 애플리케이션을 개발한다고 하면, 우선 무엇을 해야 할까? 기업 통합 패턴은 기업 통합을 위한 일관된 분석, 설계, 개발 등의 접근 방법을 규정할 뿐 개발에 별도의 생산성을 제공하지는 않는다. 그러므로 개발의 생산성을 도모하려면 기업 통합 패턴을 손쉽게 사용하게 해주는 "통합 프레임워크(Integration Framework)"가 필요하다.
기업 통합 패턴이 출판된 후, 몇몇 통합 프레임워크들이 등장했다. 통합 프레임워크는 기업 통합 패턴에 기반한 프레임워크로, 처음에는 ESB(Enterprise Service Bus)나 메시지 큐가 기존 애플리케이션들과 연결할 때 부딪치는 문제들을 해결하기 위해 필요한 작은 컴포넌트 프로젝트로서 출발하였다. 그러다가 그 유용성이 점점 인식되면서 독립적인 프레임워크로 성장하였다.
통합 프레임워크는 기업 통합 패턴을 구현한 구현체이다. 통합 프레임워크가 아직 없었을 때, 기업 통합 패턴은 말 그대로 기업 통합을 위한 일관된 접근 방법을 뿐이므로, 애플리케이션을 개발할 때 이 기업 통합 패턴의 접근 방법들 하나하나를 개발하거나, 애플리케이션들의 통합을 위해 고가의 EAI(Enterprise Application Integration) 제품들을 구매하고 EAI가 제시하는 방법대로 애플리케이션들을 개발해야 했다. 그러나 통합 프레임워크가 등장하면서 개발의 수고를 많이 줄일 수 있게 되었고, 굳이 고가인 EAI를 도입하지 않을 수 있게 되었고, 더 나아가 EAI로서도 해결하기 어려웠던 기업 통합의 네 가지 패턴(파일 전송, 데이터베이스 공유, 원격 프로시저 호출, 메시징)들을 자유롭게 중재(Mediation)할 수 있게 되었다.
대표적인 통합 프레임워크로 Apache Camel이 있다. Camel은 기업 통합 패턴에 등장하는 패턴을 컴포넌트화 하고, 메시지 변환을 위한 형식 변환기(Type Converter)들을 제공하고, 컴포넌트와 관련 기술들을 연결하고 중재하는 도메인 특화 언어(DSL, Domain Specific Language)를 제공한다. 제공되는 통합 컴포넌트의 개수도 백여 개가 넘고 데이터 형식들을 자동 변환해 주는 형식 변환기(Type Converter)들도 다양하게 지원된다. Camel 프레임워크가 제공하는 컴포넌트와 형식 변환기, DSL를 이용하면 기업 통합 패턴들을 레고 블록처럼 조립할 수 있다. 다시 말해 Camel 프레임워크는 기업 통합 패턴의 개발을 조립으로 가능하게 함으로 기업 통합에 극적인 생산성을 제공한다.
그러나 Camel과 같은 통합 프레임워크는 양날의 검이 될 수 있다. 기업 통합 패턴은 애플리케이션의 아키텍처 패턴과 애플리케이션들 사이의 메시징 패턴에 대한 개념을 모두 포함하는 패턴이다. 그러다 보니 통합 프레임워크의 동작 메커니즘을 이해하는 데, 그동안 접근해 보지 못한 방식을 접하게 되고, 또 강력한 컴포넌트들을 조율하는 수많은 동작 옵션들은 컴포넌트의 적절한 활용을 어렵게 하기도 한다. 즉 통합 프레임워크에서 사용하는 메시지의 변환이나 컴포넌트의 동작 옵션에 따라 전혀 엉뚱한 또는 걷잡을 수 없는 부작용이나 연쇄 효과들도 볼 수 있게 된다. 예를 들면 기대한 메시지의 소실, 메시지의 폭주, 메시지의 중복 또는 메시지 순서의 뒤바뀜 등 메시징 시스템의 문제들과 우발적 성능 저하, 기대 밖의 메시지의 변환, 동기화 비동기 사이의 조율, 자원 해제의 시기와 절차 등 프레임워크나 컴포넌트, 메시지 처리의 관례(convention)들을 제대로 이해하지 못한다면 다양한 예기치 못한 문제들을 만날 수 있다.
그러나 예리한 검처럼 Camel을 잘 활용하는 경우, 통합 프레임워크는 극적인 생산성과 유지보수성을 제공해 준다. 필자가 바른모 주식회사 홈페이지의 Wiki에 올렸던 Apache Camel의 예인 "기상청 사이트 서울 날씨 주간 예보 조회"처럼, 심지어 100줄 이내에 모든 중요 처리를 수행하는 애플리케이션을 개발할 수 있을 정도로 놀라운 개발 생산성을 제공해 준다. 그러므로 메시징의 동기, 비동기 메커니즘, 통합 프레임워크의 구조 및 컴포넌트 구현 기술을 잘 이해하는 경우, 통합 프레임워크를 활용하는 개발자들은 이제까지 경험해 보지 못한 놀라운 개발 성과를 얻을 수 있을 것이다.
4. 통합 프레임워크를 이용한 메일러 개발
이제 다시 메일러 개발의 문제로 돌아와서 통합 프레임워크인 Apache Camel을 이용하여 얼마나 간단하게 메일러를 개발할 수 있는지를 보일 것이다. 우리는 메일러 솔루션을 개발하는 것이 아니므로, 이곳에서는 "채널 어댑터" 패턴의 간단한 메일러를 개발하려고 한다. 그리고 네 가지 통합 방법 중 공유 데이터베이스 패턴인 데이터베이스로 테이블부터 메일 전송 요청을 로드하여 SMTP 프로토콜로 메일 서버에게 메일의 전송을 요청하는 메일러를 개발할 것이다. 즉 메일러는 "채널 어댑터" 애플리케이션이면서 "데이터베이스 공유" 패턴을 이용한다. 이 과정을 통해 어떻게 기업 통합 패턴을 따르는 설계가 실제 실무에서 활용되는 지를 보일 것이고, 통합 프레임워크가 제공하는 개발 생산성도 보일 것이다. 또한 기업 통합 패턴이 어떤 이상만을 추구하는 패턴이 아니며, 기존 애플리케이션들과도 잘 융합하는 패턴임도 보일 것이다.
5. 요구
우리가 개발하는 메일러의 핵심 요구를 다음과 같이 나열해 보자
- 1) 메일러는 데이터베이스로부터 메일의 발신 요청들을 로드한다.
- 2) 메일러는 메일 서버에게 메일의 발신 요청을 전송한다.
- 3) 메일러는 데이터베이스 접속 정보와 메일 서버의 접속 정보를 설정으로 관리한다.
- 4) 메일러는 배치 형식의 애플리케이션이다.
이 요구들을 만족하는 메일러 애플리케이션을 그림으로 도식화 하면 아래와 같을 것이다.
위 요건은 기업의 실제적인 요구를 포함한다고 볼 수 있다. 실제로 기업에서는 위와 같이 데이터베이스에 저장된 메일 전송 요청을 특정 시간에 읽어 메일로 전송하는 배치 프로그램을 운영하는 경우가 많다.
6. 분석
메일러의 요구를 정의 했으므로, 기업 통합 패턴의 접근 방법에 따라, 메일러를 분석해 보자. 메일러는 별도의 애플리케이션으로 데이터베이스 서버와 메일 서버에 모두에 접근할 수 있어야 한다. 기업 통합 패턴의 관점에서 보면 이 메일러는 "채널 어댑터"로 볼 수 있다. 그리고 이 채널 어댑터는 내부에 데이터베이스 컴포넌트와 메일 서버 컴포넌트를 가진다. 그리고 컴포넌트들 사이의 메시지 변환도 필요하다.
이런 분석을 토대로 메일러를 좀더 상세화하면, 메일러 애플리케이션의 구조는 다음과 같을 것이다.
7. 시스템 구성
메일러는 자바 애플리케이션으로 개발되며 Java 7을 사용한다. 그리고 메일러와 통합되는 데이터베이스 서버는 MySQL 서버를 사용하고 메일 서버는 Postfix 서버를 사용한다. 사용되는 시스템들을 정리하면 다음과 같다.
- 1) 자바 가상 머신(JVM) 버전: 7
- 2) 데이터베이스 서버: MySQL 서버
- 3) 메일 서버: Postfix 서버
8. 개발 도구와 프로그램 소스
메일러를 개발하는 통합 프레임워크로는 Apache Camel 프레임워크를 사용한다. 데이터베이스를 액세스하는 ORM 프레임워크는 MyBatis 프레임워크를 사용하고, 사용 라이브러리들을 쉽게 포함시키기 위해 Maven을 사용하고, 통합 개발 환경으로 Eclipse, 그리고 프로그램 소스는 GitHub를 이용한다.
- 1) 통합 프레임워크: Apache Camel
- 2) ORM 프레임워크: MyBatis
- 3) 통합 개발 환경: Eclipse
- 4) 라이브러리 관리: Maven
- 5) 프로그램 소스: GitHub
9. Camel 컴포넌트
메일러 애플리케이션은 데이터베이스 컴포넌트와 메일 서버를 액세스하는 컴포넌트들이 필요하다. 이 통합 컴포넌트들을 처음부터 개발한다면 이것만으로도 하나의 프로젝트가 될 수 있었을 것이다. 그러나 우리에게는 통합 프레임워크인 Apace Camel이 있다. Apache Camel은 이미 이 두 기술에 대한 컴포넌트들을 제공한다. 메일러는 ORM 프레임워크로 MyBatis 프레임워크를 이용하므로, Camel의 "MyBatis 컴포넌트"를 사용한다. Camel MyBatis 컴포넌트는 MyBatis 프레임워크를 이용하게 해주는 컴포넌트이다. 그리고 메일 전송 컴포넌트로는 Camel"메일 컴포넌트"를 이용한다. 각 컴포넌트에 대한 설명은 다음 URL에서 확인할 수 있다.
- Camel MyBatis 컴포넌트: http://camel.apache.org/mybatis.html
- Camel 메일 컴포넌트: http://camel.apache.org/mail.html
10. 메시지 흐름
메일러의 데이터베이스 컴포넌트는 데이터베이스로부터 메일의 전송 요청을 로드한다. 발신자는 발신하고자 하는 메일들을 데이터베이스 테이블에 축적하고, 메일러는 메일 전송 요청 레코드들을 읽는다. 즉 데이터베이스로부터 복수 개의 메일 요청 레코드들이 한번에 읽는다. 그런데 메일러는 메일 전송 레코드를 하나씩 메일 서버로 전송해야 하므로, 메일러는 읽은 메일 요청들을 개별 메일 요청으로 분할하는 메시지 분할기(Message Splitter)가 필요하다. Apache Camel을 이용하면 메시지 라우팅을 다양한 기술로 기술할 수 있다. 우리는 간단한 메일러 애플리케이션을 개발하고 있으므로 그 중에서 Camel의 Java DSL(Domain Specific Language, 도메인 특화 언어)를 사용하여 이 분할기를 포함하는 메시지 라우팅을 기술할 것이다. 우리의 분할기는 한 덩어리의 메일 발신 요청 레코드들을 레코드 개수만큼 분할할 것이다. 그리고 나서 분할기는 분할된 각 레코드들은 메일 컴포넌트로 전송할 것이다. 그런데 Camel의 메일 컴포넌트는 메시지의 헤더에 발신과 수신에 관련된 정보를, 메시지 본문에 메일 본문을 요구한다. 그러므로 분할된 메일 발신 요청 레코드는 다시 메일 컴포넌트가 요구하는 형식의 메시지로 변환돼야 한다. 이 변환에는 Bean 객체를 사용한다. 이 변환을 수행하는 Bean 객체도 일반 컴포넌트처럼 Camel의 DSL을 통해 호출한다. 그리고 발신한 메일은 데이터베이스에 해당 메일 레코드의 전송 상태를 전송 완료 상태로 갱신해야 한다. 이를 위해 위해 필요한 정보는 메일 전송 요청 레코드의 MailID 필드 정보인데, 이 정보는 데이터베이스로부터 읽은 메일 전송 요청 레코드에 포함되어 있으므로, 테이블에서 메일 발신 레코드 정보를 읽을 때, Camel의 exchange 메시지의 속성에 MailID를 저장하여 MyBatis 컴포넌트가 메일 전송상태를 갱신하는 데 이용하게 한다. 메시지 분할기와 메시지 변환기의 Camel 설명은 다음 사이트를 참조한다.
- Camel 분할기 EIP 패턴: http://camel.apache.org/splitter.html
- Camel 메시지 변환기 패턴: http://camel.apache.org/message-translator.html
기업 통합 패턴은 각 패턴마다 고유한 패턴 다이어그램을 제공하므로, 지금까지의 설계를 EIP 다이어그램(Enterprise Integration Patterns Diagram)으로 표현하면 다음과 같다. 아래 그림에는 메시지 엔드포인트, 메시지 분할기, 메시지 변환기 패턴의 다이어그램들이 보인다.
11. 설정 관리
Apache Camel 프레임워크는 설정(Configuration)에 사용할 속성들(properties)을 관리하는 컴포넌트인 Camel Properties 컴포넌트를 제공한다. 메일러 애플리케이션은 이 속성 컴포넌트를 이용하여 메일 전송 서버의 접속 정보를 mailer.properties 속성 파일로 관리한다. 또 이 속성 파일에는 데이터베이스 접속 정보도 포함되는데, 이 정보를 MyBatis가 읽을 수 있도록 MyBatis의 설정 파일인 SqlMapConfig.xml의 properties 엘리먼트의 resources 애트리뷰트에도 mailer.properties 속성 파일을 지정한다.
메일러 애플리케이션의 설정들과 관련된 위치 관례들은 다음과 같이 정한다.
- 1) mailer.properties 속성 파일은 classpath에서 찾는다.
- 2) MyBatis 설정 파일은 classpath:camel/example/mailer/data/SqlMapConfig.xml 에서 찾는다.
이제 설정과 관련된 파일들의 내부를 살펴보자. 우선 mailer.properties 속성 파일은 다음과 같다. 이 속성 파일에는 메일 서버의 접속 정보와 데이터베이스 접속 정보가 포함된다.
MyBatis의 설정 파일인 SqlMapConfig.xml 파일은 다음과 같다. 이 설정 파일에는 mailer.properties 속성 파일과 매퍼 파일의 설정이 포함된다. 이곳에서 속성 파일의 정보는 ${}로 참조한다.
MailerMapper.xml 매퍼 파일은 전형적인 MyBatis 매퍼 파일이다.
위 소스를 보면 "limit 1000" 질의 구문을 통해 한번에 최대 1,000개의 레코드를 읽게 선택 질의를 구성했음을 볼 수 있다. 이 구분은 대량 전송 테스트를 위해 메일 발신 요청 레코드의 읽는 수를 제한하기 위해 사용되었다.
log4j.properties 설정 파일은 별도로 설명하지 않는다. 프로그램 소스를 참조한다.
12. Configurer
메일러 애플리케이션은 설정자(Configurer) 패턴을 이용하여 Camel의 컴포넌트들을 등록한다. 설정자 패턴은 필자가 정의한 패턴으로 설정을 기본 로직에서 분리하여 별도로 관리하게 해주는 패턴이다. 설정자 구현 클래스에서 property 컴포넌트, MyBatis 컴포넌트, 메일 컴포넌트를 등록한다. 이 세 컴포넌트는 클래스의 설정자(setter)를 통해서도 입력 받을 수 있게 했다. 이 설정자(setter)들은 Spring 프레임워크를 통해 설정자(Configurer)가 Bean으로 활용될 때, 각 컴포넌트를 주입할 수 있게 추가한 것이다 다음은 설정자(Configurer) 인터페이스를 구현한 MailerConfigurer.java의 소스이다.
13. 메시지 라우팅
기업 통합 패턴에서는 컴포넌트와 메시지 라우팅을 이용하여 애플리케이션의 로직을 구현한다. 통합 프레임워크인 Camel은 도메인 특화 언어(DSL, Domain Specific Language)로 메시지 라우팅을 정의할 수 있게 한다. 메일러 애플리케이션은 자바 소스 형태의 DSL을 이용하여 메시지 라우팅을 정의한다. 다음은 메일러의 메시지 라우팅을 정의한 SimpleMailerBuilder.java 이다.
위 메시지 라우팅 설계를 EIP 다이어그램으로 그리면 다음과 같다.
Camel의 메시지 라우팅 설계는 순수한 기업 통합 패턴 메시지 라우팅 설계와 조금 다른데, Camel 프레임워크에서는 메시지 라우팅의 시작을 위한 동기 소비자인 direct 컴포넌트와 데이터베이스에서 읽은 레코드가 있는 경우만 후속 라우팅을 진행하는 메시지 필터가 추가되었다. 이와 같이 통합 프레임워크를 이용한 기업 통합 패턴의 설계는 순수한 기업 통합 패턴의 메시지 라우팅의 뼈대에 통합 프레임워크의 특성이 추가되는 방식으로 설계가 이루어진다.
14. 메시지 변환기
메시지 변환기는 출발 기술의 데이터 형식을 목적 기술의 데이터 형식으로 변환해 주는 컴포넌트이다. 이 글의 메일러도 데이터베이스로부터 읽은 레코드를 메일 컴포넌트가 해석할 수 있는 메시지로 변환해 주는 메시지 변환기가 필요하다. 이 변환을 수행하는 ToMailTranslator.java는 다음과 같다.
위 소스는 입력 메시지를 메일 컴포넌트가 해석할 수 있는 메시지 포맷으로 변환한다. 메일 컴포넌트는 메일 전송에 필요한 정보를 메시지 헤더에서 참조하고, 메일 본문은 메시지 본문에서 참조한다. 위 소스에서 발신 결과를 다시 테이블에 기록하기 위해 필요한 MailID를 exchange 메시지의 속성에 저장하고 있는 점에 주목하자. 위 소스는 Camel의 Processor 인터페이스를 구현한다. Camel의 Processor 인터페이스는 Camel이 내부에서 사용하는 exchange 메시지를 애플리케이션이 참조할 수 있게 해주는 process 메소드를 정의한다. 일반적으로 Camel을 이용하는 애플리케이션들은 이 메소드를 이용하여 메시지 변환기를 구현하거나, 필요한 컴포넌트 로직을 Camel의 메시지 라우팅의 중간에 삽입한다. 기업 통합 패턴의 관점에서 Processor 인터페이스의 구현체들은 "파이프 필터", "메시지 필터", "메시지 변환기", "내용 보탬이" 등의 역할을 한다.
메일러 애플리케이션은 메일을 발신에 성공한 후 발신 상태를 MyBatis 컴포넌트를 사용하여 갱신한다. 이때 MyBatis 컴포넌트는 데이터베이스의 테이블을 갱신하기 위해 메시지의 본문으로 입력 파라미터를 요구한다. 그런데 메일 컴포넌트가 이미 메시지 본문을 메일의 본문 용도로 사용했으므로, 메일을 발신한 후에도 입력 메시지의 본문은 여전히 메일 본문이다. 그러므로 MyBatis 컴포넌트가 입력 메시지의 본문을 입력 파라미터로 해석할 수 있게 메시지 변환기를 통해 입력 메시지의 본문을 변환해 주어야 한다. 또 MyBatis 컴포넌트가 참조하는 MailerMapper.xml 매퍼의 질의들은 입력 파라미터로 형식 정의에 따라 MyBatis 컴포넌트의 입력 메시지 본문의 형식은 맵 객체이어야 한다. 그러므로 맵 객체를 생성하고, exchange 메시지 속성에 보관해 놓았던 MailID를 이 맵 객체에 저장한 후, 이 맵 객체를 입력 메시지의 본문으로 지정하는 메시지 변환기가 필요하다. 다음은 이 변환을 수행하는 ToMapTranslator.java의 소스이다.
위 소스도 마찬가지로 Camel의 Processor 인터페이스를 구현한다.
15. 메일러
지금까지 메일러 애플리케이션을 위해, 설정자(Configurer), 메시지 라우팅, 메시지 변환기들을 구현했다. 이제 컴포넌트들을 사용하게 해주는 Camel 컨텍스트와 이 컨텍스트를 기동하는 로직이 필요하다. 다음은 이 과정을 수행하는 메일러 클래스인 Mailer.java이다.
위 소스는 상당히 간단해 보인다. 위 소스의 run 메소드는 Camel 컨텍스트 생성하고, 메일러 설정자(Configurer)를 호출하고, 메시지 라우팅을 등록하고, Camel 컨텍스트를 시작하고, direct:start 엔드포인트를 이용하여 메일러의 메시지 라우팅을 기동하고, Camel 컨텍스트를 닫는다. Camel Producer Template의 requestBody 메소드는 동기 호출을 수행한다. 즉 requestBody 메소드를 호출하면 direct:start 엔드포인트로 시작하는 라우팅이 완료될 때까지 호출 측은 메소드의 반환을 기다리게 된다. 참고로 Camel Producer Template의 send로 시작하는 메소드들은 비동기 호출용 메소드들이다. 즉 send로 시작하는 메소드를 호출하면 메시지 라우팅의 완료와 상관없이 메소드의 반환과 동시에 실행 흐름은 계속된다. 이 메일러 애플리케이션은, 요구에 따라 배치 스타일로 동작되게, 의도적으로 동기 메소드를 호출하여 메시지 라우팅이 시작되고 완료될 때까지 메인 스레드의 실행 흐름이 중지시켰다. Mailer 클래스도 Spring 프레임워크에서 Bean으로 사용될 경우를 고려하여, Configuerer와 RouteBuilder를 주입할 수 있는 설정자(setter) 메소드를 정의했다.
16. 실행
이제 구현을 완료했으므로, 메일러 애플리케이션을 실행할 수 있다. 메일러 애플리케이션이 실행되기 위해 MySQL 서버에 mailer 계정, EMAIL 테이블, 테스트 레코드들을 준비해야 한다. 테이블 스키마 생성과 레코드 입력은 프로그램 소스의 EMAIL.sql을 참조한다. 그리고 Postfix 서버도 준비해야 한다. 참고로 Postfix 서버를 설치하지 않더라도 메시지 라우팅을 정의하는 자바 소스에서 Camel의 smtp 엔드포인트의 URL을 수신자의 이메일 URL로 지정하면 수신자에게 직접 메일을 전송할 수도 있다. 메일 컴포넌트는 메일을 대상 메일 서버로 직접 전송할 수도 있기 때문이다. 참고로 Java 7 부터는 듀얼 소켓 드라이버를 사용하는 경우 IPv6 스택이 우선 선택된다. 메일러 애플리케이션은 아직 IPv4로 설정되었으므로, 메일러 애플리케이션을 실행할 때 JVM 명령행 옵션으로 -Djava.net.preferIPv4Stack=true 를 입력하여 JVM의 실행 시스템 프로퍼티를 IPv4 우선으로 지정해야 한다. (Java 5 버전부터도 그렇다고 하는데, 테스트 결과 Java 6까지는 IPv4가 우선 선택되었다. 즉 Java 6까지는 -Djava.net.preferIPv4Stack=true를 사용하지 않아도 IPv4가 우선 선택되었다.)
17. 실행 결과
우리의 메일러 애플리케이션은 잘 동작했다. 테스트로 1024 bytes 크기의 메일을 1,000개 요청하게 했다.
위 결과를 보면 1,000개의 메일을 전송하는 데 약 164초 정도 걸렸음을 알 수 있다. 그러므로 이 메일러는 메일 서버는 초당 약 6건 정도 메일을 전송했다. 이번엔 다음 결과를 보자. 다음 결과는 여기에 소개한 단순 메일러 애플리케이션을 병렬 처리 메일러 애플리케이션으로 수정하여 실행한 결과이다.
위 결과로 1,000개의 메일을 전송하는 데 약 33초 정도 걸렸음을 알 수 있다. 단순 메일러보다 병렬 처리 메일러가 약 5배 정도 빨리 전송되었다. 즉 초당 약 30건의 메일을 전송했다. 이 차이는 순차 처리(sequential processing)와 병렬 처리(parallel processing)의 차이에서 비롯된다. 메일러의 경우 Camel에서 순차처리를 병렬 처리로 바꾸는 일이 어렵지 않다. 그러나 어떻게 순차 처리를 병렬 처리로 바꾸었는지는 설명하지 않는다. 조금 귀뜀해 준다면 단지 프로그램 소스에 메소드 호출을 하나 더 추가한 것뿐이다. 이렇게 Camel을 잘 활용한다면 평범한 속도의 애플리케이션을 순식간에 대용량 애플리케이션으로 전환할 수 있게 해 줄 수도 있다. 그러나 세상에 공짜 점심은 없고, 현실은 마법 세상이 아니다. 이런 기술을 적용하기 위해 각각의 기술에 대한 순차 및 병렬 처리에 대한 충분한 이해, 메시지 흐름, 응답 시간, 처리량 등에 대한 충분한 기본기가 없다면 Camel로 개발하는 프로그램은 양날의 검이 되어 도리어 성능 저하, 불안정, 메시지 소실, 알 수 없는 동작 등 다양한 양상의 버그들이 등장할 수 있다. 명검을 사용하려면 먼저 명검을 다룰만한 고수가 되어야 한다.
참고로 다음은 필자가 개발한 또 다른 Camel 기반의 메일러의 결과이다.
#
|
항목
|
결과
|
설명
|
1 | 메일 크기 |
1024 bytes
|
각 메일의 본문 크기 |
2 | 요청 메일 건수 |
10,000
|
요청 메일 건수 |
3 | 평균 전송 시간 |
약 330초
|
메일러가 1만건의 메일을 요청하는 데 걸린 평균 시간 |
4 | 초당 전송 건 수 |
약 120건
|
|
5 | 시간당 전송 건수 |
약 40만건
|
한 시간에 약 40만건의 이메일을 전송할 수 있다. |
6 | 백만 건 전송 시간 |
약 2시간 30분
|
백만 건의 이메일을 약 두 시간 반만에 처리할 수 있다. |
18. 맺음말
일반적으로 기업 환경에 애플리케이션을 개발하거나 도입할 때, 애플리케이션들 사이에 단단한 결합(tight coupling)의 아키텍처를 구성하는 경우가 종종 있다. 여기서 단단한 결합이란 참여한 애플리케이션이나 시스템들 사이에 가정을 많이 포함하는 아키텍처를 말한다. 그 결과 애플리케이션은 독자적으로 유지보수하기 어려워지고 관련된 애플리케이션들이 미치는 영향들까지 모두 고려해야 하는 상황에 이르게 된다.
여기에 소개한 메일러 애플리케이션도 단순하게 개발만 고려하거나 도입만 고려한다면, 일반적으로 단단히 결합된 특화된 용도의 애플리케이션이 되기 쉬워진다. 그러므로 메일러 애플리케이션을 새로 도입하는 경우 "정규 데이터 모델"의 관점에서 도입할 수 있도록 노력해야 애플리케이션들 사이의 의존성과 데이터 변환 등, 단단히 결합된 구조를 탈피하고 느슨한 결합 구조를 갖게 되어 향후 발생할 수 있는 운영과 유지보수에 비용을 절감할 수 있게 된다. 그러나 이미 도입된 애플리케이션들의 변경이 어려운 경우, 기존 애플리케이션들의 아키텍처 구조를 최대한 보장하고 새로운 시스템 때문에 발생하는 침입적 상황을 제거하는 것도 기업 통합에서 중요하다.
이 글에서는 기업 통합의 초기 단계에 손쉽게 접근할 수 있는 "공유 데이터베이스" 패턴에 기반한 메일러 애플리케이션을 개발하는 과정을 보여 주었다. 통합 프레임워크인 Apache Camel 프레임워크를 이용하여 일반적인 애플리케이션 개발에 필요한 코딩 노력보다 상당히 적은 코딩 노력으로 메일러 애플리케이션을 구현할 수 있음을 보여 주었다. 아마도 어떤 프레임워크나 라이브러리보다 적은 량의 코딩으로 애플리케이션을 개발했을 것이다.
이렇게 통합 프레임워크는 기업 통합을 위한 애플리케이션 개발에 놀라운 생산성을 달성하게 해줄 수 있다. 그러나 이것이 끝이 아니다. 기업 통합 패턴의 장점 및 통합 프레임워크의 장점은 이렇게 개발된 애플리케이션의 확장, 수정, 유지보수 등에 따르는 비용도 놀랍도록 줄여 줄 수 있다는 것이다. 예를 들어 일반적인 메일러 애플리케이션의 경우, 메일 요청 레코드가 추가로 파일로부터도 제공돼야 한다고 요구 조건이 확장되면, 개발된 소스에 파일 처리, 레코드 추출, 데이터 변환, 메일 전송 등 전반적인 소스의 수정을 필요로 할 것이다. 그러나 기업 통합 패턴 기반 즉 통합 프레임워크 기반 애플리케이션은 이 추가된 요구에 대해 이미 준비된 파일 컴포넌트, 분할기와 메시지 라우팅의 수정 등으로 개발에서 보았던 생산성을 수정된 요구의 확장에서도 제공한다.
그러나 통합 프레임워크를 잘 사용하기 위해서는 컴포넌트에 활용된 기술이나 프레임워크를 잘 이해하는 것이 무엇보다도 중요하다. 그리고 기업 통합 패턴도 잘 이해하고 있어야 하고, 통합 프레임워크가 제공하는 편의 기능들도 잘 이해하고 있어야 한다. 이런 기본기가 없이 함부로 통합을 시도하면 결과적으로 기존 기업 애플리케이션들의 통합에 상존하는 문제들이 해결되지 않을 뿐만 아니라 새로운 양상의 문제들이 등장할 수도 있다.
기업 통합(EI, Enterprise Integration) 패턴은 그동안 우리가 시스템 통합(SI, System Integration)을 중심으로 개발하던 관행을 시스템들 사이의 통합에 대해서도 고려하게 해주고, 어떻게 시스템 통합이 개발돼야 다른 시스템들과도 잘 통합될 수 있는지에 대한 방법론을 패턴 언어로 제시한다.
코드로는 수백 줄에 불과한 메일러 애플리케이션을 개발하는 과정을 보이면서, 기업 통합에 많은 패턴들을 고려해야 했다. 즉 결과는 보잘것없을 수도 있지만 그 속에 수많은 생각들을 담고 있는 것이다. 애플리케이션들 사이의 느슨한 결합, 개발 생산성, 확장성, 유지보수성 등. 이런 것들이 고려되지 않고 우선 당장 언 발에 오줌 누기 식으로 급히 개발할 수도 있지만 그럴 경우 안정성, 속도, 유지보수성의 문제로 인해 지속되는 추가 비용을 감당해야 할 것이다. 소프트웨어는 코드의 양이 아닌 알고리즘, 아키텍처, 패턴들을 어떻게 활용하느냐가 결과적으로 성능, 생산성, 유지보수성을 높인다는 것을 우리는 너무 쉽게 잊는다.
"기업 통합 패턴"과 "Apache Camel" 프레임워크는 기업 통합 아키텍처나 생산적인 애플리케이션 개발에 관심이 있는 개발자나 아키텍트들이라면 알아야 할 패턴이고 프레임워크이다. 그리고 기업 통합 패턴은 곧 필자가 번역한 번역서로 출판될 것이므로 영문을 읽어야 하는 불편함도 해소될 것이다.
참고 사이트
댓글 없음:
댓글 쓰기