기술과 산업/아키텍처
소프트웨어 아키텍처 시리즈 13화 – CQRS 아키텍처의 실전 설계 전략: 분리를 넘어 책임의 정렬로
B컷개발자
2025. 7. 18. 12:26
728x90
CQRS는 단순히 읽기와 쓰기를 나누는 패턴이 아닙니다. 올바른 책임 분리를 통해 복잡한 시스템을 정리하고, 확장성과 유지보수를 확보할 수 있는 구조적 전략입니다. 이번 글에서는 실전 아키텍처에 CQRS를 도입할 때의 설계 방식과 고려사항을 자세히 다룹니다.
CQRS는 왜 생겨났는가?
전통적인 시스템에서는 데이터 모델이 단일하게 사용되며, 같은 레이어에서 읽기와 쓰기를 모두 처리합니다. 하지만 복잡한 도메인에서는 다음과 같은 문제가 자주 발생합니다:
- 읽기/쓰기 요구사항이 전혀 다름
- 예: 관리자 페이지는 복잡한 통계 기반 조회, 주문 처리 로직은 트랜잭션 중심
- 성능 최적화 방향이 다름
- 읽기는 캐싱, 인덱스 설계가 중요하고
- 쓰기는 무결성과 트랜잭션 관리가 핵심
- 모델의 책임이 혼재되어 복잡성 증가
- 하나의 서비스/엔티티가 지나치게 많은 역할을 수행
이러한 문제를 해결하기 위한 전략으로 등장한 것이 CQRS입니다.
CQRS 구조 핵심 요약
[ UI ]
┣━▶ [ Command Service ] ─▶ [ 도메인 모델 / 이벤트 생성 ]
┗━▶ [ Query Service ] ──▶ [ 읽기 최적화 모델 / Projection ]
구성 요소책임
Command | 상태 변경 처리 |
Query | 상태 조회 처리 |
도메인 모델 | 비즈니스 규칙, 트랜잭션 책임 |
Projection | 읽기용 데이터 모델, 성능 최적화용 |
Event Handler | 쓰기 결과를 읽기 모델로 반영 |
실전 예제 – 상품 주문 시스템
Use Case 1: 주문 등록
- PlaceOrderCommand → Command Handler에서 도메인 호출
- 주문 엔티티가 생성되고, OrderPlacedEvent가 발생
- 이벤트 핸들러가 읽기 모델(예: OrderSummaryView)를 갱신
Use Case 2: 주문 목록 조회
- OrderQueryService.getOrders(userId) 호출
- 읽기 전용 DB 또는 캐시된 Projection에서 주문 목록 반환
Projection 모델 설계 팁
- View 중심 데이터 모델링
- 고객이 자주 조회하는 항목을 중심으로 테이블 재구성
- 주문 목록, 상품 추천, 최근 활동 로그 등
- 데이터 중복 허용
- Projection은 정규화보다 속도와 사용 편의성 우선
- 정합성은 쓰기 모델에서 보장
- 비동기 업데이트 전략 필요
- 도메인 이벤트 수신 후 비동기로 Projection 갱신
- 일관성은 Eventually Consistent 방식 채택
Command 모델 설계 시 주의점
- 쓰기 모델은 오직 의미 있는 유즈케이스 단위로 설계
- 도메인 규칙을 철저히 캡슐화 (엔티티, 밸류 객체 중심)
- 외부 의존성은 포트를 통해 분리 (ex. 결제 API, 외부 시스템 호출)
실무에서 자주 저지르는 실수
실수설명
읽기/쓰기 모델 분리 없이 서비스만 분리 | 구조가 분리된 것처럼 보이지만 실제로는 하나의 DB와 모델을 공유하고 있음 |
Projection을 너무 일반화 | 도메인 모델과 Projection이 너무 유사해지고, 성능 개선 효과 없음 |
이벤트 누락 또는 순서 보장 실패 | 비동기 이벤트 기반 시스템에서 가장 흔한 오류 |
CQRS와 헥사고날 아키텍처의 연결점
CQRS의 구성요소는 헥사고날 아키텍처의 구조와 자연스럽게 연결됩니다.
- Command는 Inbound Port로 설계되고, 도메인과 연계
- Query는 별도의 Adapter 혹은 Read-Only UseCase로 구현
- Projection 갱신은 Outbound Port 또는 비동기 Subscriber에서 수행
즉, 헥사고날 구조는 CQRS의 흐름을 물리적으로 격리할 수 있게 해주는 좋은 틀이 됩니다.
CQRS 도입 기준 체크리스트
다음 중 다수에 해당하면 CQRS를 도입할 수 있습니다:
- 읽기 성능이 매우 중요한 시스템이다
- 도메인이 복잡하여 비즈니스 로직 분리가 절실하다
- 이벤트 소싱을 병행하고자 한다
- 읽기/쓰기 모델이 구조적으로 완전히 다르다
- 실시간 통계나 분석 조회가 빈번하다
그렇지 않다면 굳이 CQRS까지 가지 않아도 됩니다.
단순한 CRUD 서비스에서 CQRS는 복잡성을 도입하는 요인이 될 수 있습니다.
‘책임을 분리한다’는 의미
CQRS는 단순한 기술 트렌드가 아니라, 소프트웨어에서 읽기와 쓰기라는 본질적으로 다른 책임을 올바르게 분리하자는 설계 철학입니다.
그 철학이 잘 적용된 구조는 다음과 같은 결과를 줍니다:
- 도메인은 복잡한 로직에 집중할 수 있고
- 읽기는 사용자에게 최적화된 경험을 제공하며
- 시스템은 변화에 유연하게 대응할 수 있습니다
하지만 그만큼 아키텍처 설계, 이벤트 처리, Projection 모델링 등에서 선택과 집중이 필요합니다.
728x90