본문 바로가기

엔지니어링(TA, AA, SA)/아키텍처

[아키텍처] 이벤트 주도 아키텍처(Event-Driven Architecture)

이벤트 주도 개발 패턴은 확장성이 뛰어난 애플리케이션을 생성하는데 사용되는 널리 사용되는 분산 비동기 아키텍처 패턴이다. 또한 적용이 용이하여, 작은 응용 프로그램뿐만 아니라 크고 복잡한 응용 프로그램에서도 사용할 수 있다. 이벤트 주도 개발은 이벤트를 비동기식으로 수신하고 처리하는 고도로 분리된 단일 용도의 이벤트 처리 구성 요소로 구성된다.

 

이벤트 주도 설계 패턴은 중재자와 브로커의 두가지 주요 토폴로지로 구성된다. 중재자 토폴로지는 일반적으로 중앙 중재자를 통해 이벤트 내에서 여러 단계를 조정해야하는 경우에 사용되는 반면, 브로커 토폴로지는 중앙 중재자를 사용하지 않고 이벤트를 함께 연결하려는 경우에 사용된다. 아키텍처 특성과 구현 전략은 이 두 토폴로지간에 서로 다르므로 각 상황을 이해하여 특정 상황에 가장 적합한 것을 알아야 한다.


중재자 토폴로지(Mediator Topology)

중재자 토폴로지는 여러 단계가 있고 이벤트를 처리하기 위해 일정 레벨의 오케스트레이션이 필요한 이벤트에 유용하다. 예를 들어, 주식 거래를 하는 단일 이벤트는 먼저 거래를 확인한 다음 다양한 준수 규칙에 따라 해당 주식 거래의 준수 여부를 확인하고, 브로커에게 거래를 할당하고, 수수료를 계산하고, 마지막으로 그 브로커에게 전달된다. 이러한 모든 단계는 단계의 순서와 연속 및 병렬로 수행할 수 있는 단계를 결정하기 위해 일정 수준의 오케스트레이션이 필요하다.

 

중재자 토폴로지 내에는 "이벤트 큐, 이벤트 중재자, 이벤트 채널, 이벤트 프로세서"의 네가지 주요 유형의 아키텍처 구성 요소가 있다. 이벤트 플로우는 클라이언트가 이벤트를 이벤트 큐로 전송하는 것으로 시작하며, 이벤트 큐는 이벤트를 이벤트 중재자로 전송하는데 사용된다. 이벤트 중재자는 비동기로 전송된 이벤트를 추가적인 이벤트를 전송하고, 초기 사건을 오케스트레이션하는 역할을 하게되며, 중재자가 전달한 이벤트를 수신하는 이벤트 채널을 통해 각 프로세스가 관련 작업을 수행한다. 이벤트 채널을 구독하는 이벤트 프로세서는 이벤트 중재자로부터 이벤트를 수신하고 특정 비즈니스 로직을 실행하여 이벤트를 핸들링한다. 다음은 이벤트 중심 아키텍처 패턴의 일반적인 중재자 토폴로지를 보여준다.

이벤트 주도 아키텍처 - 중재자 토폴로지

이벤트 주도 아키텍처에는 적게는 수십개에서 수백개의 이벤트 큐가 존재하게 되는 것이 일반적이다. 패턴이 이벤트 큐 구성 요소의 구현을 지정하는 것은 아니다. 메시지 큐, 웹서비스 엔드 포인트 등의 구성요소가 될 수 있다.

 

이 패턴에는 두가지 유형의 이벤트(최초 이벤트 및 처리단계 이벤트)가 있다. 초기 이벤트는 중재가 수신하게되는 최초 이벤트인 반면, 처리 이벤트는 중재자가 생성하고 이벤트 처리 구성요소에 의해 수신된 이벤트이다.

 

이벤트 중재자 구성 요소는 초기 이벤트가 포함된 단계를 조정하게 된다. 초기 이벤트의 각 단계마다 이벤트 중재자가 특정 처리 이벤트를 이벤트 채널로 전송한 후 이벤트 프로세서가 이를 수신하여 처리한다. 이벤트 중재자는 초기 이벤트를 처리하는데 피룡한 비즈니스 로직을 실제로 수행하지는 않는다. 초기 이벤트를 처리하는데 필요한 단계를 알고 있을 뿐이다.

 

