2013년 10월 10일 목요일

Apache Camel, Hello, world!


1. 들어가며

Apache Camel은 기업 통합에 없어서는 안될 중요한 통합 프레임워크이다. Camel 프레임워크는 일반적인 애플리케이션에 내장 가능한 경량 프레임워크로, 프레임워크 내부에 라우터 엔진, 프로세서, 컴포넌트, 메시징 시스템을 포함하여, 애플리케이션의 내부를 외부 세계와 손쉽게 인터페이스할 수 있게 해준다. 즉 Camel 프레임워크는 애플리케이션, 시스템, 서비스들 사이에서 데이터(Data)와 기능(Function)을 통합(인터페이스)하는 중재자(Mediator)로서 역할한다. 이 글은 Camel 프레임워크가 어떻게 애플리케이션의 통합에 기여하는지를 간단한 "Hello, world!" 애플리케이션의 통합 과정을 통해 보여줄 것이다.

일반적으로 애플리케이션은 외부 세계와 인터페이스하기 위해 다양한 기술을 필요로 한다. 예를 들어 파일을 복사하기 위해서는 Java File Stream API를 사용해야 하고, 데이터베이스를 이용하기 위해서는 JDBC 드라이버를 사용해야 하고, 웹 서비스에 접속하기 위해서는 Apache HttpClient 라이브러리를 사용해야 하고, 이메일을 발신하기 위해서는 JavaMail API를 사용해야 한다. 게다가 새로운 Twitter 서비스를 이용하려 한다면 OAuth에 기반한 Twitter 서비스를 이용해야 한다. 즉 외부 애플리케이션이나, 서비스, 시스템들과 인터페이스 하려는 애플리케이션은 각 인터페이스에 맞는 기술을 애플리케이션 안에 모두 포함해야 한다. 따라서 애플리케이션을 개발하는 개발자가 외부와 인터페이스하는 각각의 기술에 대한 사용하는 방법을 알아야 한다. 그런데 일반적으로 애플리케이션 개발자는 비즈니스 로직 개발자들이다. 그러므로 외부 세계와 인터페이스에 많은 어려움을 호소하곤 한다. 실제로 인터페이스가 연결되지 않아 비즈니스 로직을 개발이 지연되는 경우가 상당히 많이 발생한다. 다음은 애플리케이션이 다양한 외부 시스템들과 인터페이스하는 방식을 그림으로 표현한 것이다.

그러나 애플리케이션이 Camel을 이용하는 경우, 애플리케이션은 Camel을 통해 외부 세계와 인터페이스할 수 있게 된다. 이 경우 Camel이 애플리케이션을 대신해 외부 세계와 인터페이스하게 된다. 이런 구조를 갖게 되면 애플리케이션은 Camel의 인터페이스 기술만으로, 어떤 외부 세계와도 인터페이스 할 수 있게 된다. 즉, 비즈니스 애플리케이션 개발자는 Camel 개발자하고만 의사소통하고, Camel 개발자는 인터페이스 하려는 외부 시스템의 개발자와 소통한다. 이 경우 비즈니스를 개발하는 애플리케이션 개발자의 외부 인터페이스에 대한 개발 부담은 현저하게 줄게 될 것이다. 물론 그 부담을 Camel 개발자가 떠안게 되지만, Camel은 외부 인터페이스 연동을 위해 이미 수백 가지 컴포넌트를 제공하고 있으므로, Camel 개발자는 외부와의 인터페이스에 새로운 프로그램을 작성하기 보다 Camel이 제공하는 컴포넌트를 활용할 수 있다. 이런 개발 과정이 얼마나 극적인 효과를 주는 지 곧 보게 될 것이다. 다음은 Camel을 사용한 애플리케이션의 인터페이스 방식을 그림으로 표현한 것이다.


2. Hello, world to Console

이제 Camel을 사용하는 간단한 애플리케이션을 작성해 보자. "The C Programming Language"에 처음 등장하는 "Hello, World!"를 콘솔에 출력하는 프로그램을 평범한 POJO 형식의 Java 프로그램과 Camel을 이용한 POJO 형식의 Java 프로그램으로 작성해 보자. 먼저 콘솔에 "Hello, World!"를 출력하는 Java 프로그램은 다음과 같다.

