이벤트 소싱(Event Sourcing)을 통한 Rails 애플리케이션 재구성: 진실의 스트림으로 현실을 다시 만들기
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
DevOps
대상자
- Rails 개발자 (복잡한 워크플로우, 감사 로그 필요성 있는 시스템 개발자)
- 중간~고난이도 (이벤트 기반 아키텍처 이해 필요)
핵심 요약
- 이벤트 기반 데이터 저장
UPDATE
대신AccountBalanceDeposited.new(...)
형식의 이벤트 저장 (코드 형식 강조)- 감사 로그와 시간 기반 쿼리
- 이벤트 스트림을 통해 시간 기준 데이터 복원 (예: "이전 주에 활동한 사용자 목록")
- 아키텍처 패턴
- Write Model (명령 처리 → 이벤트 발행) / Read Model (이벤트 기반의 최적화된 조회)
섹션별 세부 요약
1. 전통적 CRUD의 한계
- 문제점
UPDATE
명령으로 변경 사유(Why)가 지워짐- "주문 금액이 0이 되었는지"는 복잡한 SQL로 추적 필요
- "특정 시간에 활동한 사용자" 쿼리 불가능
- 적용 분야
- 금융 시스템 (비난제 거부 가능성)
- 규제 업계 (설계 단계의 감사 로그)
- 복잡한 워크플로우 (예: 주문 완료 시 롤백)
2. 이벤트 소싱 기반 구현
- 이벤트 저장 방식
EventStore.publish(AccountBalanceDeposited.new(...))
(코드 형식 강조)balance = events.reduce(0) { |sum, event| sum + event.amount }
(현재 잔액 계산)- 도구 활용
- RailsEventStore (Rails 전용 간단한 구현)
- Eventide (PostgreSQL 중심)
- Kafka (서비스 간 이벤트 공유)
3. 이벤트 기반 아키텍처 설계
- 모델 분리
- Write Model (명령 처리) → Read Model (이벤트 기반 조회 최적화)
- 프로젝션 (Materialized View) 활용
- 이벤트 안정성
- Upcaster로 이벤트 필드 업데이트 (예:
AccountBalanceDeposited.upcast(...)
) - 스냅샷 저장 (10,000 이벤트 복원 시 성능 개선)
4. 이벤트 소싱 구현 시 주의점
- 이벤트 분할
- 이벤트를 작고 집중적으로 유지 (이벤트 스파게티 방지)
- 아키텍처 확장성
- 단일 이벤트 스트림부터 시작 (과도한 설계 방지)
- 아이디empo티시티
- 이벤트 재생 시 중복 처리 방지 설계
5. 실무 적용 전략
- 점진적 도입
- 1개의 핵심 모델에 이벤트 발행 추가
- ActiveRecord로 쿼리 유지
- 프로젝션으로 로직 점진 이전
- 비추천 사례
- 간단한 CRUD 앱 (예: To-Do List)
- 실시간성 요구 높은 시스템 (이벤트 복원 오버헤드)
결론
- 이벤트 소싱은 ActiveRecord와 호환 가능 (기존 CRUD 모델 유지)
- 점진적 도입을 통해 복잡성 줄이고, 감사 로그와 시간 기반 쿼리 실현
- 이벤트 스파게티 방지를 위해 이벤트 단위 최소화 및 스냅샷 활용 권장