이벤트 중재자는 이벤트 채널을 사용하여 초기 이벤트의 각 단계와 관련된 특정 처리 이벤트를 이벤트 프로세서에 비동기적으로 전달한다. 이벤트 토픽은 메시지큐 또는 메시지 토픽이 될 수 있지만 메시지 토픽은 중재자 토폴로지에서 처리 이벤트가 여러 이벤트 프로세서(각각 수신된 처리 이벤트에 따라 다른 태스크를 수행함)에 의해 처리될 수 있다.

 

이벤트 프로세서 구성 요소에는 처리 이벤트를 처리하는데 필요한 응용 프로그램 비즈니스 로직이 있다. 이벤트 프로세서는 응용 프로그램이나 시스템에서 특정 작업을 수행하는 독립적이고 분리된 아키텍처 구성요소이다. 이벤트 프로세서 컴포넌트의 처리단계 내용은 다양하게 세분화될 수 있지만, 일반적으로 이벤트 프로세서 구성요소들은 단일 비즈니스 태스크를 수행해야하며 특정 이벤트를 완료하기 위해 다른 이벤트 프로세서에 의존하지 않아야 한다. 

 

이벤트 중재자는 다양한 방법으로 구현될 수 있다. 아키텍트는 이벤트 중재자로 선택한 솔루션이 요구사항과 일치할 수 있도록 각 구현 옵션을 이해하고 있어야 한다.

 

이벤트 중재자의 가장 단순하고 일반적인 구현은 Spring Integration, Apache Camel, Mule ESB와 같은 오픈 소스 통합 허브를 통하는 것이다. 이러한 오픈소스 통합 허브의 이벤트 흐름은 일반적으로 JAVA 코드 또는 DSL(도메인 별 언어)를 통해 구현된다. 보다 정교한 중개 및 오케스트레이션을 위해 공개 소스 Apache ODE와 같은 BPEL 엔진과 결합된 BPEL(비즈니스 프로세스 실행 언어)을 사용할 수 있다. BPEL은 초기 이벤트 처리에 필요한 데이터 및 단계를 설명하는 표준 XML 유사 언어이다. 보다 복잡한 오케스트레이션(인간 상호 작용이 포함된 단계 포함)이 필요한 대규모 응용 프로그램의 경우 jBPM과 같은 BPM(Business Process Manager)을 사용하여 이벤트 중재자를 구현할 수 있다.

 

이 토폴로지를 사용한 이벤트 중심 아키텍처의 성공적인 구현을 위해서는 사용자의 요구를 이해하고 올바른 이벤트 조정자 구현과 일치시키는 것이 중요하다. 간단한 라우팅 로직을 수행하기 위해 BPM 솔루션을 구현하는 것과 마찬가지로 오픈 소스 통합 허브를 사용하여 매우 복잡한 비즈니스 프로세스 관리 오케스트레이션을 수행하는것은 실패로 이어질 수 있다.

 

중재자 토폴로지 예

중재자 토폴로지의 작동 방식을 설명하기 위해 보험 회사에 가입한 뒤에 가입자 주소 변경이 발생했다고 가정해보자. 이 경우 초기 이벤트는 "이동 이벤트"와 같은 명칭으로 불릴 수 있다. "이동 이벤트" 처리와 관련된 단계는 이벤트 중재자 내에 포함된다. 각 초기 이벤트 단계마다 이벤트 중재자가 처리 이벤트(ex. 주소 변경)를 작성한다. 처리 이벤트를 이벤트 채널로 전송하고 해당 이벤트 프로세서(ex. 고객 프로세스, 주문 프로세스 등)에 의해 처리 이벤트가 처리될 때까지 기다리게 된다. 이 프로세스는 초기 이벤트의 모든 단계가 처리될때까지 계속된다.

 


브로커 토폴로지

브로커 토폴로지는 중앙 이벤트 중재자가 없다는 점에서 중재자 토폴로지와 다르다. 대신, 메시지 흐름은 경량 메시지 브로커(예: ActiveMQ, HornetQ 등)를 통해 체인과 같은 방식으로 이벤트 프로세스 구성 요소에 분산된다. 이 토폴로지는 비교적 간단한 이벤트 처리 플로우가 있고 중앙 이벤트 오케스트레이션이 필요하지 않은 경우에 유용하다.

 

