개발/Java & Kotlin

[Spring] CQRS 패턴이란?

devhooney 2025. 3. 23. 13:28
728x90

 

 

1. 개념

시스템은 크게 상태 변경과 조회 기능을 제공한다. 주문 취소, 결제 기능은 상태 변경에 해당되며, 주문서 조회, 사용자 조회 등이 조회에 해당된다. 명령 쿼리 책임 분리 패턴(Command Query Responsibility Segregation, CQRS) 는 상태를 변경하기 위한 명령을 위한 모델과 상태를 제공하는 조회(Query)를 위한 모델을 분리하는 패턴을 의미한다. 예를 들어, Order라는 리소스를 Order(명령용), OrderData(조회용) 2개의 모델로 나누어서 관리할 수 있다. 이때 OrderData를 이용해서 표현 계층에 데이터를 출력하는 데 사용하고, 애플리케이션에서는 Order를 활용해 변경을 수행할 수 있다.

 

- 자세히 !

CQRS 패턴의 주요 개념
1. 명령(Command):
상태 변경을 수행하는 작업을 처리하는 부분입니다.
예를 들어, 주문 취소, 결제와 같이 데이터의 상태를 변경하는 작업이 여기에 포함됩니다.
명령은 데이터의 변경을 트리거하며, 일반적으로 쓰기 작업에 관련된 API가 이에 해당합니다.


2. 조회(Query):
데이터 조회 작업을 수행하는 부분입니다.
예를 들어, 주문서 조회, 사용자 정보 조회와 같은 읽기 작업입니다.
조회는 데이터를 읽기만 하고 변경하지 않으며, 성능 최적화와 관련된 여러 기술을 적용할 수 있습니다.


CQRS의 두 가지 모델
1. 명령 모델 (Command Model):
이 모델은 상태 변경과 관련된 데이터를 처리합니다. 예를 들어, 주문 상태를 변경하거나, 결제를 처리하는 데 사용됩니다.
모델은 일반적으로 도메인 객체(예: Order)를 포함하며, 비즈니스 로직을 처리하는데 초점을 맞춥니다.

 

2. 조회 모델 (Query Model):
이 모델은 데이터 조회와 관련된 작업을 처리합니다. 예를 들어, 사용자가 주문을 조회할 때 필요한 데이터를 제공합니다.
조회 모델은 성능과 관련된 최적화를 많이 고려하여 데이터를 조회하는 데 필요한 필드를 포함합니다.
조회 모델은 데이터를 효율적으로 읽기 위해 필요한 형태로 최적화됩니다. 예를 들어, 복잡한 조인이 필요하지 않거나, 여러 읽기 최적화 기법이 적용될 수 있습니다.

 

 

 

728x90

 

 

 

2. CQRS 패턴의 장단점

CQRS 패턴을 따르면, 소프트웨어의 유지보수성을 높일 수 있다. 그리고, 모델별로 성능이나 요구사항에 맞는 데이터베이스나 데이터 접근 기술을 사용할 수 있다.

예를 들어, 명령 모델은 트랜잭션이 지원되는 RDB를 사용하고, 조회 모델은 조회 성능이 높은 NoSQL을 사용할 수 있다. 단, 해당 방식은 명령 모델의 변경을 조회 모델로 전파하여 동기화시켜야 할 필요가 있을 수 있다. 또 다른 예시로, 단일 데이터베이스의 테이블에 대해 CQRS 패턴을 사용한다고 가정했을 때는 명령 모델은 도메인 모델을 구현하는데 유리한 JPA를 사용하고, 조회 모델에 대해서는 SQL 데이터 조회에 유리한 MyBatis를 사용할 수 있다.

하지만, CQRS 패턴은 구현 코드가 많고, 더 많은 구현 기술이 필요하다는 점이 단점. 따라서 단일 모델을 사용할 때 발생하는 복잡함 때문에 발생하는 구현 비용과 조회 전용 모델을 만들 때 발생하는 복잡함 때문에 발생하는 구현 비용을 비교해서 신중하게 도입을 결정해야 한다.

 

 

예시

// 명령 모델
public class Order {
    private Long orderId;
    private OrderStatus status;
    private Date createdAt;

    public void cancelOrder() {
        // 주문 취소 로직
        this.status = OrderStatus.CANCELLED;
    }

    // 기타 상태 변경 로직들...
}




// 조회 모델
public class OrderData {
    private Long orderId;
    private String customerName;
    private String orderStatus;
    private Date createdAt;

    // 조회 전용으로 데이터만 제공
}

 

728x90