명령·조회 책임 분리

CQRS

쓰기 모델과 읽기 모델을 분리해 각각의 요구에 맞춥니다.

···
html
<div class="scene"><div class="title">COMMAND → PROJECTION → QUERY</div><div class="row cqrs"><div class="tile">WRITE<br><b id="write">0</b></div><div class="projection"><span class="pulse">→</span><small id="projection">pending</small></div><div class="tile">READ<br><b id="read">0</b></div></div><div class="muted" id="lag">Read model follows the write</div></div>
css
.scene{width:min(94vw,760px);height:min(86vh,320px);padding:clamp(10px,2.5vmin,18px);border:1px solid var(--line);border-radius:14px;background:var(--surface);color:var(--fg);font:500 clamp(11px,1.3vw,16px)/1.25 "Pretendard Variable",Pretendard,-apple-system,sans-serif;display:flex;flex-direction:column;gap:clamp(7px,1.6vmin,12px);overflow:hidden}.scene *{box-sizing:border-box}.title{font-weight:750;color:var(--accent)}.muted{color:var(--muted)}.mono{font-family:ui-monospace,monospace}.row{display:flex;align-items:center;justify-content:center;gap:clamp(5px,1.4vmin,12px);flex:1;min-height:0}.tile{border:1px solid var(--line);border-radius:8px;background:var(--bg);padding:clamp(6px,1.6vmin,12px);text-align:center;min-width:0}.on{border-color:var(--accent)!important;background:color-mix(in srgb,var(--accent) 12%,var(--surface))!important}.pulse{animation:pulse 1.6s infinite alternate}@keyframes pulse{to{border-color:var(--accent);transform:translateY(-3px)}}.cqrs .tile{flex:1}.cqrs b{font:700 1.8em ui-monospace,monospace;color:var(--accent)}.projection{display:grid;text-align:center;color:var(--accent)}.projection small{font-size:1em;color:var(--muted)}
js
let value=0,phase=0;function tick(){if(phase===0){value++;document.getElementById('write').textContent=value;document.getElementById('lag').textContent='Write committed; read still '+(value-1)}if(phase===1){document.getElementById('projection').textContent='applying';document.getElementById('lag').textContent='Projecting change...'}if(phase===2){document.getElementById('read').textContent=value;document.getElementById('projection').textContent='synced';document.getElementById('lag').textContent='Read model caught up'}phase=(phase+1)%3}tick();setInterval(tick,800)

CQRS는 상태를 바꾸는 명령과 데이터를 읽는 조회에 다른 모델을 둡니다. 쓰기 모델에서 변경이 일어나면 읽기 모델에 투영해 조회용 형태를 만듭니다.

데모에서는 명령이 먼저 처리되고 읽기 화면은 투영 이벤트가 도착한 뒤 갱신됩니다. 동기화가 비동기라면 잠깐의 지연을 사용자 경험과 오류 처리에 반영해야 합니다.

언제 쓰나

쓰기 규칙과 조회 형태가 크게 다르거나 읽기 부하를 별도로 최적화할 때 검토합니다.

페이지로 열기 ↗