브로커 토폴로지에는 두가지 주요 유형의 아키텍처 구성요소인 브로커 컴포넌트와 이벤트 프로세서 컴포넌트가 있다. 브로커 컴포넌트는 중앙 집중식 또는 결합될 수 있으며 이벤트 플로우에서 사용되는 모든 이벤트 채널을 포함한다. 브로커 구성 요소에 포함된 이벤트 채널은 메시지 큐, 메시지 토픽 또는 이 둘의 조합일 수 있다. 

 

아래 다이어그램에서 볼 수 있듯이 초기 이벤트를 제어하고 조정하는 중앙 이벤트 중재자 구성요소가 없다. 각 이벤트 프로세스 구성 요소는 이벤트를 처리하고 방금 수행한 작업을 나타내는 새 이벤트를 발행한다. 예를 들어, 주식 포트폴리오의 잔액을 유지하는 이벤트 프로세서 stock split이라는 초기 이벤트를 수신할 수 있다. 초기 이벤트를 기반으로, 이벤트 프로세서는 일부 포트폴리오 재조정을 수행한 다음 재조정 포트폴리오라는 브로커에 새 이벤트를 공개할 수 있다. 그런 다음 다른 이벤트 프로세서에서 이를 처리하게 된다. 이벤트가 이벤트 프로세서에 의해 발행되었지만 다른 이벤트 프로세서에 의해 처리되지 않는 경우가 있을 수 있다. 이것은 응용프로그램이 발전해나가거나 향후 기능이 확장될때 일반적인 모습이기도 하다.

브로커 토폴리지

브로커 토폴리지 작동 방식도 위 중재자 토폴로지의 보험고객 주소변경 이벤트를 예를 들어 설명한다. 브로커 토폴로지에서 초기 이벤트를 수신할 중앙 이벤트 중재자가 없으므로 고객 프로세스 구성 요소는 이벤트를 직접 수신하고 고객 주소를 변경하며 고객의 주소가 변경되었다는 이벤트를 보낸다. 이 예에서는 주소 변경 이벤트에 관심이 있는 두가지 이벤트 프로세서 인 견적 프로세스와 클레임 프로세스가 있다. 견적 프로세스 구성 요소는 주소 변경을 기반으로 새 자동 보험료율을 다시 계산하고 시스템의 나머지 부분에 이벤트를 발행하여 해당 작업을 표시하게 된다. 반면에 클레임 처리 구성요소는 동일한 변경 주소 이벤트를 수신하지만 이 경우 미해결 보험 클레임을 업데이트하고 이벤트를 업데이트 클레임 이벤트로 시스템에 게시하게 된다. 새로운 이벤트는 다른 이벤트 프로세스 구성 요소에 의해 선택되며 해당 특정 시작 이벤트에 대해 더이상 이벤트가 발행되지 않을때까지 이벤트 체인이 시스템내에서 동작하게 된다.  

브로커 토폴로지 예

위 그림에서 볼 수 있듯이, 브로커 토폴로지는 비즈니스 기능을 수행하기 위한 이벤트 체인에 관한 것이다. 브로커 토폴로지를 이해하는 가장 좋은 방법은 릴레이 경주에 빗대어 생각하는 것이다. 릴레이 경주에서 주자는 바톤을 잡고 일정 거리를 달리고 마지막 주자가 결승선을 통과할때까지 다음 주자에게 바톤을 건내어 주게 된다. 릴레이 경주에서, 주자가 바톤을 넘기면 레이스가 끝난다. 하나의 이벤트 프로세스가 처리를 수행하고 나면 더이상 해당 이벤트와 관련이 없게 되는 개념과 유사하다.


고려 사항

이벤트 주도 아키텍처 패턴은 주로 분산 비동기식 처리가 지니고 있는 기본적인 특성으로 인해 구현하기가 비교적 복잡한 패턴이다. 이 패턴을 구현할 때는 브로커 또는 중재자 실패시 원격 프로세스 가용성, 응답 부족 및 브로커 재연결 논리와 같은 다양한 분산 아키텍처를 사용하여 문제를 해결해야 한다.

 

