NYO_O

시스템의 결합도를 낮추는 비동기 통신: 이벤트 드리븐(Event-Driven) 아키텍처 본문

Architecture/MSA

시스템의 결합도를 낮추는 비동기 통신: 이벤트 드리븐(Event-Driven) 아키텍처

NYO_O 2026. 5. 27. 07:13
반응형

동기식 통신(HTTP/REST)이 만드는 시간적 강결합

지금까지 우리는 마이크로서비스 아키텍처(MSA)를 구성하는 핵심 인프라 요소들(서비스 디스커버리, 로드 밸런싱, 서킷 브레이커, 분산 추적 등)을 살펴보았습니다. 이 기술들은 모두 서비스 간의 통신이 HTTP/REST API를 통한 '동기식(Synchronous) 호출'이라는 가정하에 시스템의 안정성을 높이는 장치들입니다.

2026.05.27 - [Tech/MSA] - 흩어진 로그를 한 번에: 분산 추적(Distributed Tracing) 아키텍처

 

흩어진 로그를 한 번에: 분산 추적(Distributed Tracing) 아키텍처

마이크로서비스 환경에서의 디버깅모놀리식(Monolithic) 애플리케이션에서는 에러 추적이 비교적 단순합니다. 클라이언트의 요청 하나는 서버 내부의 단일 스레드(Thread)에 할당되어 처리되며, 예

ddangnyo.tistory.com

동기식 통신은 요청을 보내고 즉각적으로 응답을 받을 수 있어 직관적이고 설계하기 쉽습니다. 하지만 시스템의 규모가 커질수록 치명적인 아키텍처적 결함을 드러냅니다. 바로 '시간적 강결합(Temporal Coupling)'입니다.

사용자가 결제를 완료하면, 결제 서비스는 즉시 배송 서비스의 API를 호출하여 배송 준비를 지시해야 합니다. 만약 이때 배송 서비스가 정기 점검 중이거나 장애로 인해 다운되어 있다면 어떻게 될까요? 결제 서비스는 배송 서비스의 응답을 받지 못해 타임아웃에 빠지고, 결국 사용자의 결제 요청 자체가 실패로 처리됩니다. 배송 서비스의 장애가 결제 서비스의 장애로 직결되는 현상, 즉 마이크로서비스로 물리적인 시스템을 분리했음에도 논리적으로는 여전히 모놀리식처럼 강하게 묶여있는 모순이 발생하게 됩니다.

이벤트 드리븐(Event-Driven) 아키텍처

이러한 동기식 통신의 강결합을 끊어내고 진정한 의미의 독립적인 마이크로서비스를 완성하기 위해 등장한 패러다임이 바로 이벤트 드리븐 아키텍처(Event-Driven Architecture, EDA)입니다.

이벤트(Event)란 시스템 내에서 '과거에 이미 발생한 의미 있는 사실(Fact)'을 의미합니다. (예: 결제 완료됨, 회원 가입됨) 이벤트 드리븐 아키텍처에서 결제 서비스는 더 이상 배송 서비스의 API를 직접 호출하지 않습니다. 결제 서비스는 단지 자신의 비즈니스 로직을 처리한 후, "결제가 완료되었다"는 이벤트를 시스템 전역에 발행(Publish)할 뿐입니다.

배송 서비스, 포인트 적립 서비스, 알림 서비스 등 이 정보가 필요한 타 서비스들은 결제 서비스가 발행한 이벤트를 구독(Subscribe)하고 있다가, 이벤트가 발생하면 이를 가져와 각자의 비즈니스 로직을 비동기적으로 처리합니다. 이로써 서비스를 호출하는 주체(Producer)와 이를 처리하는 주체(Consumer)가 서로의 존재조차 알 필요가 없는 완벽한 느슨한 결합(Loose Coupling)이 달성됩니다.

메시지 브로커(Kafka, RabbitMQ)

