
CQRS는 읽기와 쓰기를 나누는 아키텍처 패턴입니다. 하지만 이름이 멋있다고 먼저 도입하면 단순한 CRUD보다 훨씬 어려운 시스템을 만들 수 있습니다.
핵심은 읽기 요구와 쓰기 규칙이 서로 다른 모델을 요구할 만큼 벌어졌는지 확인하는 것입니다. 이 글은 Microsoft Architecture Center, Martin Fowler, Microservices.io 자료를 기준으로 CQRS의 필요성과 남용 기준을 정리합니다.

CQRS를 한 줄로 정리하면
CQRS는 Command Query Responsibility Segregation의 약자입니다. 상태를 바꾸는 command 쪽 모델과 상태를 읽는 query 쪽 모델을 분리하는 생각입니다.
Command side
- 주문 생성
- 결제 승인
- 재고 차감
Query side
- 주문 목록 화면
- 관리자 통계 화면
- 고객별 구매 이력 조회같은 주문 데이터를 다루더라도 쓰기 쪽은 규칙과 일관성이 중요하고, 읽기 쪽은 화면에 필요한 모양과 조회 속도가 중요할 수 있습니다.
CRUD와 CQRS는 무엇이 다를까
단순 CRUD에서는 하나의 모델이 생성, 조회, 수정, 삭제를 모두 담당합니다. 화면도 단순하고 규칙도 단순하면 이 방식이 더 낫습니다. CQRS는 같은 모델 하나로 읽기와 쓰기를 모두 감당하기 어려워질 때 검토하는 선택입니다.
- CRUD: 같은 모델로 저장하고 같은 모델로 조회한다
- CQRS: 쓰기 모델과 읽기 모델을 분리할 수 있다
- CRUD가 틀린 것이 아니라, 요구가 복잡해질 때 한계가 보일 수 있다
CQRS가 필요해지는 신호
CQRS가 어울리는 신호는 보통 조회 요구와 변경 규칙이 서로 다른 방향으로 복잡해질 때 나타납니다.
- 쓰기 쪽은 강한 비즈니스 규칙과 상태 전이를 지켜야 한다
- 읽기 쪽은 여러 aggregate를 합친 화면 전용 데이터가 필요하다
- 조회 성능 때문에 denormalized view나 별도 projection이 필요하다
- 이벤트 기반으로 read model을 갱신해도 사용자가 받아들일 수 있다
- 팀이 eventual consistency와 운영 복잡도를 다룰 준비가 있다
작은 서비스에서 CQRS가 과한 이유
CQRS는 모델을 나누는 만큼 코드, 테스트, 배포, 장애 지점이 늘어납니다. 읽기 모델을 따로 만들면 그 모델이 최신 상태인지, 실패했을 때 어떻게 복구할지, 이벤트 중복은 어떻게 처리할지까지 고민해야 합니다.
- 단순 관리자 CRUD 화면만 있다
- 조회 성능 문제가 아직 없다
- 쓰기 규칙이 복잡하지 않다
- eventual consistency를 허용하기 어렵다
- 운영 모니터링과 재처리 도구가 없다
이런 상황에서는 CQRS보다 명확한 계층 분리, query 최적화, index 설계, application service 정리가 먼저일 수 있습니다.
읽기 모델과 쓰기 모델을 나눌 때의 예시
예를 들어 주문 시스템에서 쓰기 모델은 주문 상태 전이, 결제 가능 여부, 재고 차감 규칙을 지킵니다. 반면 읽기 모델은 고객 화면에 필요한 주문 요약, 배송 상태, 결제 상태를 한 번에 보여주도록 별도 테이블이나 view로 만들 수 있습니다.
Write model: Order aggregate
READY -> PAID -> SHIPPED -> COMPLETED
cancel(), pay(), ship() 같은 상태 전이 규칙 중심
Read model: OrderSummary
order_id, customer_name, total_price, payment_status, shipping_status
화면 조회와 정렬, 필터링 중심두 모델이 분리되면 읽기 화면은 빨라질 수 있지만, write model의 변경이 read model에 반영되는 흐름을 안정적으로 만들어야 합니다.
실무 체크리스트
- 먼저 CRUD와 일반 query 최적화로 충분한지 확인한다
- 읽기 요구와 쓰기 규칙이 정말 다른 모델을 요구하는지 본다
- read model 동기화 실패를 감지하고 재처리할 수 있는지 확인한다
- eventual consistency를 사용자 경험과 업무 규칙이 허용하는지 검토한다
- CQRS를 시스템 전체가 아니라 필요한 bounded context 안에서만 적용한다
정리
CQRS를 이해할 때는 용어부터 외우기보다 이 도구가 어떤 경계를 나누려는지 먼저 보는 편이 좋습니다. 오늘 글의 기준은 후보를 좁히는 흐름, 반복 상태의 이동, 입력 검증의 책임, 빌드 입력의 변경 빈도, 읽기와 쓰기의 복잡도 차이를 구분하는 것입니다.
함께 보면 좋은 내부 글은 Application Service와 Domain Service 차이, DDD Entity와 Value Object 차이, MSA는 언제 과할까입니다. 외부 기준은 Microsoft Azure Architecture Center – CQRS pattern, Martin Fowler – CQRS, Microservices.io – CQRS pattern를 확인했습니다.