CQRS 패턴에 대해 알아보자

CQRS (Command and Query Responsibility Segregation)

CQRS 패턴이란, 데이터 저장소로부터의 명령와 조회 작업을 분리하는 패턴을 말한다.

그렇다면, 명령과 조회는 무엇이고 명령과 조회는 왜 분리해야할까?

명령과 조회

데이터베이스에는 생성(Create), 조회(Read), 갱신(Update), 삭제(Delete)할 수 있다. 여기에서, 조회를 제외한 생성, 갱신, 삭제가 명령이다.

명령과 조회를 구분하는 이유는, DB로부터 데이터를 읽어오고 처리하게 되면, 이미 그 사이에 데이터가 변경되어있을 가능성이 높다.

CQRS는 Read와 CUD 사이에는 딜레이가 존재할 수 있음을 인정하는 것이다. 즉, 명령와 조회를 구분함으로써 얻는 이점을 설명하는 것이 바로 CQRS 패턴이다.

명령과 조회를 분리해야하는 이유

  1. 성능 최적화

데이터를 읽고 쓰는 방식은 서로 다를 수 있다. 쓰기 작업(명령)은 트랜잭션, 데이터의 무결성, 순차적 로깅 등이 중요할 수 있고, 읽기 작업(조회)은 응답 속도와 효율성이 더 중요할 수 있다. 각각에 최적화된 데이터 스토리지 및 처리 방법을 사용하여, 즉 명령와 조회륿 분리함으로써 성능을 최적화할 수 있다.

  1. 유지보수 향상

명령과 조회를 명확히 구분함으로써 각 부분의 코드를 독립적으로 관리할 수 있다. 이는 유지보수를 향상시킬 수 있다.

  1. 확장성 향상

명령과 조회의 부하가 서로 다른 서버 또는 리소스에 분산되어 시스템의 전체적인 확장성이 향상될 수 있다. 예를 들어서, 조회 작업이 많은 시스템에서는 쿼리 모델을 복제하여 부하를 분산시킬 수 있고, 쓰기 작업이 많은 경우에는 명령 모델의 리소스를 강화할 수 있다.

명령 DB와 조회 DB

image CQRS 패턴을 사용하기 위해서는 명령 DB와 조회 DB를 나누어 사용한다. API와 같은 트리거를 통해 DB에 명령이 들어오면, 명령 DB에서 해당 이벤트를 처리하고, 조회 DB는 데이터를 업데이트하는 이벤트가 트리거된다.

이러한 방식을 사용함으로써, 명령 DB에서는 쓰기에 최적화하여 트랜잭션의 일관성과 무결성을 유지하는데 중점을 두어 모델을 형성하게 된다. 또한 데이터 중복을 방지하고 데이터 무결성을 위하여 정규화될 가능성이 높다. 읽기 DB는 데이터 조회의 성능을 최적화하기 위해 비정규화된 형태로 데이터를 저장할 수 있다.

예를 들어, 온라인 사이트에서 고객의 주문 처리 시스템을 설계한다고 할 때, 쓰기 디비는 주문 데이터의 일관성과 정확한 처리를 위해 정규화된 형태로 구성될 수 있다. 반면 읽기 디비는 고객에게 주문 내역을 빠르게 보여주기 위해 주문 정보, 고객 정보, 상품 상세를 한 뷰로 하여 비정규화된 형태로 저장할 수 있다.

CQRS 패턴의 단점

명령 DB와 조회 DB 간의 데이터 동기화하는 작업이 복잡할 수 있고, 동기화 지연으로 인해 데이터 일관성을 해칠 수 있다.

CQRS 패턴 적용

  1. 대규모 분산 시스템

읽기와 쓰기 작업이 명확히 구분되어 있는 대규모 애플리케이션에서 CQRS를 통해 성능과 확장성을 크게 향상시킬 수 있다.

  1. 읽기와 쓰기 비율이 크게 다른 시스템

읽기 작업이 쓰기 작업보다 훨씬 빈번하거나 그 반대인 경우, CQRS를 통해 각 작업을 독립적으로 최적화하고 관리할 수 있다.

CQRS 가 실제로 적용된 서비스에 대해 알아보자

MSA에서 CQRS를 사용할 경우

MSA 아키텍처와 같은 분산 서비스에서 CQRS 패턴을 사용할 경우, 어떻게 활용할 수 있을지에 대해 이야기해보자!

예를 들어서, 판매 서비스와 반품 서비스가 있다고 가정해보자. 판매DB는 고객들의 판매 정보를 저장하고, 반품DB에서는 판매된 데이터에 대하여 반품이 발생했을 경우의 데이터를 저장한다. 그리고 판매DB와 반품DB를 JOIN하여 각 업체에게 정산하는 정산 서비스가 있다. 판매 서비스와 반품 서비스는 서로 독립된 서비스이기 때문에 두 DB를 JOIN할 수 없다. 그렇다면 정산 서비스에서는 어떻게 두 DB를 JOIN하여 사용할 수 있을까?

image

이러한 경우에는 판매DB와 반품DB를 조인하여 정산에 필요한 데이터를 따로 저장하는 정산DB를 새로 만드는 것이다. 그리고 CUD를 사용하는 DB에서 데이터 변환이 일어날 때마다 Data Changed를 감지하여 변경된 데이터를 정산 DB에도 반영하는 것이다! 이러한 아키텍처를 사용하면, 각 서비스를 독립적으로 사용하면서도 새로운 Read용 DB를 생성함으로써 CQRS 패턴도 함께 적용할 수 있다.