위 소스는 Java 개발자라면 모두 이해할 수 있는 간단한 프로그램이다. 이제 위 Java 프로그램과 동일한 결과를 출력하는 프로그램을 Camel를 이용해 작성하면 다음과 같다.

위 프로그램 소스를 살펴보자. 위 소스에서는 기본 Java 프로그램에서는 없었던, Spring Bean 정의 XML 파일인 ToConsole.xml을 Camel Main 객체에 지정한다. (Spring Bean 정의 XML을 사용한 이유는 느슨한 결합(loose coupling)이 가능하도록 Camel을 설정하기 위해서이다. 결합도(coupling)를 고려하지 않는다면 Java 프로그램 소스안이 이 XML을 프로그램적으로도 삽입 할 수 있다.) 프로그램의 main 메소드는 Camel Main 객체를 이용하여 Camel 컨텍스트를 시작한 후, Main 객체로부터 Camel과 통신할 수 있는 생산자(발신자) 객체인 ProducerTemplate를 얻어, "direct:start" URL과 "Hello World!" 문자열을 파라미터로 ProducerTemplate 객체의 requestBody 메소드를 호출한다. (생산자(producer) 또는 발신자(sender)는 기업 통합 패턴에서 사용하는 용어로 메시지를 발신하는 개체를 말한다.) 그리고 main 객체를 종료한다. Camel Context의 초기화와 종료를 제거하고 보면 처음의 Java 프로그램에 비해 그렇게 복잡하지 않다. 그렇다면 ToConsole.xml 파일이 혹시 복잡한 것은 아닐까? ToConsole.xml을 살펴 보자.

위 XML 설정을 보면 Camel Context를 정의하고 그 안에 라우팅 로직을 하나 지정했다. 이 설정은 "direct:start" 엔드포인트에서 출발하여 "stream:out" 엔드포인트로 도착하는 라우팅을 지정한다. ToConsole.xml을 기업 통합 패턴(EIP) 다이어그램으로 보면 다음과 같다.

여기에서 "direct:start" 엔드포인트는 Camel이 애플리케이션으로부터 동기 호출을 수신하는 출발 지점이다. "stream:out" 엔드포인트는 Camel이 표준 콘솔로 메시지를 발신하는 지점이다. 즉 위 라우팅은 Camel의 "direct:start" 엔드포인트로 수신된 메시지를 "stream:out" 엔드포인트로 전달하라는 정의를 담고 있다.

프로그램을 컴파일하고 실행하기 위해서는 다음의 Maven 의존이 필요하다.

하나는 Camel을 Spring 프레임워크와 함께 사용하기 위해 필요한 의존이고, 하나는 Camel의 Stream 컴포넌트 라이브러리에 필요한 의존이다.

Eclipse 환경에서 프로그램을 컴파일하고 실행할 수 있도록 프로그램 소스를 Eclipse 프로젝트로 GitHub에 올려 놓았다. Maven을 설치했다면, Eclipse 환경에서 프로그램 소스를 열지 않고도, 다음과 같이 명령창에서 Maven(mvn)을 이용하여 컴파일과 실행이 가능하다.


3. Hello, world to Log

지금까지 작성한 Console 출력 애플리케이션을 log4j를 통해 로그로 기록하는 프로그램으로 수정해 보자. 수정된 프로그램 소스는 다음과 같다.

위 소스에서 Console로 출력하는 프로그램과 달라진 점은 Spring Bean 정의 XML 파일이 ToConsole.xml에서 ToLog.xml로 달라진 것 밖에 없다. Spring Bean 정의 파일을 새로 지정한 이유는 단지 이곳의 예를 위해서 필요했기 때문이다. 실제 프로그램에서는 애플리케이션 소스를 수정하지 않고 Spring Bean 정의 XML 파일의 라우팅 정의를 수정함으로 애플리케이션의 출력을 즉시 Console에서 Log로 변경할 수 있다. ToLog.xml을 살펴 보자.

라우팅 정의에서 출발지는 "start:direct"로 ToConsole.xml에서와 같고 도착지의 "log:ToLog?level=WARN"로 ToConsole.xml의 "stream:out"와 다르다. Camel에서는 라우팅의 엔드포인트(도착지, 출발지)에 따라 인터페이스하는 외부 세계가 달라진다. 즉 ToLog.xml의 외부 세계는 이제 로그 라이브러리가 된 것이다. 상단의 logFormatter Bean 객체의 정의는 로그 기록 포맷을 지정하는 포맷터로 로그 기록 포맷을 자유롭게 커스터마이징할 수 있게 해 준다. 이곳에서 사용한 HelloFormatter는 메시지 본문의 문자열을 로그로 기록한다. HelloFormatter 소스는 프로그램 소스가 올라가 있는 GitHub를 참조한다. ToLog.xml의 EIP 다이어그램은 다음과 같다.

