225. CQRS (Command Query Responsibility Segregation) - 명령과 조회 모델 분리 데이터베이스 읽기 쓰기 분리 이벤트 소싱 성능 최적화 마이크로서비스

핵심 인사이트: 인터넷 쇼핑몰 데이터베이스(엑셀 파일 1개)가 하나 있다. 이 파일에 직원이 "새 상품 등록(Write)"을 하느라 무거운 자물쇠(Lock)를 걸어버렸다. 그 찰나의 순간, 전국에서 10만 명의 고객이 "이 상품 보여주세요!(Read)" 라며 조회를 미친 듯이 때렸다. DB는 자물쇠 때문에 조회를 다 막아버렸고, 쇼핑몰은 응답 지연으로 폭발했다. 개발자가 빡쳐서 책상을 엎었다. "야!! 쇼핑몰 트래픽의 99%는 고객이 물건을 '눈으로 보는(Read)' 트래픽이고, 관리자가 물건을 '등록/수정(Write)'하는 트래픽은 1%밖에 안 되잖아! 왜 이 두 개를 엑셀 파일 1개(DB)에서 같이 하려고 해!! 당장 엑셀 파일을 두 개로 찢어버려!! 1번 파일은 관리자 전용 '쓰기(Write) 전용 DB'로 두고, 2번 파일은 전 세계 고객이 미친 듯이 긁어가는 '읽기(Read) 전용 DB'로 완벽하게 찢어서 분리해(CQRS)!! 그럼 관리자가 1번에서 등록을 하든 말든, 10만 명은 2번 파일에서 번개처럼 상품을 볼 수 있잖아!!" 읽기와 쓰기의 운명을 두 동강 내버린 클라우드 시대의 궁극 최적화 마법, CQRS다.

Ⅰ. 통짜(단일) 데이터베이스의 참혹한 병목 현상

  • 과거(CRUD 아키텍처)에는 회원 DB 테이블 하나에다가 새로운 회원을 추가(Insert/Write)하는 일과, 회원의 목록을 검색(Select/Read)하는 일을 다 같이 했습니다.
  • 딜레마: 데이터를 **'쓸 때'**는 엄격한 무결성(자물쇠)이 중요해서 구조가 복잡해야 하고, 데이터를 **'읽을 때'**는 여러 테이블을 조인(Join)해서 한눈에 봐야 하니 구조가 넓적해야 빠릅니다.
  • 하나의 DB 구조로 이 상충하는 두 마리 토끼를 다 잡으려다 보니 속도도 느려지고 병목(Lock 대기)이 터져버립니다.

Ⅱ. CQRS (명령과 조회의 책임 분리)의 개념 🌟

  • 개념: 버트란드 마이어(Bertrand Meyer)의 CQS 원칙에 기원하여 그렉 영(Greg Young)이 명명한 패턴.
  • 시스템을 데이터의 상태를 변경하는 명령(Command = Write/Update/Delete) 모델과, 데이터의 상태를 화면에 보여주기만 하는 조회(Query = Read) 모델로 철저하게 두 동강 내어, 로직과 데이터베이스(DB) 자체를 아예 물리적으로 분리해 버리는 극단적 성능 최적화 아키텍처입니다.

Ⅲ. CQRS를 완성하는 3단계 진화 모델 🌟 핵심 기출 🌟

얼마나 독하게 찢을 것인가에 따라 레벨이 다릅니다.

1단계: 코드(로직)만 분리

  • DB는 1개만 씁니다. 하지만 자바(Java) 코드에서 쓰는 통로를 WriteService 파일과 ReadService 파일로 완전히 쪼갭니다. 가장 소극적이고 쉬운 분리입니다.

