← 문제로

20. 이벤트 소싱(Event Sourcing)과 CQRS

난이도 상
내 답안
모범답안

난이도: 상

모범답안 — 이벤트 소싱과 CQRS

1. 핵심 개념

전통적 상태 저장 방식은 엔티티의 "현재 값"만 DB 에 두고 갱신할 때마다 덮어쓴다(UPDATE balance=100). 과거는 사라진다. 이벤트 소싱은 상태를 바꾼 사건들을 순서대로 append 하고(Deposited 60, Deposited 40), 현재 상태는 그 이벤트들을 접어(fold/reduce) 계산한다(0 → +60 → +100). 상태는 "이벤트 로그의 함수"다.

이벤트는 불변·추가 전용(append-only) 이다. 이미 일어난 사실은 바뀌지 않는다 — 실수를 되돌리려면 옛 이벤트를 지우는 게 아니라 보정 이벤트(Reversed)를 추가한다. 이 성질이 감사·재현·디버깅의 토대가 된다. 이벤트는 과거형 사실(ItemPurchased)로 명명하고, 도메인 불변식 검증은 이벤트를 만들기 전(커맨드 처리)에서 한다.

2. 재구성·스냅샷

이벤트가 수백만 개인 애그리거트를 매 요청마다 처음부터 접으면 느리다. 스냅샷은 특정 버전 시점의 계산된 상태를 따로 저장해 두고, 로드 시 "가장 최근 스냅샷 + 그 이후 이벤트만 재생"하는 최적화다. 예: 버전 1,000,000 스냅샷 + 이후 12개 이벤트만 접기. 스냅샷은 캐시일 뿐 진실의 원천은 여전히 이벤트 로그라서, 스냅샷이 깨지거나 로직이 바뀌면 언제든 이벤트에서 다시 만들 수 있다. 주기(이벤트 N개마다)나 임계값으로 스냅샷을 찍는다.

3. CQRS

커맨드(쓰기: PurchaseItem)와 쿼리(읽기: GetInventory)는 요구가 다르다. 쓰기는 불변식·트랜잭션이 중요하고 정규화된 애그리거트로 처리되지만, 읽기는 화면별로 다양한 모양·빠른 조회가 필요하다. CQRS 는 이 둘의 모델을 분리한다. 쓰기 모델은 커맨드를 받아 이벤트를 생성·저장하고, 프로젝션(projection) 이 그 이벤트 스트림을 구독해 읽기 전용 뷰(리더보드, 인벤토리 목록, 일일 통계)를 각각 채운다. 하나의 이벤트에서 여러 읽기 뷰를 파생시킬 수 있다.

트레이드오프는 최종 일관성: 프로젝션이 이벤트를 비동기로 따라잡으므로 읽기 뷰가 쓰기보다 수 ms~수 초 뒤처질 수 있다. 방금 산 아이템이 인벤토리 화면에 잠깐 안 보일 수 있다. 강한 일관성이 필요한 읽기(구매 직후 확인)는 쓰기 모델에서 직접 읽거나, 프로젝션이 따라잡을 때까지 기다리는 장치가 필요하다.

4. 게임 서버에서의 이점

  • 감사·치팅 조사: "이 아이템이 어떻게 생겼나"를 이벤트 로그로 정확히 추적. 복제·비정상 획득을 사후에 조사할 수 있다.
  • 버그 복구: 잘못된 상태를 만든 버그를 고친 뒤, 이벤트를 다시 접거나 보정 이벤트를 넣어 정확한 상태를 복원. 상태만 저장했다면 과거가 없어 복구가 불가능하다.
  • 리플레이/관전/시간여행 디버깅: 이벤트를 재생해 특정 시점 상태를 재현. 전투 리플레이, "그 순간 서버가 뭘 봤나" 디버깅에 유용.
  • 파생 뷰 자유도: 나중에 새 통계·랭킹이 필요하면 과거 이벤트를 재생해 뷰를 만들 수 있다(상태 저장 방식은 과거 데이터가 없어 불가).

경제·거래가 있는 게임에서 "무슨 일이 있었는지"는 곧 신뢰·정산·분쟁 해결의 근거라, 이벤트 로그의 가치가 특히 크다.

5. 함정과 한계

  • 스키마 진화: 이벤트는 영구 보존되므로 옛 버전 이벤트를 몇 년 뒤에도 역직렬화할 수 있어야 한다. 필드 추가는 기본값으로, 의미 변경은 새 이벤트 타입으로. 업캐스터(upcaster)로 옛 이벤트를 새 형태로 변환하는 계층을 둔다.
  • 삭제 불가 vs 삭제 요구: 이벤트는 append-only 라 개인정보 삭제(GDPR) 요구와 충돌한다. 흔한 해법은 crypto-shredding(개인 데이터를 키로 암호화해 이벤트에 담고, 삭제 시 키만 폐기).
  • 프로젝션 재빌드 비용: 뷰 로직이 바뀌면 전체 이벤트를 재생해 뷰를 다시 만들어야 하는데, 로그가 크면 오래 걸린다.
  • 복잡성: 이벤트 스토어, 프로젝션 파이프라인, 최종 일관성 처리, 스냅샷 관리 등 인프라가 늘어난다.
  • 언제 과한가: 단순 CRUD, 과거 이력이 중요하지 않은 도메인, 강한 읽기 일관성이 필수인 소규모 서비스엔 오버엔지니어링이다. 경제·거래·전투 판정처럼 "이력과 감사가 본질"인 영역에 선택적으로 적용하고, 나머지는 상태 저장으로 두는 하이브리드가 실용적이다.

핵심 요약

이벤트 소싱은 "상태" 대신 "상태를 만든 사건들"을 진실의 원천으로 삼아 감사·복구·리플레이를 얻고, CQRS 로 쓰기/읽기 모델을 분리해 다양한 조회 뷰를 파생시킨다. 대가는 최종 일관성·스키마 진화·인프라 복잡성이며, 이력이 본질인 게임 경제/거래 영역에 선별 적용하는 것이 정석이다.