프로그램을 컴파일하고 실행하기 위해서는 다음의 Maven 의존이 필요하다.

Log 컴포넌트는 Camel Core 라이브러리에 포함되어 있으므로 stream 컴포넌트처럼 별도의 라이브러리가 필요하지 않다. Eclipse에서 프로젝트를 열지 않는 경우, 다음과 같이 명령창에서 Maven(mvn)을 이용하여 컴파일과 실행이 가능하다.


4. Hello, world to Mail

지금까지는 별로 특이한 사항은 없다. 이제 좀더 특별한 애플리케이션을 만들어 보자. 애플리케이션에서 메일을 발신하려고 한다. 애플리케이션은 어떻게 수정돼야 할까? 메일을 발신하는 ToMail.java는 다음과 같다.

위 소스도 ToCosole이나 ToLog 애플리케이션과 동일하다. 단지 Spring Bean 정의 XML 파일이 ToMail.xml로 달라진 것 밖에 없다. 즉 애플리케이션은 "Hello, world!"를 메일로 발신하기 위해서도 수정되지 않는다. ToMail.xml을 살펴 보자.

라우팅 정의에서 "start:direct" 엔드포인트는 이전 프로그램들 설정들과 동일하고 도착지 엔드포인트가 메일 엔드포인트인 "smtp:barunmo.com?username=testuser&password=testpassword"로 바뀌었다. 메일을 발신하기 위해서는 SMTP 서버와 메일 주제, 발신자, 수신자 등이 추가적으로 필요한데, 이런 정보들은 XML에 설정으로 지정했다. ToMail.xml의 EIP 다이어그램은 다음과 같다.

이전 라우팅 정의와 달리 중간에 setHeader 태그로 지정된 부분은 메시지 변환기(Message Translator)로 표시되었다. 이 메시지 변환기는 EIP 패턴 다이어그램 중 하나이다. 더 많은 패턴 다이어그램이 기업 통합 패턴(Enterprise Integration Patterns)에 정리되어 있다.

프로그램을 컴파일하고 실행하기 위해서는 다음의 Maven 의존이 필요하다.

Camel Mail 컴포넌트는 내부적으로 JavaMail API를 이용한다. Camel Mail 컴포넌트의 의존이 필요한 관련 라이브러리를 자동으로 추가해 줌으로 애플리케이션에서는 별도로 추가할 의존은 없다. Eclipse에서 프로젝트를 열지 않는 경우, 다음과 같이 명령창에서 Maven을 이용하여 컴파일 및 실행이 가능하다. 단 GitHub의 소스에서 내려 받은 소스에는 메일 정보들이 가상의 값으로 채워져 있으므로, 이 예를 실행 전에 메일 수신자와 발신자 정보 그리고 smtp 엔드포인트의 SMTP 서버 정보, 사용자, 패스워드를 테스트 하는 시스템과 개발자의 정보로 수정해야 한다.

다음은 ToMail 프로그램을 실행하여 필자가 수신한 메일이다.

ToMail 애플리케이션도 Spring XML 설정을 가리키는 부분 이외의 프로그램의 수정이 없으면서, 메일을 발신하는 기능을 갖게 되었다. 다음은 마지막으로 Twitter와 인터페이스하는 애플리케이션을 작성해 보자.


5. Hello, world to Twitter

요즈음 애플리케이션들은 소셜 네트워크와 뗄래야 뗄 수 없는 환경에 있다. 그러므로 이제 우리의 애플리케이션도 트위터로 트읫을 전달하게 만들어 보자.

트위터 애플리케이션을 만들기 위해서는 https://dev.twitter.com/apps/new 사이트에서 애플리케이션을 등록하고 OAuth 등의 정보를 획득해야 한다. 필자도 이 사이트에 접속하여 Barunmosoft 계정으로 접속하는 애플리케이션을 등록했다. 이 애플리케이션을 테스트하기 위해서는 독자들도 이 사이트에 접속하여 트위터 애플리케이션을 등록해야 한다. 다음은 Hello, world를 트윗하기 위한 ToTwitter.java의 소스이다.