2단계: DB 물리적 분리 (Master-Slave 복제) 🌟 대세 🌟

  • 가장 많이 쓰는 실무 패턴입니다. 쇳덩어리 DB 자체를 2대로 찢습니다.
  • 명령(Command) DB (Master): 쓰기 전용입니다. 오라클 같은 무겁고 튼튼한 관계형 DB를 씁니다. 관리자가 물건을 여기에 등록합니다.
  • 조회(Query) DB (Slave/Replica): 읽기 전용입니다. Elasticsearch나 MongoDB처럼 읽기 속도가 빛의 속도인 NoSQL DB를 씁니다. 10만 명의 트래픽은 모조리 여기로 꽂혀서 검색을 처리합니다.

3단계: 동기화의 흑마법 (이벤트 브로커 연결) 🌟

  • DB를 2개로 찢어놨으니, 관리자가 1번(쓰기 DB)에 새 옷을 등록하면 2번(읽기 DB)에도 그 옷이 복사되어야 고객이 쇼핑을 하겠죠?
  • 관리자가 1번 DB에 글을 쓰는 순간, 214번에서 배운 **이벤트 드리븐(Kafka 메시지 큐)**으로 2번 DB에 비동기 쪽지를 휙 던집니다. 2번 DB는 쪽지를 주워다가 자기 창고에 빛의 속도로 똑같이 복사해 놓습니다. (최종적 일관성, Eventual Consistency 달성)

Ⅳ. 도입 시 장점과 치명적 단점

  • 장점 (극강의 확장성과 속도): 읽기 트래픽이 100만 배 폭주하면 쓰기 서버는 냅두고 읽기 서버(Read DB)만 100대로 쫙 스케일 아웃(복제)하면 됩니다. 또한 읽기 DB는 조인(Join) 따위 안 하도록 이미 다 펼쳐진 엑셀(비정규화)로 덤프를 떠놔서 검색 속도가 타의 추종을 불허합니다.
  • 단점 (최종적 일관성의 저주): 쓰기 DB에서 데이터가 바뀌고 읽기 DB로 복사되는 데 약 0.1초의 물리적 지연(Delay)이 무조건 생깁니다. 만약 고객이 돈을 충전(Write)하고 0.001초 만에 잔고 조회(Read)를 눌렀는데, 아직 복사가 안 돼서 '0원'이라고 뜨면 고객 멘탈이 나갑니다(Sync 꼬임). 이 미묘한 시간 차이를 감당할 수 있는 시스템에만 써야 하는 양날의 검입니다.

📢 섹션 요약 비유: 기존의 단일 DB(CRUD) 시스템'하나의 거대한 장부(장부장)'에 식당의 모든 재료 입고(쓰기)와 메뉴판 출고(읽기)를 동시에 적고 읽는 끔찍한 병목 공간이었습니다. 창고 직원이 새 고기를 장부에 적으려고 펜(Lock 자물쇠)을 쥐고 있는 1분 동안, 홀 서빙 직원 100명은 메뉴판을 보지 못해 뒤에 서서 발을 동동 구르며 멍때리고(대기열 폭발) 서 있어야 했습니다. 이를 부숴버린 CQRS 패턴은 이 장부를 무참하게 두 개로 찢어버린 혁명입니다. **1번 장부(명령 Command DB)**는 오직 주방장만 만지는 '재료 입고 전용 비밀 장부'입니다. 고기를 꼼꼼하고 안전하게 적습니다. 반면 홀 벽면에는 **2번 장부(조회 Query DB)**인 거대한 전광판 메뉴판을 수십 개 달아놓았습니다. 손님 10만 명은 주방장이 1번 장부에 글을 쓰든 말든 상관없이, 전광판(2번)만 보고 1초 만에 번개처럼 주문(읽기)을 때립니다. 주방장이 1번 장부에 신메뉴를 적으면 0.1초 뒤 전광판에 스르륵 복사되어 켜집니다(비동기 동기화). 읽는 놈과 쓰는 놈이 아예 물리적으로 눈도 마주치지 못하게 분리하여, 서로의 작업이 1%의 간섭도 없이 우주 끝까지 성능을 뽑아내는 궁극의 데이터 분리 마술입니다.