콘텐츠로 바로가기

CQRS Pattern

Command Query Responsibility Segregation: 데이터 쓰기(Command)와 읽기(Query) 모델을 물리적으로 분리하는 아키텍처 패턴. Write Model의 이벤트를 구독하여 Read Model을 동기화하는 비동기 프로세스. 결과적...

지식 성숙도: evergreen

목차 보기22

1. Overview

CQRS(Command Query Responsibility Segregation)는 데이터를 바꾸는 쪽(Command)과 읽는 쪽(Query)의 모델을 분리해 각각을 자기 목적에 맞게 설계하는 아키텍처 패턴입니다. 한 모델로 쓰기와 읽기를 모두 처리하는 전통적 CRUD 구조에서는 두 요구가 서로의 설계를 제약하는데, CQRS는 이를 분리해 쓰기 모델은 도메인 규칙의 일관성에, 읽기 모델은 조회 성능과 형태에 최적화합니다.

이 패턴은 Bertrand Meyer의 CQS(Command-Query Separation) 원칙을 객체 수준에서 시스템 수준으로 넓힌 것으로 설명되며, Greg Young이 이름을 붙이고 Martin Fowler가 널리 소개했습니다. 이 글은 두 모델의 역할 분담, 이벤트로 읽기 모델을 갱신하는 Projection, 결과적 일관성이라는 대가, 그리고 이 패턴이 맞는 경우와 과설계가 되는 경우를 정리합니다.

2. Scope & Boundaries

In-Scope

  • 핵심 분리 (Command/Query Split): Write Model과 Read Model의 책임과 저장 구조.
  • Projection: Write Model의 이벤트로 Read Model을 동기화하는 과정과 결과적 일관성.
  • 적용 판단: 도입이 맞는 조건과 주의점.

Out-of-Scope

Boundaries

  • CQRS vs. Event Sourcing: 둘은 자주 함께 쓰이지만 서로 다른 패턴입니다. CQRS는 읽기/쓰기 모델의 분리이고, Event Sourcing은 상태 대신 변경 이벤트 열을 저장하는 방식입니다. CQRS를 쓰면서 Write Model을 일반 DB로 둘 수도 있습니다.
  • 모델 분리 vs. 저장소 분리: 분리의 최소 단위는 "모델"입니다. 같은 데이터베이스 위에 별도의 읽기 모델을 두는 구성부터, 쓰기와 읽기 저장소를 완전히 나누는 구성까지 폭이 있으며, 물리적 분리는 필요에 따라 선택하는 단계입니다.
  • CQRS vs. 단순 CRUD: 도메인이 단순하면 CRUD가 더 적절하며, CQRS는 한 시스템의 일부 영역에 선택적으로 적용하는 편이 일반적입니다.

3. Counterexample

  • 단순 CRUD 서비스에 CQRS 도입: 도메인 규칙이 적고 읽기와 쓰기 형태가 거의 같은 서비스에 두 모델과 동기화 파이프라인을 얹는 경우. 두 모델 유지로 시스템 복잡도가 증가하므로 단순 CRUD 서비스에는 과설계입니다. Fowler도 대부분의 시스템에서는 CQRS가 위험한 복잡도를 더한다고 경고하며 신중히 적용할 것을 권합니다.
  • 읽기 모델이 즉시 반영된다는 가정: Projection이 비동기이면 읽기/쓰기 모델 간 동기화 지연이 생겨, 사용자가 방금 작성한 데이터가 즉시 안 보일 수 있습니다. 화면 설계(낙관적 UI, 응답에 결과 포함 등)와 합의된 일관성 수준 없이 도입하면 사용자 불만이 됩니다.
  • 모든 곳에 같은 모델을 쓰는 전역 적용: 시스템 전체를 CQRS로 통일하려는 시도. 일관성이 중요한 영역과 조회 최적화가 중요한 영역은 서로 다르므로 영역별로 판단해야 합니다.

4. Prerequisites

  • 트랜잭션과 정규화 (Basic): Write Model이 불변식을 어떻게 지키는지, 정규화 스키마가 무엇을 보장하는지 알아야 합니다.
  • 결과적 일관성 (Basic): 비동기 갱신이 읽기에 어떤 지연과 불일치를 만드는지 이해해야 합니다.
  • 이벤트 기반 설계 (Recommended): Projection이 이벤트를 구독해 읽기 모델을 갱신하므로 → Event Sourcing을 함께 보면 도움이 됩니다.

