이벤트 소싱 핵심 개념
이벤트 소싱은 현대 소프트웨어 아키텍처, 특히 C# 및 .NET Core 애플리케이션에서 자주 사용되는 강력하지만 복잡한 패턴입니다. 이벤트 소싱을 구현하는 방법을 완벽하게 이해하기 위해 CodeOpinion.com 의 데릭 코마틴이 " 이벤트 소싱 핵심 개념 " 이라는 영상에서 설명하는 주요 원칙들을 살펴보겠습니다.
이벤트 스토어 개념, 도메인 주도 설계 또는 이벤트 기반 시스템 구축 방법을 더 잘 이해하고 싶다면 제대로 찾아오셨습니다. 자, 시작해 볼까요!
이벤트 소싱 소개
영상 초반(0:00)에 데릭은 이벤트 소싱과 관련된 핵심 개념에 대한 혼란에 대해 이야기합니다. 그는 이벤트 소싱을 단순히 "현재 상태를 캡처하는 것"이 아니라 이벤트를 저장하는 것, 즉 도메인 모델에 발생하는 모든 변경 사항을 추가 전용 로그에 기록하는 것이라고 정의합니다.
이렇게 하면 시스템 상태 변화에 대한 완전한 기록을 유지할 수 있습니다. 최종 상태만 저장하는 대신, 발생한 비즈니스 로직을 반영하는 이벤트를 저장합니다.
이벤트: 비즈니스 정보 수집
0:33에 데릭은 이벤트가 진정으로 무엇인지 설명합니다. 이벤트는 시스템 내에서 이미 발생한 일, 즉 비즈니스 사실을 나타냅니다. 예를 들어, "ProductReceived" 이벤트에는 수령한 수량과 날짜가 포함됩니다.
데릭은 이벤트를 저장할 때 "제품 출하됨" 또는 "재고 조정됨"과 같이 과거 시제를 반영하는 명명 규칙을 사용해야 한다고 강조합니다. 이벤트 스키마에는 공개 GUID ID, 공개 문자열 이름, 타임스탬프와 같은 필드가 있을 수 있습니다.

각 이벤트에는 고유 식별자가 있어야 합니다. 이는 이벤트 스트림에 이벤트를 추가하는 방식의 저장 시스템에서 매우 중요합니다.
이벤트 스트림: 이벤트 구성
2시 2분에 데릭은 이벤트 스트림으로 전환합니다. 그는 관계형 데이터베이스 테이블과 비교하면서 이벤트 소싱에서는 도메인 객체(예: 재고 품목)와 관련된 모든 이벤트가 동일한 이벤트 스트림에 속한다고 설명합니다.
각 이벤트 스트림은 특정 엔티티와 연결되며, 이 엔티티는 일반적으로 공개 GUID ID와 문자열 이름으로 정의됩니다. 예를 들어, SKU가 ABC123인 제품은 입고 또는 출고와 같은 영구적인 이벤트를 포함하는 자체 스트림을 갖게 됩니다.
데릭은 이러한 것들을 모델링할 때 도메인 주도 설계 개념에서 차용한 집합 루트와 도메인 객체의 관점에서 생각하라고 제안합니다.
수명 주기(단기 또는 장기)를 모델링하면 스트림의 수와 크기를 최적화하는 데 도움이 됩니다. 이는 복잡한 시스템의 성능을 향상시키는 데 매우 중요합니다.
예측 및 읽기 모델
4시 12분에 데릭은 예측과 읽기 모델을 소개합니다. 이벤트 기반 시스템은 현재 상태뿐만 아니라 이벤트 자체를 캡처하므로 "현재 재고 수량은 얼마입니까?"와 같은 질문에 답하려면 이벤트를 재생해야 합니다.
읽기 모델을 구축하려면 스트림에서 이벤트를 처리해야 합니다. 예를 들어, private void Apply(Event e) 또는 이와 유사한 메서드를 사용하여 각 이벤트 유형에 대한 이벤트 핸들러에 따라 재고를 증가시키거나 감소시킬 수 있습니다.
데릭은 문서 데이터베이스나 관계형 데이터베이스에서 읽기 모델을 구축하는 방법을 설명합니다. 예를 들어, 하나의 프로젝션은 단순히 재고 수량을 보여주고, 다른 프로젝션은 배송 내역을 보여줄 수 있습니다.
이는 명령-쿼리 책임 분리(CQRS)를 반영합니다. 즉, 쓰기 작업(명령)과 읽기 작업(쿼리)을 분리하는 것입니다.
쓰기 모델에 대한 예측
6시 48분에 데릭은 쓰기 측에도 프로젝션을 적용하는 방법을 보여줍니다. 이는 조치가 시행되기 전에 그 타당성을 검증하는 데 매우 중요합니다.
명령 처리기에서 제품을 출하하기 전에 충분한 수량이 있는지 확인해야 합니다. 데릭은 private void Apply(List)와 같은 메서드를 사용합니다.
공개 정수형인 Version과 같은 필드는 스트림의 변화를 추적하여 궁극적인 일관성을 보장하는 데 도움이 됩니다.

