채움의 길
[DeepDive] CQRS와 Projection으로 보는 조회 모델의 진화 본문
파트 내용
- CQRS의 핵심 개념과 조회 분리의 필요성
- Projection: 조회 전용 모델의 설계 철학
- Eventual Consistency의 수용과 트레이드 오프
- 조회 요구사항에 따른 Projection 패턴 선택
1. CQRS의 핵심 개념과 조회 분리의 필요성
CQRS란
Command Query Responsibility Segregation, 시스템의 명령과 조회를 분리하는 설계 패턴
이 패턴의 핵심은 데이터 상태를 변경하는 연산(명령)과 데이터를 읽는 연산(조회)을 분리하여 각각 다른 모델을 사용하고, 경우에 따라서는 다른 데이터 저장소를 사용할 수 있도록 하는 것
- 명령(Command) : 데이터의 상태를 변경하는 연산, '상품 등록' 이나 '주문 취소'와 같은 쓰기 작업에 해당됨
비즈니스 규칙과 유효성 검사를 담당하며 주로 복잡한 도메인 로직을 처리하는 데 최적화됨 - 조회(Query) : 데이터의 상태를 변경하지 않고 단순히 조회하는 연산, '상품 목록 조회', '사용자 정보 조회'와 같이 읽기 작업에 해당됨
조회 모델은 복잡한 비즈니스 로직 없이 데이터 조회를 효율적으로 수행하는 데 초점을 맞춤
조회 분리의 필요성
주요 이뉴는 성능, 확장성, 그리고 복잡성 관리에 있음
- 성능 및 확장성 개선
- 대부분의 서비스에는 읽기 작업 > 쓰기 작업이 되는데, 조회 모델을 분리할 경우 읽기 작업에 특화된 데이터베이스 스키마와 캐싱 전략을 사용할 수 있어 성능 극대화 가능
- 여러 테이블을 조인하는 복잡한 쿼리 대신 단순한 형태로 데이터를 미리 구성해놓을 수 있음
- 읽기 전용 데이터베이스를 여러 대 복제하여 부하를 분산시키고 수평적 확장성을 확보할 수 있음
- 복잡성 관리
- 전통적인 CRUD 모델은 읽기와 쓰기 연산을 동일한 모델로 처리하는데, 이 과정에서 쓰기 연산을 위한 복잡한 도메인 모델과 조회 연산을 위한 단순한 데이터 구조가 혼재되어 모델이 복잡해질 수 있음
- 조회를 분리하게 되면 쓰기 모델은 오직 비즈니스 로직에만 집중하고, 조회 모델은 데이터 조회에만 집중할 수 있어 코드 복잡성을 줄이고 유지보수를 용이하게 만듦
- 읽기-쓰기 부하 불균형 해결
- 데이터 조회 요청이 데이터 변경 요청보다 훨씬 많은 현대 웹 서비스에서, 읽기와 쓰기의 데이터베이스를 동일하게 사용한다면 읽기 부하가 쓰기 부하에 요청을 미칠 수 있음
- CQRS는 읽기 전용 데이터 베이스를 사용하여 이런 부하 불균형을 해결하고, 각 작업에 최적화된 자원을 할당할 수 있게 함
2. Projection: 조회 전용 모델의 설계 철학
Projection의 설계 철학
Projection의 핵심 철학은 사용하는 데이터만 가져온다는 것, 이 철학은 ORM을 통해 엔티티 전체를 로드하는 방식과는 대비되는 부분이지만 아래와 같은 이점이 있음
- 성능 최적화
네트워크를 통해 전송되는 데이터 양이 줄고, 데이터베이스에서 불필요한 데이터를 읽는 부하가 감소하여 쿼리 속도가 빨라짐 - 메모리 효율
애플리케이션 서버의 메모리 사용량이 줄어듦 - 결합도 감소
조회 로직이 특정 엔티티의 모든 필드에 의존하지 않으므로, 엔티티 모델이 변경되어도 조회 로직에 미치는 영향이 최소화됨
이러한 이유로 인해 Projection은 1번에서 설명된 CQRS의 핵심 개념을 구현하는 데에 필수적인 기술이라고 함
* DTO, Projection, record의 관계
- Projection으로 데이터베이스에서 필요한 데이터만 가져옴
- record로 Projection으로 가져온 데이터를 담음 (간결하게 정의된 불변 객체 용도)
- DTO를 통해 계층 간 데이터를 전달
3. Eventual Consistency의 수용과 트레이드 오프
Eventual Consistency란
분산 시스템에서 데이터가 결국에는 일치하게 되는 일관성 모델
데이터가 업데이트되면 시스템의 모든 노드에 즉시 반영되지 않을 수 있지만, 일정 시간이 지나면 모든 노드의 데이터가 동일한 상태로 동기화되는 것
Eventual Consistency의 수용
- 즉각적인 일관성을 포기하는 대신 높은 가용성과 확장성을 얻고자 할 때 수용됨
- 읽기 작업이 쓰기 작업보다 훨씬 많은 경우
대부분의 서비스나 소셜 미디어 피드처럼 조회 요청이 압도적으로 많을 경우에 모든 쓰기 작업에 대해 즉각적인 동기화를 보장하는 것보다 분산된 노드에서 빠르게 읽기 작업을 처리하는 것이 효율적 - 실시간 동기화가 필수적이지 않은 경우
블로그 게시물 조회수, '좋아요' 수와 같이 약간의 지연이 사용자 경험에 큰 영향을 미치지 않는 데이터에 적합
트레이드 오프
- 일관성과 가용성/확정성
- 일관성(Consistency) 포기
데이터가 모든 노드에서 즉시 동일하지 않을 수 있으며, 업데이트 직후에 다른 노드에서 데이터를 조회하면 예전 데이터가 보일 수 있음 - 가용성 및 확장성 확보
동기화가 필요 없으므로 각 노드가 독립적으로 작동할 수 있어 시스템 전체의 가용성이 높아지고 노드를 쉽게 추가하며 확장할 수 있음
- 일관성(Consistency) 포기
- 개발 복잡성 증가
- 일관성이 깨지는 경우를 처리하기 위한 추가적인 로직(충돌 해결 또는 비동기 데이터 동기화)이 필요해 개발 복잡성이 높아짐
- 일관성 모델을 이해하고 비즈니스 요구사항에 맞게 설계해야 함, 금융 거래와 같이 데이터의 즉각적인 무결성이 중요한 시스템에는 이 모델이 적합하지 않을 수 있음
위와 같은 개념을 가지기 때문에 Eventual Consistency는 분산 시스템에서 필수적으로 고려해야 하는 중요한 개념이며, 특히나 CQRS와 같은 패턴을 적용하여 쓰기와 읽기 데이터 저장소를 분리할 때 자주 접하게 됨
4. 조회 요구사항에 따른 Projection 패턴 선택
클래스(DTO) 기반 Projection
- 특정 필드를 담을 객체를 미리 정의하는 방식
- 조회 결과를 담을 DTO 클래스를 만들고
- JPQL의 NEW 키워드를 사용하여 결과를 매핑
- Spring Data JPA를 이용한 DTO Projection (@Query)
- QueryDSL 사용
// DTO 클래스 정의
public class OrderSummaryDto {
private Long id;
private String orderStatus;
// 생성자
public OrderSummaryDto(Long id, String orderStatus) {
this.id = id;
this.orderStatus = orderStatus;
}
}
인터페이스 기반 Projection
- 조회에 필요한 getter 메소드만 정의된 인터페이스를 만들고, JPA가 런타임에 이 인터페이스를 구현하는 프록시 객체를 생성하여 데이터를 담아주는 방식
// 인터페이스 정의
public interface OrderSummary {
Long getId();
String getOrderStatus();
}
// JPA Repository 메소드
// List<OrderSummary> findByCustomerId(Long customerId);
동적 Projection (Tuple 기반)
- 결과 타입이 정해져 있지 않은 동적 쿼리에 적합함
- Tuple 타입을 사용하여 JPQL이나 QueryDSL을 사용하여 조회 결과를 Tuple 객체에 담아 반환
- 조회 필드가 자주 변경되거나 미리 정의된 DTO가 없을 때 유용
// JPQL 쿼리 예시
// SELECT o.id, o.orderStatus FROM Order o
TypedQuery<Tuple> query = em.createQuery("...", Tuple.class);
// 쿼리 실행 후 값 접근
Tuple tuple = query.getSingleResult();
Long id = tuple.get(0, Long.class);
| 방식 | 장점 | 단점 |
| 클래스 기반 | 타입 안전성, 높은 가독성/유지보수성 | 반복적인 코드 |
| 인터페이스 기반 | 코드 간결성, 직관성 | 제한적인 기능, 복잡한 쿼리 |
| 동적 기반 | 유연성 | 낮은 타입 안전성, 높은 코드 복잡성 |
이와 같은 이유로 인해 사실상 실무에서는 클래스(DTO) 기반의 방식을 가장 많이 사용하고, 동적(Tuple) 기반의 방식은 거의 사용하지 않는다고 함
대부분 운영되는 서비스는 정적인 Projection을 사용하기 때문에 타입 안전성과 높은 가독성을 확보하는 것이 훨씬 이득이기 때문
따라서 동적 Projection은 특정된 예외적인 상황에서만 사용되는 특수 목적의 기술이라고 볼 수 있다고 함
'지식 채우기 > 동아리' 카테고리의 다른 글
| [DeepDive] Circuit Breaker의 목적과 동작 방식 알아보기 (0) | 2026.01.07 |
|---|---|
| [DeepDive] 시스템이 이벤트를 이해하는 방식 (0) | 2025.10.28 |
| [DeepDive] 동시성 제어 기법을 알아보자 (2) | 2025.09.27 |