MSA와 이벤트 드리븐 아키텍처(Event-Driven Architecture, EDA) 간단하게 알아보기

2026. 2. 8. 19:39·📘 IT 지식
728x90

 

 

이전 글에서 MSA 아키텍처에 대한 기본적인 구조와 개념들에 대하여 알아 보았습니다. 이번 글에서는 MSA 구조에서 데이터를 주고 받기 위해 사용하는 이벤트 기반 구조에 대한 이해를 해보려고 합니다.

 

마이크로 서비스 아키텍처에 대해서 궁금하신 분은 아래의 글을 참고해주세요.

 

MSA (Micro Service Architecture) 구조와 API Gateway 역할

많은 서비스들이 규모가 커질수록 배포, 장애, 확장성과 같은 문제들이 중요해지면서 모놀리식 구조에서 MSA로 전환을 고민하게 된다는 이야기들을 많이 들어보셨을거에요. 이 과정에서 자연스

minchoi0912.tistory.com

 

 

 

이번 글은 우아콘 - 배달의 민족 마이크로 서비스 여행기 영상과 우아한 기술 블로그의 글을 참고하여 이해한 바를 정리한 글입니다. 

 

 

 

 

 

회원시스템 이벤트기반 아키텍처 구축하기 | 우아한형제들 기술블로그

최초의 배달의민족은 하나의 프로젝트로 만들어졌습니다. 배달의민족의 주문수는 J 커브를 그리는 빠른 속도로 성장했고, 주문수가 커지면서 자연스럽게 트래픽 또한 매우 커졌습니다. 하나의

techblog.woowahan.com

 

 

 

이벤트 드리븐 아키텍처(EDA)는 시스템에서 일어난 상태의 변화(Event)를 알리고, 이를 필요로 하는 서비스들이 알아서 반응할 수 있도록 만드는 방식입니다. MSA의 핵심 키워드 중 하나인 서비스 간의 `느슨한 결합`을 위하여 이벤트를 활용 할 수 있습니다. 서로 시스템에 대한 의존도와 영향을 줄이고 목적에 집중하도록 하여 서비스를 독립적으로 사용 할 수 있도록 합니다. 

 

서비스 간 이벤트 전달 방식과 API 호출을 통한 방식을 비교해보겠습니다. 쇼핑몰 주문 프로세스를 통해 설명해보겠습니다.

 

"직접 전화하기" vs "게시판에 공지하기"

  • 직접 전화하기 = API 호출, 강한 결합
    • 주문 서비스가 결제 서비스에게 전화를 걸어 "결제 해!" 라고 이야기 합니다. 결제 팀이 전화를 받지 않는 경우 주문 팀도 일을 하지 못하고 기다려야 합니다. 이후 배송 서버와도 동일합니다. 
    • 마지막배송 서버가 응답하지 않으면 사용자는 주문 완료 화면을 보지 못하고 무한 로딩에 빠지게 됩니다. 
  • 게시판에 공지하기 = 이벤트 기반 공지, 느슨한 결합
    • 주문 서비스가 이벤트를 메세지를 메세지 큐에 던지고 사용자에게는 '주문 성공!' 화면을 보여주고, 이후 결제, 배송 서비스는 이벤트를 받아서 처리하는 목적만 처리하면 됩니다. 
    • 배송 서버가 잠시 죽은 상태여도 이벤트는 메세지 큐에 쌓여 있음으로, 배송 서버가 살아났을 때 다시 처리 할 수 있으므로 전체 프로세스는 안전합니다. 

 

 

Gemini가 생성해준 마이크로 서비스 아키텍처와 이벤트 드리븐 이미지

 

 

이벤트 발행 정의

 

특정 서비스에서 이벤트 메세지를 발행 할 때는 의존 관계가 없이, 다른 서비스에서 처리를 기대하는 목적을 제외하고 단순한 도메인 이벤트 그 자체만을 발행하여 느슨한 결합을 가지도록 설계하는 것이 중요합니다.

 