위 소스도 ToCosole이나 ToLog나 ToMail 애플리케이션과 마찬가지로 소스상에 변화는 없다. 단지 Spring Bean 정의 XML 파일이 ToTwitter.xml로 달라진 것 밖에 없다. 즉 애플리케이션은 트윗을 사용하기 위해서도 수정이 필요 없다. ToTwitter.xml을 살펴 보자.

라우팅 정의는 이전 프로그램들처럼 도착지 엔드포인트가 트위터 엔드포인트인 "twitter://timeline/user"로 바뀌었다. 트윗하기 위해서는 트위터에 애플리케이션을 등록해야 하는데, 이 등록 과정을 진행하고 나면 트위터 사이트는 consumerKey, consumerSecret, accessToken, accessTokenSecret 값을 생성해 준다. 이 생성된 값을 twitter 컴포넌트의 Bean 정의에 속성으로 지정한다. 위 설정에서는 트위터 관련 토큰들이 엔드포인트 URI에 키-값으로 지정됨으로 엔드포인트 URL이 길어지는 것을 방지하기 위해, twitter 컴포넌트의 팩토리 Bean에 해당 값들을 속성으로 지정했다. 위 라우팅 정의의 EIP 다이어그램은 다음과 같다.

프로그램을 컴파일하고 실행하기 위해서는 다음의 Maven 의존이 필요하다.

Camel Twitter 컴포넌트는 내부에서 Twitter4J 를 이용한다. Camel Twitter 컴포넌트의 의존이 필요한 관련 라이브러리를 자동으로 추가해 줌으로 애플리케이션에서는 별도로 추가할 의존은 없다. Eclipse에서 프로젝트를 열지 않는 경우, 다음과 같이 명령창에서 Maven을 이용하여 컴파일 및 실행이 가능하다. 단 프로그램 실행 전에 트위터 사이트로부터 받은 consumerKey, consumerSecret, accessToken, accessTokenSecret 값을 Twitter.xml에 올바르게 지정해야 한다.

다음은 ToTwitter 프로그램을 실행하여 필자가 수신한 트윗의 타임라인이다..


6. Camel의 애플리케이션 적용 패턴

지금까지 프로그램은 JVM 환경의 Java 애플리케이션에 Spring 프레임워크와 결합된 Camel을 내장하는 Camel의 사용 패턴을 사용했다. 이것은 Camel이 애플리케이션에 얼마나 쉽게 내장할 수 있는지를 보여 주는 하나의 예에 불과하다. Camel Core는 약 2.5M bytes 정도의 작은 크기를 가지면서도 POJO 방식을 지원하여 애플리케이션에 쉽게 내장될 수 있다. 이 작은 프레임워크는 애플리케이션에 내장되든, 독립된 애플리케이션으로 동작하던 애플리케이션들을 손쉽게 통합할 수 있게 해 준다. 이점이 기존 애플리케이션 통합 제품인 EAI 제품들이 일반적으로 메시징 미들웨어를 기반으로 통합하는 방식과 다른 점이다. 다음은 애플리케이션에서 Camel을 적용하는 몇몇 패턴들을 보여준다.


7. 맺음말

이 글에서는 애플리케이션이 Apache Camel 프레임워크로 외부 세계와 소통하는 예로 "Hello, world!" 메시지를 콘솔에 출력하는 애플리케이션에서부터 "Hello, world!" 메시지를 트위터의 타임라인으로 트윗하는 애플리케이션까지 개발해 보았다. 이 과정에서 애플리케이션은 일관되게 Camel의 ProducerTemplate 객체를 사용하여 "Hello, world!" 메시지를 전송했으며, Camel 설정의 수정을 통해 이 "Hello, world!" 메시지는 점점 더 복잡한 프로토콜을 가진 외부 시스템으로 전송되었다. 이 글의 마지막에 보인 애플리케이션처럼 애플리케이션을 트위터 시스템과 연동하게 하는 방법에 있어서, Camel보다 더 간단하게 애플리케이션을 트위터 시스템과 연동하게 할 수 있는 솔루션이나 EAI 제품이 있을까?