이벤트를 발행하고 구독하는 비동기 통신이 가능하려면, 중간에서 이벤트를 안전하게 수신하고 전달해 줄 신뢰성 있는 우체국 역할이 필요합니다. 이를 메시지 브로커(Message Broker) 또는 이벤트 스트리밍 플랫폼이라고 부릅니다.

실무 환경에서는 주로 Apache Kafka(카프카)RabbitMQ가 이 역할을 수행합니다. 결제 서비스가 결제 완료 이벤트를 Kafka의 특정 토픽(Topic)으로 전송하면, Kafka는 이를 자신의 디스크나 메모리에 안전하게 저장합니다. 배송 서비스는 자신의 처리 속도에 맞춰 Kafka로부터 이벤트를 순차적으로 꺼내어(Pull) 소비합니다.

이러한 구조 덕분에 메시지 브로커는 분산 시스템의 거대한 완충재(Buffer) 역할을 하게 됩니다.

이벤트 드리븐 아키텍처의 2가지 강력한 이점

이벤트 드리븐 아키텍처와 메시지 브로커를 도입하면 시스템은 다음과 같은 강력한 이점을 얻습니다.

1) 완벽한 장애 격리와 회복 탄력성(Resilience): 앞선 서론의 상황으로 돌아가 보겠습니다. 결제 서비스가 이벤트를 Kafka에 발행했는데 배송 서비스가 다운되어 있다면 어떻게 될까요? 결제는 정상적으로 완료되어 사용자에게 성공 응답이 내려갑니다. 배송 서비스가 처리해야 할 이벤트들은 배송 서비스가 복구될 때까지 Kafka에 안전하게 쌓여 대기합니다. 이후 배송 서비스가 재가동되면, 밀려있던 이벤트들을 다시 읽어와 누락 없이 처리를 재개합니다. 단일 서비스의 장애가 전체 트랜잭션의 실패로 이어지지 않는 진정한 장애 격리가 실현된 것입니다.

2) 시스템 확장의 유연성 (Open-Closed Principle): 만약 서비스가 성장하여 결제 완료 시 '회원 등급 업데이트' 기능이 추가되어야 한다면 어떨까요? 동기식 환경이라면 결제 서비스의 코드를 수정하여 새로운 서비스의 API를 호출하도록 만들어야 합니다. 하지만 이벤트 드리븐 환경에서는 결제 서비스의 코드를 단 한 줄도 수정할 필요가 없습니다. 그저 '회원 서비스'를 새로 띄운 뒤, 기존의 결제 완료 이벤트를 똑같이 구독하도록 설정하기만 하면 됩니다.

최종 일관성(Eventual Consistency)

모든 아키텍처가 그렇듯, 이벤트 드리븐 역시 극복해야 할 치명적인 트레이드오프를 가집니다. 바로 '데이터 정합성 보장의 어려움'입니다.

동기식 통신에서는 2개의 서비스에 걸친 작업을 하나의 흐름에서 처리하므로 성공과 실패(Rollback)를 즉각적으로 동기화할 수 있습니다. 하지만 이벤트 드리븐 아키텍처에서는 결제는 성공했는데, 비동기적으로 처리되던 배송 시스템에서 예외가 발생할 경우 데이터의 불일치가 발생합니다.

이를 해결하기 위해선 배송 처리가 실패했음을 알리는 '보상 이벤트(Compensation Event)'를 다시 발행하여 결제를 취소하는 식의 복잡한 분산 트랜잭션 관리 기법, 이른바 Saga 패턴(Saga Pattern)을 개발자가 직접 설계하고 구현해야 합니다. 데이터가 실시간으로 완벽히 일치하지는 않지만 결과적으로는 일치하게 되는 '최종 일관성(Eventual Consistency)' 개념을 비즈니스 부서와 타협하고 시스템에 녹여내는 과정이 필수적입니다.

마무리

오늘은 서비스 간의 얽히고설킨 강결합을 비동기 이벤트 스트리밍으로 끊어내고, 시스템의 유연성과 탄력성을 극대화하는 '이벤트 드리븐 아키텍처'에 대해 알아보았습니다.

반응형