다음과 같은 비즈니스 정책에 대해서 설명해보겠습니다.

이 정책은 회원 서비스, 쿠폰/혜택 서비스 간의 의존성을 가질 수 있게 되는데 이벤트 방식을 통해 의존성을 분리 할 수 있습니다.

"회원이 탈퇴하면, 해당 회원이 보유한 모든 미사용 쿠폰은 즉시 무효화 되어야 한다"

 

우선 코드 상의 강한 결합으로 이루어진 경우는 다음과 같습니다.

class UserService:

    def withdraw_user(self, user_id: int):
        # 1. 회원 탈퇴 처리
        self.user_repository.mark_withdrawn(user_id)

        # 2. 쿠폰 도메인을 "직접" 호출 (강한 결합)
        self.coupon_service.invalidate_unused_coupons(
            user_id=user_id
        )

 

여기서 드러나는 문제점은 UserService가 CoponService의 존재를 알고 있어야 하며, 쿠폰 서비스가 장애가 발생하게 되는 경우에는 회원 탈퇴 과정이 불완전한 상태가 될 수 있습니다. 따라서 회원 도메인이 쿠폰 도메인의 책임을 함께 짋어지는 형태가 되며 강한 도메인 간의 결합이 발생하게 됩니다.

 

두 서비스 간의 의존성을 느슨하게 하려면 어떻게 해야 할까요?

회원 서비스는 메세지를 통해 이벤트를 발행하고, 쿠폰 서비스는 그 이벤트에 반응하도록 할 수 있습니다. 이 때 발행하는 메세지에 대해서도 의존성 없이 정의하는 것이 필요합니다. 회원 서비스에서 쿠폰 서비스로 부터 쿠폰 무효화 라는 이벤트를 발생하는 것이 아닌 '회원 탈퇴' 라는 이벤트 자체를 정의하는 것이 필요합니다. 

 

하나의 모듈이 따른 모듈에 종속적이면 안되며, 의존성을 제거 해야 합니다. 

 

그러면, 해당 비즈니스 정책을 이벤트 드리븐 방식으로 구현해보겠습니다. 

회원 서비스는 쿠폰 서비스를 호출하지 않고, 단순히 '탈퇴했다' 라는 사실만을 이벤트로 발생 시켜야 합니다. 

class UserService:

    def withdraw_user(self, user_id: int):
        self.user_repository.mark_withdrawn(user_id)

        # 쿠폰 서비스 호출 X
        # "탈퇴했다"라는 사실만 이벤트로 발행
        self.event_bus.publish(
            event_type="USER_WITHDRAWN",
            payload={"user_id": user_id}
        )

 

이벤트 자체는 도메인 이벤트를 통해 달성하려는 목적이 아닌 도메인 이벤트 그 자체를 발행해야 합니다.

 

이벤트 구성 요소 정의

이벤트를 발행할 때는 단순히 "변했다" 라는 사실 이외에 다음 4가지 요소가 명확하게 표함되어 있어야 합니다

.

  • 대상(Target)
    • 어떤 식별자(ID)가 변경되었는가?
  • 행동(Action)
    • 무엇을 했는가? 
    • '배차됨', '취소됨' 처럼 과거형으로 표현되는 경우가 많음
  • 정보(Data)
    • 행동과 관련된 데이터 (예시: 주문ID, 라이더ID 등)
    • 하지만 무분별하게 모든 데이터를 담게되면 결합도가 높아지게 되니 주의가 필요함. Consumer가 편하도록 모든 정보를 이벤트에 넣게 되면, 나중에 발행처의 로직을 고칠 때마다 소비처에 대한 영향이 커질 수 있음으로 최소한의 필수 정보만 담는 것이 필요합니다. (Zero Payload 방식)
    • 추가적인 정보는 소비처에서 API를 통해 다시 조회하게 하거나 공통된 형식으로 정의하는 것이 필요합니다.
  • 시간(Time)
    • 이벤트가 발생한 시간이 아닌, 실제 도메인에서 행위가 발생한 시각을 담아야함

 