5. Learning Map

Sequence Core Cluster Objective & Description Evidence (BoK)
1 Command/Query Split 쓰기와 읽기 모델의 책임을 분리하고 각각의 저장 구조를 설계합니다. Primary:Fowler2011
2 Projection 이벤트를 구독해 읽기 모델을 갱신하는 흐름과 결과적 일관성을 이해합니다. Primary:Young2010
3 Applicability 도입이 맞는 조건과 과설계가 되는 조건을 구분합니다. Primary:Fowler2011
4 Operations 동기화 지연의 허용 범위를 SLO로 정의하고 모니터링합니다. Industry:Microsoft

연결 노트

6. Learning Topics

Basic

Core Topic 01: Command와 Query의 분리 (Write Model / Read Model)

  • Why to Learn: 쓰기와 읽기는 요구가 다릅니다. 쓰기는 규칙 검증과 일관성이, 읽기는 조회 속도와 표현 형태가 중요하므로 하나의 모델로 둘을 만족시키려 할 때의 충돌을 이해해야 합니다.
  • What to Learn:
    • Concepts: 데이터 쓰기(Command)와 읽기(Query) 모델을 분리합니다. Write Model은 도메인 불변식(Invariant) 검증에 최적화되어 정규화와 트랜잭션 중심이고, Read Model은 조회 성능에 최적화된 비정규화 Materialized View로 스키마를 쿼리 요구사항에 맞게 자유롭게 설계합니다.
    • Skills: Command는 상태를 바꾸고 결과 데이터를 돌려주지 않는 방향으로, Query는 상태를 바꾸지 않는 방향으로 인터페이스를 나눕니다.
    • Tools: 관계형 DB(쓰기), 캐시·검색 인덱스(읽기).
    • Trade-offs: 각 모델을 목적에 맞게 최적화할 수 있는 대신 모델이 둘이 되어 운영 대상이 늘어납니다.

요청 흐름은 아래처럼 두 갈래로 나뉩니다.

CODE
클라이언트
  ├─ 쓰기 요청(Command) ──▶ Write Model : 이벤트 저장소 또는 정규화 DB
  └─ 읽기 요청(Query)   ──▶ Read Model  : 비정규화 캐시 또는 검색 인덱스
  • How to Learn:
    • 1단계: 주문 도메인에서 "주문 생성", "주문 취소"는 Command로, "주문 목록 조회", "일별 매출 집계"는 Query로 분류해 봅니다.
    • 2단계: 같은 데이터를 쓰기용 정규화 테이블과 조회용 비정규화 뷰로 나눠 설계하고 두 스키마를 비교합니다.
  • Implement: Command 핸들러와 Query 핸들러를 별도 모듈로 나눈 작은 예제 서비스.

Core Topic 02: Projection과 결과적 일관성 (Eventual Consistency)

  • Why to Learn: 두 모델을 분리하면 둘 사이를 잇는 동기화가 시스템의 핵심 위험 지점이 되므로, 동기화가 어떻게 이루어지고 어떤 불일치가 생기는지 알아야 합니다.
  • What to Learn:
    • Concepts: Projection은 Write Model에서 나오는 이벤트를 받아 Read Model에 반영하는 비동기 과정이고, 그래서 읽기 쪽은 잠시 뒤처질 수 있는 결과적 일관성(Eventual Consistency)을 받아들입니다.
    • Skills: 이벤트 순서 보장, 중복 전달에 대비한 멱등(idempotent) 처리, Projection 재구축(replay).
    • Tools: 메시지 브로커, 이벤트 로그.
    • Trade-offs: 읽기 확장과 비정규화의 이점 대신 쓰기 직후 조회 시 지연이 생깁니다.
  • How to Learn:
    • 1단계: 쓰기 직후 조회에서 값이 안 보이는 상황을 의도적으로 재현합니다.
    • 2단계: 같은 이벤트가 두 번 전달돼도 읽기 모델이 같은 상태가 되도록 멱등 처리를 설계합니다.
  • Implement: 이벤트 목록을 읽어 비정규화 읽기 모델을 구축하고, 처음부터 다시 재생해 같은 결과가 나오는지 확인하는 Projection.

Practical

