Ruby를 위한 풀스택 프로그래밍 모델과 이벤트 소싱

Ismael Celis — Event-Sourced Mental Models in Ruby | Baltic Ruby 2025

작성자
Baltic Ruby
발행일
2025년 09월 02일

핵심 요약

  • 1 이벤트 소싱을 통해 시스템의 상태 변화를 시간 기반의 불변 이벤트로 기록하고, 이를 활용하여 현재 상태를 재구성하는 도메인 모델링 방식을 제시합니다.
  • 2 CQRS(Command Query Responsibility Segregation) 패턴을 적용하여 쓰기(명령) 모델과 읽기(쿼리) 모델을 분리하고, 프로젝션을 통해 UI에 최적화된 실시간 뷰를 구축합니다.
  • 3 Ruby 액터 모델 기반의 명령, 이벤트, 반응 핸들러를 사용하여 비즈니스 로직을 명시적으로 표현하고, 동시성 처리 및 자동화된 워크플로우를 구현하는 방법을 시연합니다.

도입

본 발표는 칠레 출신의 런던 기반 Ruby 개발자가 Ruby를 위한 풀스택 프로그래밍 모델과 이벤트 소싱(Event Sourcing)에 대해 설명합니다. 발표자는 기존의 정적인 구조 및 관계 중심의 도메인 모델링 방식이 복잡한 비즈니스 행위를 표현하는 데 한계가 있음을 지적하며, 시간의 흐름에 기반한 행동 중심의 도메인 모델링 접근 방식의 필요성을 강조합니다. 이를 위해 가상의 커피숍 POS(Point of Sale) 애플리케이션 데모를 활용하여 이벤트 소싱의 개념과 실제 적용 사례를 명확하게 제시합니다.

기존 도메인 모델링의 한계

기존의 소프트웨어 도메인 모델링은 주로 구성 요소 간의 구조와 관계를 그래프 형태로 이해하는 방식에 의존합니다. 이는 시스템의 정적인 측면을 설명하는 데는 효과적이지만, 비즈니스 워크플로우와 같은 동적인 ‘행동’을 명시적으로 표현하는 데는 부족함이 있습니다. 워크플로우는 종종 웹 컨트롤러, 백그라운드 작업 등 다양한 실행 컨텍스트에 암묵적으로 결합되어 있어 이해하고 관리하기 어렵습니다.

이벤트 소싱(Event Sourcing)의 도입

이벤트 소싱은 시스템의 상태 변화를 불변의 이벤트(Event) 형태로 기록하고, 이 이벤트들의 순차적인 적용을 통해 현재 상태를 재구성하는 방식입니다. 이는 은행 계좌의 거래 내역을 통해 잔액을 파악하는 것과 유사합니다. 핵심 구성 요소는 다음과 같습니다. * 명령(Command): 시스템 상태 변경에 대한 사용자의 의도. 유효성 검사를 거칩니다. * 이벤트(Event): 시스템에서 실제로 발생한 불변의 사실(Fact). 저장소에 영구적으로 기록됩니다. * 스트림(Stream): 특정 엔티티(예: 주문)에 대한 이벤트들을 그룹화하는 식별자. 스트림 내 이벤트는 순차적으로 처리됩니다.

CQRS(Command Query Responsibility Segregation)와 읽기 모델

이벤트 소싱은 상태 업데이트(쓰기)에 최적화되어 있지만, 복잡한 쿼리(읽기)에는 비효율적입니다. 이를 해결하기 위해 CQRS 패턴이 사용됩니다. CQRS는 데이터 모델을 두 가지로 분리합니다. * 명령 모델(Command Model): 이벤트 소싱을 사용하여 시스템 상태를 업데이트합니다. * 쿼리 모델(Query Model): UI나 클라이언트에 최적화된 읽기 전용 데이터 모델입니다. 이벤트 저장소에서 발생하는 이벤트를 수신하여 ElasticSearch, Redis, PostgreSQL 등 다양한 형태로 데이터를 재구성(프로젝션)하여 저장합니다. 쿼리 모델은 언제든지 이벤트 히스토리로부터 재구축 가능하여 유연성이 높습니다.

Ruby 구현 상세

발표자는 sourced라는 자체 라이브러리를 활용하여 Ruby로 이벤트 소싱을 구현하는 방법을 소개합니다. * OrderState: 주문의 현재 상태를 나타내는 단순한 인메모리 Struct 또는 해시. * 명령/이벤트(Command/Event): AddItemCommand, ItemAddedEvent와 같이 기능의 한 조각을 정의하는 데이터 구조체. * 액터(Actor): 특정 스트림(예: 주문)에 발생할 수 있는 모든 것을 캡슐화하는 클래스. OrderActorcommand_handler에서 명령을 검증하고 이벤트를 생성하며, event_handler에서 이벤트를 통해 상태를 업데이트합니다.

동시성 및 워크플로우

이벤트 소싱은 스트림 단위의 동시성을 제공합니다. reaction_handler를 통해 특정 이벤트에 반응하여 새로운 스트림(예: 결제 스트림)을 시작하거나 다른 액터에 명령을 보낼 수 있습니다. 이는 비즈니스 로직 내에서 동시성을 명시적으로 모델링하는 것을 가능하게 합니다. 이벤트 간의 인과 관계(causation)와 상관 관계(correlation)를 추적하여 복잡한 동시성 워크플로우를 감사하고 디버깅할 수 있습니다.

자동화 및 실시간 UI

  • 자동화: 특정 조건(예: 주문이 결제되고 준비 완료됨)이 충족될 때, reaction_handler를 통해 자동으로 다음 명령(예: 주문 배달)을 실행하여 인간의 수동 작업을 대체할 수 있습니다.
  • 실시간 UI: 서버 센트 이벤트(SSE)를 활용하여 백엔드에서 발생하는 관련 이벤트를 브라우저에 실시간으로 전송하고, 브라우저는 수신된 HTML 조각을 DOM에 병합하여 페이지 새로고침 없이 UI를 업데이트합니다. Sinatra 애플리케이션에서 sourced 라이브러리를 통해 명령을 비동기적으로 처리하고, 프로젝터가 UI 관련 이벤트를 발생시켜 실시간 업데이트를 구현합니다.

향후 계획

발표자는 이 실험적인 작업의 향후 계획으로 버그 수정, 중첩된 상태 머신(State Machine) 및 내구성 있는 실행(Durable Execution) 개념 도입, SQLite 지원 추가, 그리고 명령/이벤트 정의와 액터 DSL을 기반으로 한 자동 다이어그램 및 문서 생성 기능을 언급합니다.

결론

본 발표는 Ruby 환경에서 이벤트 소싱과 CQRS 패턴을 활용하여 복잡한 도메인 비즈니스 로직을 효과적으로 모델링하고 구현하는 새로운 접근 방식을 제시합니다. 시간 기반의 행동 중심 모델링은 비즈니스 워크플로우를 명시적으로 표현하고, 시스템의 모든 변화를 불변의 기록으로 남겨 감사(Audit) 및 디버깅의 용이성을 높입니다. 또한, CQRS를 통한 읽기/쓰기 모델의 분리는 시스템의 확장성과 유연성을 극대화하며, 액터 모델과 서버 센트 이벤트를 활용한 동시성 처리 및 실시간 UI 업데이트는 현대 애플리케이션 개발의 요구사항을 충족시킵니다. 이 모델은 견고하고 유연하며 확장 가능한 Ruby 애플리케이션을 구축하는 데 중요한 통찰을 제공합니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!