이벤트의 순서가 보장되지 않으면 비즈니스 오류가 발생할 수 있음으로 항상 이벤트 소비시에는 순서가 꼬일 가능 성을 염두에 두고 로직을 설계 하는 것이 필요합니다. 또한 모든 것에 대한 '실시간성'에 대한 강박을 버리고 '언젠가'는 알림이 가고 통계에 반영된다는 식의 수용을 통하여 시스템 복잡도를 낮추는 것이 필요합니다.

 

Zero Payload 방식은?

이벤트에 비즈니스 데이터(payload)를 실어 보내지 않고 무슨 일이 일어났다 라는 사실만 전달하는 방식으로, "이벤트는 사실만 알리고, 데이터는 각 서비스가 직접 조회한다." 라고 이해 할 수 있습니다. 이벤트가 점점 DTO처럼 비대해지는 현상을 방지하고 생산자와 소비자 간의 결합도가 증가되는 것을 방지 할 수 있습니다. 

 

이 방식은 도메인 간의 경계를 보호하고, 각 소비자가 자기 필요에 맞게 데이터를 조회할 수 있도록 하게 소비자 독립성을 지킬 수 있게 합니다. 하지만 소비자가 API/DB 조회를 추가적으로 하게 되면서 복잡성이 커지고 엄밀히 말하면 실시간성이 어려우며 약간의 지연이 발생 될 수 있습니다. 

 

 

단순히 기술 스택에 Kafka와 같은 이벤트 발행 아키텍처를 추가한다고 하여 EDA가 완성되는 것은 아닙니다. 서비스 간의 도메인 경계를 정하고, 서비스 간의 데이터 흐름을 제어하는 방식을 반드시 고민해야 하는 것이 필요합니다. 

 

 

300x250
저작자표시 비영리 변경금지 (새창열림)

'📘 IT 지식' 카테고리의 다른 글

MSA (Micro Service Architecture) 구조와 API Gateway 역할  (0) 2026.02.05
웹 소켓 vs SSE vs Polling  (0) 2023.04.06
from origin 'http://localhost:3000' has been blocked by CORS policy / CORS란?  (1) 2022.09.21
AWS EC2 서버 구축에서 git commit, RDS 연동까지  (0) 2022.08.17
브라우저 로딩 과정 및 성능 측정 기준  (0) 2022.08.02
'📘 IT 지식' 카테고리의 다른 글
  • MSA (Micro Service Architecture) 구조와 API Gateway 역할
  • 웹 소켓 vs SSE vs Polling
  • from origin 'http://localhost:3000' has been blocked by CORS policy / CORS란?
  • AWS EC2 서버 구축에서 git commit, RDS 연동까지
최밍구
최밍구
발등에 불이 뜨겁게 배우는 중
  • 최밍구
    프로그래밍구
    최밍구
  • 전체
    오늘
    어제
    • 분류 전체보기
      • ☕️ JAVA
      • 🌱 Spring
      • 🐍 Python
      • 🗄️ DataBase
      • 🌊 Data Engineering
      • 🛠️ DevOps
      • ✨ JavaScript
        • Node.js
      • 🤖 AI
      • 🔎 알고리즘
      • 📘 IT 지식
      • 🍀 창고
        • 비트캠프
        • 취업
        • 일기장
  • 링크

    • Go Github
  • 인기 글

  • 태그

    제네릭
    인터넷
    카프카
    database
    타임존
    RabbitMQ
    data
    데이터베이스
    자바
    Airflow
    MSA
    자바제네릭
    dataengineering
    생활코딩자바
    Kafka
    생활코딩
    pgvector
    mongoDB
    Java
    PostgreSQL
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
최밍구
MSA와 이벤트 드리븐 아키텍처(Event-Driven Architecture, EDA) 간단하게 알아보기
상단으로

티스토리툴바