이 아키텍처 패턴을 선택할 때 고려해야할 사항은 단일 비즈니스 프로세스에 대한 원자성 트랜잭션이 없다는 것이다. 이벤트 프로세서 구성요소는 분리 분산되어 있기때문에 트랜잭션 작업 단위를 유지하기가 매우 어렵다. 이러한 이유로 이 패턴을 사용하여 응용 프로그램을 설계할때는, 독립적으로 실행할 수 있는 이벤트와 독립적 실행이 불가능한 이벤트에 대해 지속적으로 생각하고 그에 따라 이벤트 프로세서의 세분화를 계획해야 한다. 하나의 작업 단위를 분리할 필요가 있음을 발견하게 되면, 분리된 트랜잭션 환경에서 동작되어야 한다는 것을 뜻한다. 이러한 상황이 응용프로그램 요구사항에 들어맞는지 않는 내용 일 수도 있다.

 

아마도 이벤트 주도 아키텍처 패턴의 가장 어려운 측면 중 하나는 이벤트 프로세서 구성요소의 생성, 유지, 관리와 같은 내용일 것이다. 각 이벤트는 일반적으로 그것과 관련된 특정 규약(ex. 이벤트 처리기에 전다로디는 데이터 값과 데이터 형식)을 가지고 있을 것이다.

 

이 패턴을 사용할때는 표준 데이터 형식(ex. XML, JSON, Java Object 등)을 확정하고, 처음부터 데이터 규약에 따른 버전 정책을 올바르게 수립하는 것이 매우 중요할 것이다.


패턴 분석

다음 표에는 이벤트 주도 아키텍처 패턴의 일반적인 아키텍처 특성에 대한 등급 및 분석이 포함되어 있다. 각 특성에 대한 등급은 패턴의 일반적인 구현뿐만 아니라 패턴이 일반적으로 알려진 기능에 따른 경향에 기초한다.

 

Overall Agility (전반적인 민첩성) - 높음

전반적인 민첩성은 끊임없이 변화하는 환경에 빠르게 대응할 수 있는 능력이다. 이벤트 프로세서 구성 요소는 단일 용도이며 다른 이벤트 프로세서 구성요소와는 완전히 분리되어있다. 변경 사항은 일반적으로 하나 또는 몇개의 이벤트 프로세서로 분리되며 다른 구성요소에 영향을 주지 않고 신속하게 작성할 수 있다.

 

Deployment (간편한 배포) - 높음

전체적으로 이 패턴은 이벤트 처리기 구성 요소의 분리된 특성으로 인해 배포가 비교적으로 쉽다. 브로커 토폴로지는 중재자 토폴로지 보다 배포하기 더 쉬운 편이다. 이벤트 중재자 구성요소의 경우 이벤트 프로세서에 다소 밀접하게 연결되어 있기 있다. 브로커의 경우, 주어진 변경에 대해서만 배포하면 되기 때문이다.

 

Testability (쉬운 테스트) - 낮음

유닛 테스트는 그리 어렵지 않지만 이벤트를 생성하려면 일종의 특수한 테스트 클라이언트 또는 테스트 도구가 필요하다. 이 패턴의 비동기 특성으로 인해 테스트도 복잡하다.

 

Performance (성능) - 높음

모든 메시징 인프라로 인해 성능이 좋지 않아질 것으로 예상하지만, 일반적으로 이 패턴은 비동기 기능을 통해 고성능을 달성할 수 있다. 다시 말해, 분리된 병렬 비동기 작업을 수행하는 기능이 메시지 큐, 큐 제거 비용보다 더 많은 영향을 준다.

 

Scalability (확장성) - 높음

독립적인 이벤트 처리기 구성덕분에 이 패턴은 확장성이 높은 편이다. 각 이벤트 처리기는 개별적으로 확장되어 세밀한 확장성을 제공한다.

 

Development (개발의 용이성) - 낮음

비동기가 가진 특성과 규약 작성 및 이벤트 처리기가 응답이 없다거나 실패하는 경우에 대비해서, 브로커 내의 고급 오류처리 기능이 필요성이 요구되어, 이로 인하여 개발 복잡성이 다소 높은 편이다.