Core Topic 03: 도입 판단과 운영 (When to Use & Cautions)

  • Why to Learn: CQRS는 복잡도를 늘리는 패턴이므로 어떤 문제에 쓰고 어떤 문제에 쓰지 않을지 판단하는 능력이 구현 능력만큼 중요합니다.
  • What to Learn:
    • Concepts: 읽기 부하가 쓰기보다 압도적으로 높을 때(일반적 웹 서비스), 동일 데이터를 다양한 형태로 조회해야 할 때(대시보드, 검색, 리포트), 변경 이력을 빠짐없이 남기는 감사 추적이 필요해 Event Sourcing과 함께 쓸 때도 어울립니다.
    • Skills: 읽기/쓰기 모델 간 동기화 지연을 사용자 경험과 SLO로 정의하는 법.
    • Tools: 지연 모니터링 대시보드.
    • Trade-offs: 두 모델 유지로 시스템 복잡도가 증가하므로 단순 CRUD 서비스에는 과설계입니다.
  • How to Learn:
    • 1단계: 현재 운영 중인 서비스의 읽기:쓰기 비율과 조회 형태 종류를 측정합니다.
    • 2단계: 동기화 지연의 허용 한계(예: 몇 초)를 정하고 이를 SLO 항목으로 적어 봅니다.
  • Implement: 도입 판단 체크리스트(읽기/쓰기 비율, 조회 형태 수, 일관성 요구)를 점수화한 간단한 평가표.

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry:ID) Flags(core)
CQRS 쓰기(Command)와 읽기(Query)에 서로 다른 모델을 사용하도록 분리하는 아키텍처 패턴입니다. 기본 구조 패턴 CQS vs. CRUD 항상 저장소를 둘로 나눠야 한다고 오해함 Primary:Fowler2011 core
Write Model 도메인 불변식 검증과 상태 변경을 책임지는 정규화·트랜잭션 중심의 모델입니다. 기본 쓰기 측 Invariant / Aggregate vs. Read Model 조회에도 같은 모델을 써야 한다고 가정함 Primary:Young2010 core
Read Model 조회 성능과 화면 요구에 맞게 비정규화해 둔 읽기 전용 모델입니다. 기본 읽기 측 Materialized View vs. Write Model 원천 데이터라고 오해함(파생 데이터) Primary:Young2010 core
Projection 이벤트를 구독해 Read Model을 갱신하는 비동기 과정입니다. 권장 동기화 Event / Replay vs. ETL 동기 트랜잭션으로 반영된다고 가정함 Primary:Young2010 core
Eventual Consistency 갱신 후 일정 시간이 지나면 읽기 모델이 쓰기 결과와 일치하게 되는 일관성 모델입니다. 권장 일관성 Staleness / SLO vs. Strong Consistency 영원히 불일치할 수 있다고 오해함 Industry:Microsoft core

8. References

Primary References

  • [Fowler, M. "CQRS." martinfowler.com/bliki/CQRS.html] — 읽기와 쓰기 모델 분리의 개요, 장점, 그리고 복잡도에 대한 경고.
  • [Young, G., 2010. "CQRS Documents."] — Command와 Query 분리를 체계적으로 설명한 Greg Young의 문서.
  • [Meyer, B., 1988. "Object-Oriented Software Construction." Prentice Hall] — Command-Query Separation 원칙의 출처.

Secondary References

  • [Fowler, M. "Event Sourcing." martinfowler.com/eaaDev/EventSourcing.html] — 이벤트 열로 상태를 저장하는 방식과 CQRS와의 관계.

Industry References

  • [Microsoft Azure Architecture Center: "CQRS pattern." learn.microsoft.com/en-us/azure/architecture/patterns/cqrs] — 읽기/쓰기 모델 분리의 적용 시나리오, 이점, 일관성 고려 사항.

9. Final Checklist

Primary Checklist

  • Write Model과 Read Model이 각각 무엇에 최적화되는지 설명할 수 있는가?
  • CQRS와 Event Sourcing이 서로 다른 패턴임을 구분해 말할 수 있는가?

Secondary Checklist

  • Projection이 비동기일 때 생기는 읽기 지연과 그 대응(낙관적 UI 등)을 설계했는가?
  • 이벤트 중복 전달에 대비한 멱등 처리와 Projection 재구축 절차가 있는가?

Industry Checklist

  • 단순 CRUD로 충분한 서비스에 CQRS를 도입하고 있지 않은지 읽기/쓰기 비율과 조회 형태로 검토했는가?
  • 결과적 일관성의 허용 범위를 SLO로 정의하고 지연을 모니터링하고 있는가?

System Architecture · Event-Driven & CQRS

6 / 7