이 글을 꼼꼼히 읽은 독자라면 Camel이라는 통합 프레임워크의 가능성을 잘 이해했을 것이다. 즉 Camel 프레임워크를 이용해 애플리케이션에게 일관된 통합 인터페이스를 제공했고, 애플리케이션으로부터 통합 로직을 분리했으며, 외부 시스템의 변경은 설정으로 대응하도록 했다. 그러나 이 글에 등장하는 패턴은 기업 통합 패턴 중 하나인 메시징 게이트웨이(Messaging Gateway) 패턴을 적용한 것으로 볼 수 있으며, 또한 Camel의 많은 기능들 중 극히 일부분 이곳에 사용되었다. 이 글에서는 Camel의 능력을 과시(?)하기 위해 외부 시스템에 대한 인터페이스에 집중해서 Camel의 사용을 보여 주었지만, 사실 중요한 것은 어떻게 인터페이스할 것인가 보다 어떻게 통합할 것인가 이다. 즉 나무보다는 숲을 보는 통합 아키텍처 관점이 더 중요하다. 그런 관점에서 이 곳에 설명한 프로그램 예는 단지 기업 통합이라는 신세계의 입구를 본 것뿐이다.

일반적으로 우리는 프로시저 호출(procedure call) 방식의 동기 패러다임의 아키텍처에 익숙하다. 그러나 기업 통합 패턴(Enterprise Integration Patterns)은 메시지 전달(Messaging) 방식의 비동기 패러다임으로 기업 아키텍처를 접근한다. 이런 관점의 전환이 Apache Camel이라는 통합 프레임워크를 탄생시켰고, 지금도 난제로 여겨지고 있는 애플리케이션 통합에 신선한 전환점을 만들어 주고 있다.

Apache Camel은 Red Hat의 JBoss Fuse Middleware에도 포함되어 있다. JBoss Fuse는 오픈 소스로 구성된 메시징 서버(ActiveMQ), ESB 엔진(ServiceMix), 통합 프레임워크(Camel), OSGi 컨테이너(Karaf)의 애플리케이션 통합 미들웨어 제품군이다. JBoss Fuse는 기존 기업 내 애플리케이션 통합에 사용되던 독점적 폐쇄적 EAI 제품을 대체할 수 있다. 그리고 Red Hat에서 제품으로 출시되고 있으므로 필요한 경우 상용 제품과 동일한 수준의 기술 및 제품을 지원 받을 수 있다. 그러므로 오픈 소스인 Apache Camel에 대해 관심을 가진 누구라도 오픈 소스를 직접 다운받아 활용해 보거나, Red Hat을 통해 기업 통합에 필요한 컨설팅을 받을 수 있을 것이다. 필자의 회사도 Red Hat과 Fuse 제품의 기술 지원 파트너이다.

Spring 프레임워크의 창시자인 Rod Johnson이 2012년에 출판된 "Spring Integration In Action" 책의 서문 첫 문장에 다음과 같이 썼다. "Integration is currently a hot topic" (통합은 현재 뜨거운 주제이다.) 즉 미국에서도 현재 애플리케이션들 사이의 통합이 뜨거운 주제인 것이다. 그리고 Spring Source에서도 Apache Camel 보다는 늦었지만 Spring Integration 프레임워크를 만들어 열심히 발전 중에 있다. (참고로 필자는 Spring Integration의 채널 중심의 통합 접근 방법을 별로 좋아하지는 않는다.) 그리고 Camel이나 Spring Integration이나 그 사상은 모두 기업 통합 패턴(Enterprise Integration Patters) 책에 기반한다. (이 책은 필자가 번역해 "기업 통합 패턴, 에이콘 출판"으로 출간됐다.) 우리나라 사정은 어떤가? 우리나라도 애플리케이션들 사이의 통합이 심각한 문제이나, 전체 아키텍처 측면에서는 어떻게 접근해야 하는지 별다른 방안을 갖지 않는 것 같다. (물론 독점적 EAI 제품을 도입한다거나 스파게티 구조의 인터페이스를 가지기도 한다.) 통합에 있어서, 인터페이스를 개별적으로 고민하지 말고 전체 아키텍처를 고민해야 한다. 그래야 인터페이스의 추가에 따르는 개발 비용과, 유지보수에 따르는 비용을 절감할 수 있게 된다. 이런 통합 문제를 해결하기 위한 중심에 기업 통합 패턴(Enterprise Integration Patterns)이 있고 Apache Camel이 있다.


참고 사이트