554. CQRS (명령과 조회 책임 분리) - 쓰기 DB와 읽기 DB 분리, 동기화 문제 해결
핵심 인사이트 (3줄 요약)
- 본질: CQRS(Command and Query Responsibility Segregation)는 시스템의 뇌를 세로로 반갈죽하여, 상태를 바꾸는 무거운 **'쓰기(Command) API/DB'**와 상태를 그저 화면에 뿌려주기만 하는 가벼운 **'읽기(Query) API/DB'**를 물리적으로 완전히 찢어버리는 극강의 분할 아키텍처다.
- 가치: 일반적인 서비스 트래픽의 90%는 '조회(Read)'고 단 10%만이 '쓰기(Write)'다. 1통짜리 DB에 이 둘을 섞어 쓰면 블랙프라이데이 때 조회 폭주로 락(Lock)이 걸려 결제(쓰기)가 뻗는 참사가 터진다. CQRS는 읽기 전용 DB(Redis/NoSQL)를 100개로 무한 복제(Scale-out)하여 10만 조회를 방어하고, 쓰기 DB는 쾌적하게 비워두는 압도적 조회 성능과 결제 가용성 방어망을 완성한다.
- 융합: 쓰기 DB에 들어간 돈이 읽기 DB로 복제되는 '0.1초의 시차(Eventual Consistency)'를 타협해야 하며, 이 동기화 찰나의 틈을 메우기 위해 카프카(Kafka) 비동기 브로커 및 **이벤트 소싱(555장)**이라는 클라우드 네이티브 블록체인 사상과 100% 한 몸으로 융합되어 진화한다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: CQRS는 영어 길어서 쫄 필요 없다.
- Command (명령): "돈 넣어라, 비번 바꿔라" ➡ DB 상태를 바꾸는(Write, Update, Delete) 묵직한 삽질.
- Query (조회): "내 잔고 얼마냐?" ➡ DB 상태를 건드리지 않고 눈으로 보기만 하는(Read) 깃털 같은 행위.
- 책임 분리 (RS): 이 삽질과 눈팅을 똑같은 1개의 오라클(Oracle) DB 서버에서 처리하지 말고, 아예 DB를 2개로 찢어서 "삽질 전용 DB", "눈팅 전용 DB"로 역할을 완벽히 나누자(Segregation)는 흑마법이다.
-
필요성: 쿠팡 같은 이커머스에서 고객들은 물건을 사기(Write) 전에 페이지 새로고침과 검색(Read)을 100번씩 누른다. 트래픽 비율이 99(Read) : 1(Write)이다. 그런데 옛날엔 통짜 DB 1개로 이걸 다 받았다. 10만 명이 검색(SELECT)을 쾅쾅 때리니까 DB CPU가 100% 치솟아서, 정작 물건을 사려는 1명의 귀중한 결제(UPDATE) 쿼리가 타임아웃 렉에 걸려 뻗어버렸다(Lock Contention). **"수만 명의 윈도우 쇼핑(조회) 트래픽이, 회사 돈줄인 단 1명의 결제(쓰기) 트래픽의 목을 조르는 비극을 막기 위해 뼈와 살을 분리하는 외과 수술"**이 CQRS다.
-
💡 비유: CQRS 안 하는 옛날 시스템은 **'카운터 1개짜리 동네 김밥천국'**입니다. 아줌마 한 명이 카운터에서 요리 주문(Command)도 받고, "화장실 어딨어요?(Query)" 질문도 100번씩 받습니다. 질문하려는 사람 줄이 길어지면, 정작 10만 원어치 결제하려는 손님이 빡쳐서 나갑니다. CQRS는 **'대형 종합 병원의 원무과와 안내 데스크의 분리'**입니다. 돈 내고 입원 수속(Command) 하는 무거운 업무는 원무과 창구(쓰기 DB)에서만 은밀하게 1:1로 처리하고, "화장실 어딨어요? 1번 진료실 어디에요?(Query)" 같은 가벼운 질문은 로비 한가운데 세워둔 AI 터치스크린 안내판 10대(읽기 복제 DB들)가 다 튕겨내 버려 원무과의 숨통을 완벽히 틔워주는 분업술입니다.
-
등장 배경 및 발전 과정:
- CRUD의 한계 (전통적): 개발자들은 숨 쉬듯 C(쓰기), R(읽기), U/D(쓰기)를 하나의 덩어리로 짰다. 조회할 때마다 JOIN 5개를 묶어 때리니 1명 조회에 3초씩 걸렸다.
- Master-Slave DB 복제 (과도기): "조회 트래픽이 빡세네? 읽기 전용 복제본(Read Replica) DB를 두자!" 이 정도까진 CQRS가 아니다. 그냥 DB만 2대인 거다. 여전히 테이블 구조가 똑같아서 JOIN 지옥은 그대로였다.
- CQRS 패턴 탄생 (Greg Young 창시): "야, 어차피 읽기 전용 DB 띄울 거면, 아예 테이블 구조도 화면에 뿌리기 좋게(NoSQL, JSON) 찰흙처럼 뭉개서 미리 저장해 둬! 그럼 JOIN 1도 없이 0.001초 만에 튕겨내잖아!" 극단의 퍼포먼스 최적화 사상으로 진화했다.
-
📢 섹션 요약 비유: 옛날 CRUD 방식은 손님이 피자를 시킬 때마다 주방장이 도우를 반죽하고 굽는 **'수제 피자집(매번 JOIN 연산)'**입니다. 1명에 20분 걸립니다. CQRS는 **'맥도날드 피자 뷔페'**입니다. 주방(쓰기 DB)에서 피자를 굽자마자 뷔페 진열대(읽기 DB)에 쫙 깔아둡니다. 손님(조회 트래픽) 수만 명이 몰려와도 주방장한테 말 안 걸고 뷔페 진열대에서 피자(이미 완성된 JSON 데이터)만 0.1초 만에 쏙쏙 집어가면 끝나는, 무자비한 대량 조회의 치트키입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
1. CQRS의 아키텍처 뼈대 (3단계 동기화 릴레이)
어떻게 읽기와 쓰기를 완전히 찢어놓고 데이터를 맞추는가?
[ 🛡️ 완벽히 분할된 2개의 우주 ]
- Command (쓰기/삽질): 유저가
결제 API를 쏜다. **쓰기 전용 DB(Oracle)**에 데이터를 쾅 넣는다. - Event 동기화 (Kafka): 쓰기 DB에 데이터가 꽂히는 찰나의 순간, "나 결제됐음!" 이벤트 메시지 1방이 카프카(Kafka) 큐를 타고 0.01초 만에 날아간다.
- Query (읽기/눈팅): 카프카 반대편에 있는 **읽기 전용 DB(Redis/Elasticsearch)**가 그 메시지를 줍고 자기 메모리에 업데이트한다. 유저 10만 명이
잔고 조회 API를 미친 듯이 쏜다. 이 트래픽은 쓰기 DB를 쳐다보지도 않고, 빛의 속도로 Redis(읽기 DB)에 박혀있는 완성된 JSON 텍스트 쪼가리만 10만 번 튕겨낸다.
2. CQRS를 완성하는 마술: Materialized View (구체화된 뷰)
읽기 DB(Query 모델)는 절대 쓰기 DB(Command 모델)의 테이블 구조를 복사해 오지 않는다.
-
문제 (JOIN 지옥): 내 주문 내역 화면을 보여주려면
회원 정보 + 주문 내역 + 상품 정보 + 배송 정보테이블 4개를 JOIN 해야 한다. 유저 10만 명이 새로고침 누를 때마다 저 무거운 JOIN을 10만 번 돌리면 DB 램(RAM)이 불타버린다. -
해결 (CQRS 읽기 DB의 JSON 뭉개기): CQRS의 읽기 DB(Redis/MongoDB) 안에는 저 4개 테이블이 없다! 오직 "화면에 보여줄 통짜 HTML/JSON 데이터" 1덩어리로 아예 미리 뭉개져서(Denormalization) 캐싱되어 있다. (이걸
Materialized View라 부름). 유저가 10만 번 조회 버튼을 누르면 JOIN 연산(CPU 낭비)은 단 1번도 발생하지 않고 0.001초 컷으로 미리 구워진 피자(View)만 반환한다. 조회 퍼포먼스의 신세계가 열린다. -
📢 섹션 요약 비유: 쓰기 DB(Command)는 쌀, 김, 단무지가 완벽하게 칸칸이 나뉘어 저장된 **'정규화된 마트 진열장'**입니다. 무결성이 100점이죠. 하지만 여기서 김밥 한 줄을 달라고 하면 3곳에서 재료를 모아오느라(JOIN) 5분이 걸립니다. 읽기 DB(Query 모델)는 미리 김밥을 수천 줄 말아놓은 **'편의점 삼각김밥 매대(Materialized View)'**입니다. 밥, 김이 하나의 덩어리로 합쳐져(비정규화) 있기 때문에 손님이 몰려와도 1초 만에 쏙쏙 집어서 나갈 수 있는, 속도(조회)에 몰빵한 변종 데이터베이스입니다.
Ⅲ. 융합 비교 및 다각도 분석
1. 일반 Master-Slave 복제 vs 진성 CQRS 의 차이점
초보 개발자 면접 단골 질문. "읽기/쓰기 찢은 건 DB Master-Slave(리플리케이션)랑 똑같은 거 아님?"
| 척도 | 1. DB Master-Slave (단순 복제) 😅 | 2. CQRS 아키텍처 👑 |
|---|---|---|
| 물리적 분리 | DB 2대 (Master 쓰기, Slave 읽기) | API 레벨(백엔드 앱 소스코드)부터 DB까지 전부 2단 분리 |
| 테이블 구조 | Master와 Slave의 스키마(테이블 모양)가 100% 동일함. | 쓰기 DB(정규화 RDBMS)와 읽기 DB(비정규화 NoSQL) 모양이 완전 다름. |
| 성능 한계 | Slave를 10대 늘려도, 결국 조회 시 무거운 JOIN 쿼리는 똑같이 쳐야 함 (성능 저하). | 미리 다 합쳐둔 JSON 뭉탱이를 반환하므로 JOIN 연산 자체가 지구상에서 소멸(초광속). |
| 복잡도 | 1시간이면 인프라팀이 뚝딱 세팅함 (쉬움). | 개발자가 Event 비동기 카프카 코드 치느라 피눈물 흘리며 1달 걸림 (어려움). |
과목 융합 관점
-
클라우드 컴퓨팅 (Eventual Consistency, 결과적 정합성 타협): CQRS의 가장 잔혹한 대가다. 유저가 글을 썼다(Command). 그 글이 카프카를 타고 날아가 읽기 DB(Query)에 꽂히기까지 0.1초의 틈이 생긴다. 유저가 글 쓰자마자 새로고침을 F5 눌렀는데 자기 글이 안 보이는 더티 리드(Dirty Read) 버그가 100% 터진다. 아키텍트는 "이 찰나의 데이터 불일치(BASE 사상)를 허용할 수 있는가?"를 따져야 한다. 은행 결제 창구에 CQRS 바르면 큰일 나지만, 인스타 게시물이나 유튜브 댓글 달기는 1초 늦게 보여도 아무도 안 죽는다. 이 타협(Trade-off)을 받아들여야만 10만 TPS의 속도 신세계를 누릴 자격이 있다.
-
분산 데이터베이스 (Polyglot Persistence): "폴리글랏 퍼시스턴스". 쓰기 전용 Command DB는 절대 에러가 나면 안 되니 무결성 철통방어 **Oracle/MySQL (RDBMS)**을 쓴다. 읽기 전용 Query DB는 조회가 미친 듯이 빨라야 하고 검색어 텍스트를 마구 던질 수 있어야 하니 **Elasticsearch 나 Redis (NoSQL)**를 쓴다. 하나의 서비스에 2개의 완전히 다른 철학을 가진 DB 엔진을 양손에 쥐고 극한의 효율을 뽑아 먹는 MSA 아키텍처의 정수다. (533장 연계)
-
📢 섹션 요약 비유: Master-Slave가 **'원본 교과서를 복사기로 10권 복사해 두는 짓'**이라면, CQRS는 **'원본 교과서(쓰기 DB)를 읽고 핵심만 예쁘게 요약한 단원별 암기 노트(읽기 DB)를 따로 10권 만들어두는 천재적인 짓'**입니다. 복사본은 학생이 시험공부 할 때 교과서를 일일이 뒤져야 하는 수고(JOIN)가 똑같지만, 암기 노트(Materialized View)는 그냥 눈으로 1초만 스캔하면 정답이 팍 튀어나오는, 극강으로 최적화된 조회 치트키입니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — CQRS '결과적 정합성' 버그로 인한 CS(고객 클레임) 폭주: 쇼핑몰 장바구니에 CQRS를 발랐다. 고객이 '사과'를 장바구니에 담았다(Command API 성공). 그리고 고객이 0.1초 만에 뒤로 가기를 눌러 장바구니 화면을 봤다(Query API 호출). 그런데 카프카 동기화가 0.5초 밀려서 장바구니 뷰(Redis)에 사과가 아직 안 들어왔다! 고객은 "어? 담기 버튼 안 눌렸나?" 하고 3번을 더 담았다. 5초 뒤 장바구니에 사과 4개가 찍혔다. 회사가 사기꾼으로 몰렸다.
- 아키텍트의 해결책: 프론트엔드 UX(사용자 경험) 타협 및 캐싱 트릭 장착이다. 아키텍트는 백엔드 단에서 이 0.1초 시차(Eventual Consistency)를 100% 막을 순 없다. 대신 프론트엔드/앱 개발자에게 꼼수를 오더한다. "고객이 사과 담기 성공하면, 백엔드 읽기 DB(조회)를 다시 호출하지 말고, 안드로이드 앱 내부 로컬 메모리 배열에 임시로 찰흙(사과 이미지)을 덮어 씌워서 그냥 3초 동안 보여줘 놔라(Optimistic UI Update)." 3초 뒤엔 카프카가 무조건 읽기 DB 동기화를 끝내놓을 테니, 다음번 조회 때는 실제 완벽한 데이터가 끌려온다. 물리적 딜레이의 구멍을 눈속임 UX 마술로 덮어버리는 융합 방어술이다.
-
시나리오 — 조회(Query) DB의 100% 메모리 붕괴(OOM)와 동기화 대재앙: 커뮤니티 앱에 CQRS 읽기 DB로 Elasticsearch(ES)를 깔았다. 1년 뒤 데이터가 10억 건 쌓였다. 카프카가 "수정됨!" 이벤트를 미친 듯이 쏴대며 ES의 도큐먼트(View) 1억 개를 갈아엎다 보니 ES 서버 메모리가 빵 터져 죽어버렸다(OOM). ES가 10분간 뻗었다 다시 켜졌다. 자, 이제 카프카에 밀려있던 지난 10분 치의 동기화 큐 수십만 건을 뻗었던 ES가 어떻게 유실 없이 멱살 잡고 안전하게 다시 받아먹어 복구(Re-sync)할 것인가?
- 아키텍트의 해결책: 이벤트 소싱(Event Sourcing) 기반의 무한 재생(Replay) 아키텍처 융합이다. 일반적인 CQRS는 카프카 메시지 1번 놓치면 영영 짝짝이 장부가 되어버리는 극악의 리스크를 안고 뛴다. 이걸 막기 위해선 쓰기 DB 자체를 **이벤트 소싱 장부(555장)**로 짜야 한다. 만약 조회 DB(ES)가 완전히 박살 나도 걱정 없다. 그냥 ES 서버 깨끗이 포맷하고 새로 띄운 다음, 쓰기 DB에 쌓여있던 과거 1년 치의 [이벤트 로그 10억 건]을 1번부터 10억 번까지 미친 듯이 0.1초 단위로 재생(Replay)시켜 버리면, 완벽하게 정합성이 100% 맞는 갓 구운 피자(Materialized View)로 1시간 만에 부활하는 궁극의 재해 복구(DR) 파이프라인이 완성된다.
도입 체크리스트
- 비즈니스적: "이 도메인이 진짜로 (읽기 ト래픽 : 쓰기 트래픽) 비율이 100 대 1이 넘어가는 극한의 조회 병목 코어인가?" 쿠팡의 상품 전시 페이지(조회 10만, 구매 1명)나 네이버 카페 메인 화면은 CQRS가 없으면 1분 만에 뻗는다. 하지만 사내 인트라넷 ERP의 직원 근태 입력 화면은? 하루에 직원 100명이 1번씩 읽고 쓴다 (비율 1:1). 이런 쩌리 화면에 CQRS 이벤트를 발랐다간 "김 대리가 결재 올렸는데 부장님 화면엔 1분 뒤에 뜬대요! 에러 아니에요?"라는 멍청한 클레임 잡느라 데브옵스 팀원 연봉만 낭비된다. 트래픽 비율의 냄새를 맡고 도끼(CQRS)를 댈 곳과 안 댈 곳을 가려내는 게 아키텍트의 눈이다.
- 기술적: 두 시스템(Command/Query) 코드를 별개의 Git 저장소로 찢어내어 배포할 깡이 있는가? 완벽한 CQRS는 패키지 분리를 넘어 서버 통(Container) 자체를 찢어버린다. 개발자 A팀은 오직 "Write DB에 Insert 하는 Spring Boot 서버"만 짠다. 개발자 B팀은 오직 "Read DB 읽어다 뿌려주는 Node.js 서버"만 짠다. 한 도메인을 2개의 뇌로 찢어 개발하는 극한의 마이크로서비스 유지보수 헬파티를 견딜 조직적(DevOps) 성숙도가 담보되지 않으면 섣불리 건드려선 안 된다.
안티패턴
-
"CQRS 읽기 DB에 살짝살짝 직접 UPDATE(쓰기) 치는 비양심적 꼼수": "아오 ㅆㅂ 카프카 이벤트 기다리기 1초 딜레이 너무 짜증 나네. 내가 그냥 Query DB(Redis)에 강제로 UPDATE 쿼리 쳐서 당장 화면에 뜨게 조작할래 ㅋ" 개발자가 이딴 짓을 하는 순간 CQRS 아키텍처는 그날로 개박살이 난다. 쓰기 DB(진실의 원본)와 읽기 DB의 끈이 완벽히 끊겨 영원히 데이터 정합성을 맞출 수 없게 된다. "읽기 DB(Query Model)는 우주가 두 쪽 나도 오직 카프카가 물어다 주는 동기화 이벤트(Event Handler)에 의해서만 찰흙처럼 빚어지고 구워져야(Materialized) 하며, 인간이나 타 앱의 강제 쓰기 접근을 100% 철통 방어해야 한다."
-
📢 섹션 요약 비유: 조회 DB에 직접 쓰기(UPDATE)를 때리는 것은, **'은행원이 장부(쓰기 DB)에 기록도 안 하고, 고객한테 줄 복사본 암기 노트(읽기 DB)에만 볼펜으로 쓱쓱 1억 원을 적어주는 미친 사기 행각'**과 같습니다. 당장 눈앞의 고객 화면엔 1억 원이 찍히겠지만, 나중에 장부 감사를 돌리면 돈이 하나도 안 맞아 회사 문을 닫아야 합니다. 암기 노트는 반드시 원본 장부가 복사기(카프카)를 타고 흘러나온 잉크로만 적혀야 하는 절대 불변의 룰입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 1통짜리 Oracle DB로 CRUD 다 받아먹던 시절 (AS-IS) | Command/Query 서버 찢고 Kafka로 복제하는 CQRS (TO-BE) | 개선 효과 |
|---|---|---|---|
| 정량 | 수만 명 조회(SELECT)에 락 걸려 결제(UPDATE) 3초 타임아웃 뻗음 | 조회가 10만 건 폭주해도 결제 쓰기 서버 CPU 점유율 변동 0% | 읽기 부하 격리를 통한 코어 쓰기(결제/주문) 가용성 100% 무적 방어 |
| 정량 | 조회 시 JOIN 5번 치느라 1명당 쿼리 반환에 2.5초 소요 | JOIN 1도 없는 비정규화된 JSON 딱지 0.01초 컷 즉각 반환 | 캐싱 뷰(Materialized View) 기반 조회 렌더링 속도 200배 부스팅 |
| 정성 | "DB 튜닝 전문가(DBA) 님 제발 인덱스 좀 걸어주세요 ㅠㅠ" | "검색 엔진(Elasticsearch) 맘대로 달아서 텍스트 팍팍 검색할래!" | 복잡한 비즈니스 뷰 요구사항에 최적화된 NoSQL 기술 자율 스위칭(Polyglot) 확보 |
미래 전망
- Event Sourcing (이벤트 소싱) 체제와의 대통합 아키텍처 (CQRS/ES): 2025년 엔터프라이즈의 끝판왕 콤보다. (바로 다음 555장 내용). CQRS만 단독으로 쓰면 반쪽짜리다. 동기화가 끊겨 읽기 DB가 박살 났을 때 복구할 방법이 없기 때문이다. 따라서 쓰기 DB(Command)를 낡은 RDBMS의 UPDATE 방식이 아니라 "잔고 1만 원 올림", "잔고 5천 원 뺌"이라는 이벤트 이력 자체를 무한 저장하는 Event Store(Kafka 원장)로 갈아엎는다. 그리고 이 무한한 로그 기록 덩어리를 고속 재생(Replay)시켜 읽기 DB(조회 화면 뷰)를 수천 개씩 빵어내듯 무한 생성 복제해 내는 완벽한 데이터 무결점 파이프라인의 핵심 축으로 완전히 통합되고 있다.
- 클라우드 네이티브 실시간 스트리밍 DB의 출현 (ksqlDB, Flink): 개발자가 일일이 "카프카 이벤트 받아서 ➡ JOIN 하고 JSON 뭉개서 ➡ Redis에 밀어 넣는" 코레오그래피 코딩을 치다가 머리털이 다 빠졌다. 이젠 툴이 다 해준다. Apache Flink나 ksqlDB 같은 실시간 스트리밍 인프라는 카프카 큐 위로 날아다니는 메시지들을 공중에서 즉각 낚아채 실시간으로 JOIN(뭉개기)을 치고 0.1초 단위로 구체화된 뷰(Materialized View)를 뽑아내 준다. 아키텍트는 골치 아픈 동기화 자바 코드를 다 버리고 인프라 쿼리 1줄로 극한의 CQRS 파이프라인을 딸깍 세팅하는 오토 스트리밍 시대로 넘어가고 있다.
참고 표준
- Greg Young의 CQRS 패턴 창시: CQS(Command Query Separation)라는 객체지향 룰을 시스템 아키텍처와 분산 DB 레벨로 끄집어 올려 "아예 찢어버려!"라고 주창한 갓-파더.
- Polyglot Persistence (폴리글랏 퍼시스턴스): "왜 세상의 모든 테이블을 낡아빠진 RDBMS(관계형) 하나로만 서비스하려고 하냐?" 데이터 특성에 맞게 쓰기는 RDBMS, 읽기는 Redis, 검색은 Elasticsearch 등 여러 DB 엔진을 한 앱에 다 때려 박고 골라 먹는 클라우드 MSA의 기둥 철학. CQRS가 없다면 폴리글랏은 태어날 수 없었다.
CQRS (Command and Query Responsibility Segregation)는 소프트웨어 공학이 도달한 **'조회(Read)와 쓰기(Write)의 권력과 책임을 물리적으로 난도질하여, 극강의 성능 쾌락을 위해 데이터의 완벽한 1초 일치(Consistency)를 차갑게 찢어 팔아버린 아키텍처의 악마적 거래(Trade-off)'**다. 모놀리식 시절 개발자들은 1개의 DB 안에서 이 두 마리 토끼를 다 잡으려 수천 개의 인덱스(Index)를 걸고 밤새워 쿼리 튜닝 헛발질을 했다. 결과는 늘 DB 락(Lock)에 걸린 참혹한 전사 셧다운 묘비명이었다. 아키텍트는 깨달았다. 사용자가 돈을 지불하는 무거운 "명령(Command)"과 화면을 스와이프하며 1초에 100번씩 쏘아대는 깃털 같은 "조회(Query)"는 애초에 같은 물에서 놀아선 안 되는 전혀 다른 유전자다. 명령은 좁고 안전한 밀실(쓰기 DB)에서 묵직하게 처리하고, 조회는 넓고 얕은 광장(읽기 복제 DB 100대)에 미리 구워진 피자 조각(뷰)을 쫙 깔아두어 폭도(트래픽)들이 0.1초 만에 뜯어먹고 나가게 만들어라. 그 둘 사이를 이어주는 비동기 큐(Kafka)의 1초 딜레이는 결코 버그가 아니다. 수백만 명의 트래픽을 멈추지 않고 굴려내기 위해 마이크로서비스가 기꺼이 감수하고 끌어안아야 하는 '우아하고도 필연적인 찰나의 숨고르기(Eventual Consistency)'일 뿐이다. 분리하는 자만이 속도를 지배한다.
- 📢 섹션 요약 비유: 1통짜리 통짜 DB 구조는 **'도서관의 귀중한 원본 책 한 권'**과 같습니다. 누군가 연필로 밑줄 치며 공부(쓰기)하고 있으면, 책을 그냥 눈으로 읽고(조회) 싶은 10명의 학생은 공부 끝날 때까지 뒤에서 멍하니 기다려야 합니다(Lock 셧다운). CQRS 아키텍처는 공부하는 놈은 **원본 책(쓰기 DB)**에다 혼자 무겁게 쓰게 놔두고, 조교(카프카)가 10분마다 찰칵찰칵 복사기를 돌려 **수백 장의 낱장 복사본(읽기 전용 DB 뷰)**을 옆 테이블에 미친 듯이 뿌려두는 것입니다. 1만 명의 학생이 밀려와도 그냥 낱장 복사본만 쓱 가져가면 되니 아무도 기다리지 않는 압도적인 무적의 도서관 시스템입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 이벤트 소싱 (Event Sourcing) | CQRS의 100% 찰떡 영혼의 단짝. CQRS에서 쓰기 DB(Command)를 낡은 RDBMS 업데이트로 짜지 않고 아예 무한 이벤트 원장 로그(블록체인)로 짜버리는 절대 진화체 아키텍처. (다음 장 555번 연계) |
| Materialized View (구체화된 뷰) | CQRS의 읽기 DB(Query)가 10만 조회를 1초 컷 튕겨내는 마술. 4개 테이블 쪼개서 조인(JOIN) 쳐야 하는 걸 찰흙 1덩어리 JSON으로 아예 합쳐서 저장해 두는 치트키. (이전 장 352번 뷰 연계) |
| 결과적 정합성 (Eventual Const.) | CQRS가 짊어져야 하는 업보이자 BASE 철학. 쓰기 DB에 들어간 돈이 카프카 타고 읽기 DB로 복제되는 0.1초의 시차 동안 유저가 더티 리드(Dirty Read)를 보는 현상을 타협하는 사상. (이전 장 551번 연계) |
| 마스터-슬레이브 DB (Replication) | CQRS의 구석기 원시인 버전. DB만 2대 찢었지, 테이블 구조(스키마)가 똑같아서 조회 시 JOIN 연산 지옥은 해결 1도 못 하는 반쪽짜리 분할술. |
| Kafka / 비동기 메시지 큐 | CQRS의 쓰기 뇌와 읽기 뇌를 이어주는 0.01초 빛의 핏줄. 이 메시지 큐가 끊기거나 유실되면 읽기 DB의 뷰가 짝짝이로 영원히 멈추는 가장 위험한 전송로. (이전 장 536번 연계) |
👶 어린이를 위한 3줄 비유 설명
- 내가 일기장(쓰기 DB) 한 권에 비밀 일기도 쓰고(명령), 친구들한테 내 그림을 보여주기도(조회) 했어요. 한 권밖에 없으니 내가 글 쓸 땐 아무도 내 그림을 못 봐서 친구들이 짜증을 냈죠(렉/병목!).
- 그래서 난 엄청 똑똑하게 일기장을 2개로 완전히 찢어버렸어요(CQRS)! 하나는 나만 무겁게 글을 쓰는 '비밀 일기장(쓰기 DB)', 다른 하나는 친구들이 막 가져가서 1초 만에 보는 **'자랑용 그림 복사본 노트 100권(읽기 DB)'**으로 나눴죠.
- 내가 비밀 일기장에 글을 쓰면, 내 똑똑한 비서(카프카)가 몰래 그 내용을 1초 만에 자랑용 노트에 예쁘게 요약해서(뷰) 쫙 뿌려줘요. 이제 내가 일기를 쓰고 있어도 1만 명의 친구들이 기다림 없이 짱 빠르게 내 노트를 볼 수 있는 마법의 분리술을 'CQRS'라고 부른답니다!