이러한 실질적인 구현은 새로운 이벤트를 처리할 때 비즈니스 로직을 강화하여 시스템이 유효한 상태 전환에 대해서만 작동하도록 보장합니다.
구독: 새로운 이벤트에 대한 대응
8시 1분에 데릭은 구독에 대해 이야기합니다. 구독을 통해 이벤트 참여자들은 새로운 이벤트 소식을 받아보고 반응할 수 있습니다.
예를 들어, 프로젝터는 이벤트 스트림을 구독하고 "제품 배송됨" 이벤트를 감지하면 읽기 모델을 업데이트할 수 있습니다. 또는, 퍼블리셔가 이벤트를 수신하고 RabbitMQ 또는 Kafka와 같은 외부 시스템에 게시하여 다른 서비스와 통합할 수도 있습니다.

데릭은 내부 모델 업데이트뿐만 아니라 여러 이벤트 시스템에 걸쳐 데이터를 배포하고 최종 일관성을 유지하기 위한 구독 기능에 대해서도 설명합니다.
이는 이벤트 기반 아키텍처의 또 다른 주요 이점을 보여줍니다. 즉, 서비스는 최종 일관성을 유지하면서도 독립적으로 확장할 수 있다는 것입니다.
핵심 개념 요약
9시 21분에 데릭은 핵심 개념을 요약합니다.
사건은 사실을 포착합니다.
이벤트 스트림은 엔티티별로 이벤트를 구성합니다.
프로젝션은 스트림을 쿼리 가능한 읽기 모델 또는 실행 가능한 쓰기 모델로 변환합니다.
- 구독을 통해 서비스는 그에 맞춰 반응하고 업데이트할 수 있습니다.
그는 이벤트를 추가 전용 로그에 저장하고, 감사 추적을 유지하며, 필요할 때 이벤트를 다시 재생해야 한다고 강조합니다.
스냅샷 및 최적화
9시 39분에 데릭은 스냅샷에 대해 이야기합니다. 이벤트 소싱과 함께 자주 논의되지만, 그는 스냅샷이 핵심 요구 사항이라기보다는 성능 최적화를 위한 것이라고 명확히 밝혔습니다.
스냅샷은 주기적으로 부분적인 상태를 저장함으로써 모든 이벤트를 다시 재생하는 데 필요한 오버헤드를 줄여주지만, 전체 기록은 추가 전용 로그를 통해 여전히 유지됩니다.
핵심 차이점: 이벤트 소싱 vs 이벤트 기반 아키텍처
오전 10시에 데릭은 흔히 잘못 알려진 사실, 즉 이벤트 소싱과 이벤트 기반 아키텍처는 다르다는 점에 대해 경고합니다! Kafka와 같은 도구는 데이터 분산에 도움이 되지만, 진정한 이벤트 소싱은 도메인 이벤트를 변경 불가능한 감사 추적 기록으로 남기는 데 중점을 둡니다.
이벤트 소싱을 복잡한 시스템에 통합할 때 이러한 차이점을 이해하는 것이 매우 중요합니다.
결론
데릭 코마틴의 영상을 보면, 이벤트 소싱은 최종 상태뿐만 아니라 모든 변경 사항을 이벤트로 기록하는 것임을 알 수 있습니다. 이벤트를 추가 전용 스토리지 시스템에 저장하면 감사 추적, 이벤트 쿼리의 유연성, 명령 쿼리 책임 분리에 대한 강력한 지원을 제공하는 풍부한 이벤트 스키마를 구축할 수 있습니다.
.NET Core 에서 C# 이벤트 소싱을 다루든 다른 플랫폼에서 다루든, 데릭이 핵심 개념, 이벤트 핸들러, 프라이빗 세트, 프로텍티드 세트, 그리고 모델에서 private void Apply()와 같은 이벤트를 적용하는 방법에 대해 체계적으로 설명한 내용은 매우 유용합니다.
탄력적인 도메인 모델을 구축하거나, 스토리지 시스템을 개선하거나, 최종 일관성과 같은 여러 이점을 제공하는 복잡한 패턴을 만들고 있다면, 데릭의 접근 방식을 연구하는 것은 필수적입니다. 그의 YouTube 채널 에서 더 많은 유익한 영상을 확인해 보세요.

