System Design 실전 가이드북
이 문서는 System Design 폴더의 기존 문서를 다시 찾아가게 만드는 실전 안내서다.
읽는 순서는 단순하다. 먼저 비슷한 상황의 Case를 고르고, 관찰할 증거를 자기 상황에 맞게 채운 뒤, 찾아볼 문서를 다시 읽는다. 여기서 답을 바로 외우려고 하지 말고, 어떤 요구사항이 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 바꾸는지 확인한다.
이 가이드북은 새 이론을 추가하지 않는다. 기존 문서를 실제 문제 상황에서 회수하는 용도다.
전체 Case 목록
기본 회수 Case
| Case | 상황 | 주로 회수하는 챕터 |
|---|---|---|
| Case 01 | 새 기능 요구사항을 구조로 바꾸는 첫 설계 | 01. System Design 전체 지도 |
| Case 02 | 기능 요구사항과 비기능 요구사항을 분리하는 리뷰 | 02. Functional Non-functional Requirement |
| Case 03 | 이벤트 트래픽을 숫자로 바꾸고 capacity를 잡는 상황 | 03. Traffic Estimation Capacity Planning |
| Case 04 | 상태 저장 위치와 데이터 owner를 정하는 상황 | 04. Stateless Stateful Data Ownership |
| Case 05 | Redis cache를 붙이기 전에 정합성 계약을 정하는 상황 | 05. Cache 전략과 정합성 |
| Case 06 | 느린 후속 작업을 queue로 분리하는 상황 | 06. Queue Async Event-driven Design |
| Case 07 | timeout, retry, idempotency를 함께 설계하는 상황 | 07. Retry Timeout Idempotency |
| Case 08 | API 남용과 queue 적체를 제한하는 상황 | 08. Rate Limit Backpressure |
| Case 09 | 일관성과 가용성 사이에서 API 계약을 정하는 상황 | 09. Consistency Availability Partition |
| Case 10 | scale up, scale out, sharding을 검토하는 상황 | 10. Scale Up Scale Out Partitioning Sharding |
| Case 11 | Monolith 유지, Modular Monolith, MSA 분리를 고르는 상황 | 11. Monolith Modular Monolith MSA |
| Case 12 | 외부 의존성 장애를 격리하는 상황 | 12. Circuit Breaker Bulkhead 장애 격리 |
| Case 13 | read/write 분리와 CQRS를 검토하는 상황 | 13. Read Write 분리 CQRS |
| Case 14 | URL Shortener, Notification, Chat, Feed 설계를 비교하는 상황 | 14. 실전 설계 사례 URL Shortener Notification Chat |
| Case 15 | 설계 리뷰와 장애 대응을 runbook으로 회수하는 상황 | 90. System Design 실전 플레이북 |
교차 설계 Case
| Case | 상황 | 함께 봐야 하는 축 |
|---|---|---|
| Case 16 | 선착순 쿠폰 발급 API를 출시 전 리뷰하는 상황 | 요구사항, 트래픽, rate limit, idempotency |
| Case 17 | 상품 상세 이벤트 페이지가 느려져 cache와 fallback을 검토하는 상황 | cache, capacity, 장애 격리 |
| Case 18 | 알림이 중복 발송되고 일부는 늦게 도착하는 상황 | queue, retry, idempotency, notification |
| Case 19 | 결제 provider timeout 이후 주문 상태가 애매해진 상황 | timeout, unknown state, 일관성, fallback |
| Case 20 | 피드 timeline freshness와 fanout 비용을 조정하는 상황 | traffic, queue, consistency, CQRS |
| Case 21 | 대형 채팅방에서 ordering과 presence가 흔들리는 상황 | state, backpressure, sharding, chat |
장애/운영 Case
| Case | 상황 | 먼저 볼 증거 |
|---|---|---|
| Case 22 | 가격 변경 후 일부 화면에 오래된 값이 보이는 상황 | cache key, TTL, invalidation, 사용자 영향 |
| Case 23 | queue age가 늘고 retry가 provider를 더 압박하는 상황 | oldest message age, retry rate, 429, DLQ |
| Case 24 | 주문 생성 후 목록에 바로 보이지 않는 상황 | replica lag, projection lag, read-after-write |
| Case 25 | app instance를 늘렸는데 DB와 특정 shard만 더 느려진 상황 | connection 총합, hot partition, downstream budget |
| Case 26 | 배송 provider 장애가 주문 상세 전체 지연으로 번지는 상황 | timeout, circuit open, bulkhead rejected, fallback rate |
설계 리뷰/종합 Case
| Case | 상황 | 점검할 판단 |
|---|---|---|
| Case 27 | ”MSA로 나누면 확장된다”는 제안을 리뷰하는 상황 | owner, shared DB, 배포 독립성, 관측 |
| Case 28 | URL Shortener 설계 답안을 실제 운영 관점에서 다듬는 상황 | redirect path, cache, click log, abuse |
| Case 29 | 설계 문서를 운영 인수인계 가능한 형태로 고치는 상황 | 결정 기록, 지표, rollback, 미해결 질문 |
| Case 30 | System Design 모의 면접 답변을 한 번 더 끌어올리는 상황 | 요구사항, 숫자, trade-off, runbook |
Case 01. 새 기능 요구사항을 구조로 바꾸는 첫 설계
상황
제품에서 “사용자가 이벤트 쿠폰을 받을 수 있게 해 주세요”라는 요청이 들어왔다. 아직 API, 데이터, 트래픽, 실패 응답은 정리되지 않았다. 이 상태에서 바로 Redis, queue, DB schema를 고르면 설계가 요구사항을 따라가지 못한다.
관찰할 증거 또는 확인할 단서
- 사용자가 성공으로 기대하는 결과
- 쓰기 API, 읽기 API, 운영 API의 분리 여부
- 상태 owner와 최종 진실의 원천
- 피크 트래픽과 실패 응답 기준
- 개인 프로젝트 수준인지 운영 서비스 수준인지
먼저 생각해보기
처음 그릴 것은 컴포넌트 그림이 아니다. 기능 문장을 API 계약, 상태, 병목, 장애 경계로 쪼개야 한다. 기술 선택은 그 다음이다.
찾아볼 문서
- 00. System Design 전체 지도 압축 정리
- 01. 요구사항에서 구조로 가는 흐름 상세
- 02. 백엔드 시스템 구성 요소 지도 상세
- 03. 성능 확장성 신뢰성 트레이드오프 상세
- 04. 개인 프로젝트와 기업 설계 수준 비교 상세
- 05. System Design 학습 로드맵 상세
판단 흐름
- 사용자 성공 기준을 한 문장으로 쓴다.
- 기능을 command, query, admin operation으로 나눈다.
- 각 기능의 상태 owner를 정한다.
- cache, queue, replica 같은 컴포넌트가 필요한 이유를 요구사항으로 설명한다.
- 선택이 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 바꾸는지 적는다.
정리
첫 설계의 품질은 컴포넌트 수가 아니라 번역 품질로 드러난다. 요구사항이 구조로 번역되지 않았으면, 이후의 기술 선택은 대부분 근거가 약해진다.
가져갈 한 문장
System Design은 기술 이름을 고르는 일이 아니라 요구사항을 API, 상태, 트래픽, 실패 경계로 번역하는 일이다.
Case 02. 기능 요구사항과 비기능 요구사항을 분리하는 리뷰
상황
알림 기능 기획서에 “사용자에게 빠르고 안정적으로 알림을 보낸다”라고 적혀 있다. 구현자는 endpoint와 테이블부터 만들 수 있지만, 운영 기준이 없으면 성공과 실패를 판단하기 어렵다.
관찰할 증거 또는 확인할 단서
- 기능 범위와 제외 범위
- latency, throughput, availability 같은 숫자
- 반드시 지켜야 하는 제약과 임시 가정
- 검증 방법이 붙은 요구사항
- 실패 응답과 사용자 안내 문구
먼저 생각해보기
기능 요구사항은 행동을 정하고, 비기능 요구사항은 그 행동이 어느 품질로 동작해야 하는지 정한다. 둘을 분리하지 않으면 queue, retry, cache 같은 선택이 감으로 정해진다.
찾아볼 문서
- 00. Functional Non-functional Requirement 압축 정리
- 01. Functional Requirement 정리 상세
- 02. Non-functional Requirement 상세
- 03. Latency Throughput Availability 상세
- 04. Constraint Assumption 질문법 상세
- 05. 요구사항 리뷰 템플릿 상세
판단 흐름
- “무엇을 한다”를 caller, endpoint, command/query, 상태 전이로 쪼갠다.
- “빠르게”를 p95 latency나 처리량 같은 숫자로 바꾼다.
- 제약과 가정을 분리하고, 가정에는 검증 계획을 붙인다.
- 템플릿에 비어 있는 칸이 있으면 의도적으로 보류했는지 확인한다.
정리
좋은 요구사항 리뷰는 개발 범위를 늘리는 절차가 아니다. 나중에 구조를 크게 바꾸게 만들 질문을 초기에 드러내는 장치다.
가져갈 한 문장
요구사항은 기능 목록이 아니라 설계 선택을 판정하는 기준표가 되어야 한다.
Case 03. 이벤트 트래픽을 숫자로 바꾸고 capacity를 잡는 상황
상황
마케팅 팀이 다음 주에 대형 이벤트를 연다. 예상 참여자는 50만 명이지만, 서버가 봐야 할 것은 총 사용자 수가 아니라 초당 어떤 행동이 몰리는지다.
관찰할 증거 또는 확인할 단서
- DAU, MAU, 동시 접속, 활성 시간대
- read/write 비율과 write hot spot
- payload 크기, 저장량, bandwidth, cache memory
- headroom과 backlog drain time
- 숫자가 어떤 구조 선택으로 연결되는지
먼저 생각해보기
평균 QPS만 계산하면 위험하다. 피크, retry, batch, hot key, downstream quota가 같은 시간에 겹치면 평균은 거의 도움이 되지 않는다.
찾아볼 문서
- 00. Traffic Estimation Capacity Planning 압축 정리
- 01. QPS TPS DAU MAU 추정 상세
- 02. Read Write 비율과 Peak Traffic 상세
- 03. Storage Bandwidth Memory 추정 상세
- 04. Capacity Planning과 여유율 상세
- 05. 추정값을 설계 결정으로 연결 상세
판단 흐름
- 사용자 수를 행동 빈도와 활성 시간대로 바꾼다.
- read peak와 write peak를 따로 계산한다.
- 저장량과 cache memory를 원본 데이터가 아니라 파생 데이터까지 포함해 본다.
- headroom을 비용 낭비가 아니라 장애 중 완충재로 해석한다.
- 계산값을 cache, queue, replica, partition, rate limit 선택으로 연결한다.
정리
트래픽 추정의 목적은 정답 숫자를 맞히는 것이 아니다. 어떤 구조가 필요한지 설명할 수 있는 근거를 만드는 것이다.
가져갈 한 문장
추정값은 숫자로 끝나면 안 되고, 설계 선택과 운영 지표로 이어져야 한다.
Case 04. 상태 저장 위치와 데이터 owner를 정하는 상황
상황
단일 서버 쇼핑몰을 여러 대의 Spring 인스턴스로 늘리려 한다. 로그인 세션, 장바구니, 주문 상태, 재고 예약, 알림 발송 상태가 어디에 저장되는지 불명확하다.
관찰할 증거 또는 확인할 단서
- 요청 사이에 유지되어야 하는 상태
- 인스턴스가 죽어도 살아야 하는 상태
- Redis가 cache인지 session store인지 idempotency store인지
- 최종 변경 권한을 가진 owner
- RTO, RPO, 복구 가능성
먼저 생각해보기
Stateless는 상태가 없다는 말이 아니다. 상태가 특정 인스턴스에 묶이지 않고 명확한 owner가 있는 저장소로 이동한다는 뜻이다.
찾아볼 문서
- 00. Stateless Stateful Data Ownership 압축 정리
- 01. Stateless 서버와 Session 분리 상세
- 02. Stateful Component와 복구 비용 상세
- 03. Data Ownership과 Service Boundary 상세
- 04. Session Store와 Sticky Session 판단 상세
- 05. 상태 관리 설계 실수 상세
판단 흐름
- 상태를 요청 내 상태, 요청 간 상태, 파생 상태, 최종 상태로 나눈다.
- 각 상태의 owner와 복구 비용을 붙인다.
- sticky session, session store, token 방식의 전환 비용을 비교한다.
- local lock, static map, TTL 없는 Redis key 같은 숨은 상태를 찾는다.
정리
상태 위치를 정하지 않은 scale out은 실패 확률을 높인다. 특히 session과 owner가 흐리면 장애 때 어떤 데이터를 믿어야 할지 모르게 된다.
가져갈 한 문장
상태는 저장 위치보다 owner와 복구 비용을 먼저 정해야 한다.
Case 05. Redis cache를 붙이기 전에 정합성 계약을 정하는 상황
상황
상품 상세 API p95가 높아 Redis를 붙이자는 제안이 나왔다. 하지만 가격, 재고, 리뷰 수, 추천 배너가 같은 TTL로 캐시되어도 되는지는 아직 정해지지 않았다.
관찰할 증거 또는 확인할 단서
- 캐시하려는 데이터의 stale 허용 범위
- cache miss, expired, hot key, nonexistent key의 처리
- invalidation owner와 전파 방식
- Redis 장애 시 fallback 가능 여부
- 사용자에게 오래된 값을 어떻게 드러낼지
먼저 생각해보기
캐시는 성능 장치이면서 정합성 계약이다. 어떤 값이 얼마나 오래 틀려도 되는지 설명하지 못하면 Redis를 붙인 뒤 사고 범위만 넓어진다.
찾아볼 문서
- 00. Cache 전략과 정합성 압축 정리
- 01. Cache Aside Read Through Write Through 상세
- 02. TTL Eviction Invalidation 상세
- 03. Cache Stampede와 Hot Key 상세
- 04. 정합성 깨짐과 사용자 경험 상세
- 05. Redis Cache 실전 설계 상세
판단 흐름
- 캐시 패턴을 책임 위치 기준으로 고른다.
- TTL, eviction, invalidation을 서로 다른 문제로 분리한다.
- hot key와 stampede를 정상 운영 경로 안에 포함한다.
- stale data를 사용자 경험과 API 계약으로 표현한다.
- Redis 장애 시 DB fallback이 안전한지 별도로 판단한다.
정리
캐시 설계는 hit 상태만 설계하면 부족하다. miss, expired, stale, hot, Redis down 상태를 함께 설계해야 한다.
가져갈 한 문장
캐시는 빠르게 만드는 선택이 아니라 틀릴 수 있는 시간을 계약하는 선택이다.
Case 06. 느린 후속 작업을 queue로 분리하는 상황
상황
주문 생성 후 영수증 이메일, 추천 통계 적재, 마케팅 이벤트 발행이 사용자 응답을 늦게 만든다. 팀은 queue로 빼자고 하지만, 어떤 작업이 늦어져도 주문 성공으로 말할 수 있는지는 정리되지 않았다.
관찰할 증거 또는 확인할 단서
- 사용자 응답 시점에 반드시 끝나야 하는 일
- 발행 성공과 처리 성공의 구분
- 메시지 payload, ack 위치, retry 정책
- DLQ owner와 재처리 절차
- 이벤트가 command처럼 쓰이는지 여부
먼저 생각해보기
비동기화의 첫 질문은 “느린가?”가 아니다. “아직 끝나지 않았는데 성공이라고 말해도 되는가?”다.
찾아볼 문서
- 00. Queue Async Event-driven Design 압축 정리
- 01. Sync Async 처리 경계 상세
- 02. Queue Producer Consumer 기본 상세
- 03. Event-driven 설계와 느슨한 결합 상세
- 04. Retry DLQ Ordering 중복 상세
- 05. Spring과 메시지 처리 설계 상세
판단 흐름
- 동기 경로에 남길 불변식과 비동기 경로로 미룰 작업을 나눈다.
- producer 발행 성공과 consumer 처리 성공을 서로 다른 상태로 본다.
- 이벤트 이름보다 payload 의미, version, 발생 시점 계약을 먼저 고정한다.
- retry, DLQ, ordering, idempotency를 한 묶음으로 설계한다.
- Spring listener의 transaction, ack, 예외 처리, concurrency를 운영 지표와 연결한다.
정리
Queue는 느린 일을 숨기는 장치가 아니라 지연, 중복, 재처리 책임을 밖으로 드러내는 장치다.
가져갈 한 문장
비동기는 성공 시점을 다시 정의하는 설계다.
Case 07. Timeout, Retry, Idempotency를 함께 설계하는 상황
상황
결제 API에서 사용자가 timeout을 보고 다시 결제 버튼을 눌렀다. 서버는 결제가 실패했는지, provider에서 승인됐지만 응답만 유실됐는지 확신하지 못한다.
관찰할 증거 또는 확인할 단서
- client, gateway, app, DB, external API timeout 계층
- retry 대상과 retry하면 안 되는 실패
- idempotency key 저장소와 unique constraint
- provider idempotency 지원 여부
- UNKNOWN 상태와 후속 확인 절차
먼저 생각해보기
Retry는 실패를 줄일 수 있지만, idempotency가 없으면 중복 결제와 중복 주문을 만든다. Timeout이 없거나 너무 길면 내부 자원이 오래 붙잡힌다.
찾아볼 문서
- 00. Retry Timeout Idempotency 압축 정리
- 01. Timeout 계층 설계 상세
- 02. Retry Backoff Jitter 상세
- 03. Idempotency Key와 중복 요청 방어 상세
- 04. 외부 API 호출 안정성 상세
- 05. 장애 증폭을 막는 호출 설계 상세
판단 흐름
- 각 계층의 timeout 순서를 확인한다.
- retry 가능한 실패와 확정 실패를 나눈다.
- 같은 비즈니스 의도를 식별할 idempotency key를 정한다.
- 외부 API timeout 이후 내부 상태를 UNKNOWN으로 둘지 판단한다.
- retry storm을 막기 위해 budget, jitter, rate limit, circuit breaker를 함께 본다.
정리
Timeout, retry, idempotency는 따로 적용하는 기능이 아니다. 하나가 빠지면 나머지가 장애를 키울 수 있다.
가져갈 한 문장
다시 시도할 수 있으려면, 같은 요청을 두 번 받아도 같은 결과를 줄 수 있어야 한다.
Case 08. API 남용과 queue 적체를 제한하는 상황
상황
검색 API와 export API가 같은 시간에 몰리면서 DB와 queue가 동시에 느려진다. 일부 클라이언트는 429를 받자마자 즉시 재시도하고 있다.
관찰할 증거 또는 확인할 단서
- 보호해야 할 리소스와 제한 위치
- IP, user, tenant, API key, endpoint 기준
- token bucket, sliding window, fixed window 선택 이유
- queue depth보다 중요한 oldest age와 처리율
- 429와 503의 구분, Retry-After 계약
먼저 생각해보기
Rate Limit은 사용자를 밀어내는 기능이 아니다. 중요한 요청을 살리기 위해 감당 가능한 유입 속도를 정하는 장치다.
찾아볼 문서
- 00. Rate Limit Backpressure 압축 정리
- 01. Rate Limit 목적과 알고리즘 상세
- 02. Token Bucket Sliding Window 상세
- 03. Backpressure와 Queue 적체 상세
- 04. 사용자별 API별 제한 정책 상세
- 05. 429 응답과 클라이언트 계약 상세
판단 흐름
- 제한의 목적을 비용 보호, abuse 방지, 공정성, 장애 격리 중 어디에 둘지 정한다.
- 알고리즘을 burst 허용 여부와 트래픽 모양으로 고른다.
- queue 적체는 depth뿐 아니라 age, 처리율, downstream 상태로 본다.
- 사용자별, API별, tenant별 정책을 분리한다.
- 429 응답이 클라이언트 retry storm을 만들지 않게 계약을 쓴다.
정리
제한 정책은 숫자보다 기준이 어렵다. 누구를 어떤 API에서 어떤 이유로 줄이는지 설명되어야 한다.
가져갈 한 문장
좋은 제한은 핵심 기능을 살리기 위해 덜 중요한 유입을 명시적으로 늦추는 계약이다.
Case 09. 일관성과 가용성 사이에서 API 계약을 정하는 상황
상황
주문 생성은 성공했지만 목록 화면에는 아직 보이지 않는다. 어떤 팀은 강한 일관성을 요구하고, 어떤 팀은 가용성을 위해 eventual consistency를 허용하자고 한다.
관찰할 증거 또는 확인할 단서
- 틀린 응답보다 실패가 나은 데이터인지
- 방금 쓴 값을 어디서 읽어야 하는지
- strong consistency가 필요한 불변식
- eventual consistency를 드러낼 UX 상태
- partition, timeout, partial failure 처리 방식
먼저 생각해보기
CAP는 약어 암기가 아니라 장애 중 사용자에게 어떤 응답을 줄지 정하는 언어다. 모든 데이터에 같은 일관성 기준을 적용하면 비용이 과하거나 UX가 깨진다.
찾아볼 문서
- 00. Consistency Availability Partition 압축 정리
- 01. CAP를 실무 언어로 이해하기 상세
- 02. Strong Eventual Consistency 상세
- 03. Read-after-write와 Monotonic Read 상세
- 04. 분산 환경의 실패 모델 상세
- 05. 일관성 선택이 UX에 미치는 영향 상세
판단 흐름
- 데이터별로 strong과 eventual의 비용을 나눈다.
- read-after-write와 monotonic read가 필요한 화면을 분리한다.
- unknown, pending, stale 같은 상태를 API로 드러낼지 정한다.
- outbox, idempotency, conflict 처리, UX 문구를 함께 설계한다.
정리
일관성 선택은 DB 설정만의 문제가 아니다. 사용자가 시스템을 믿을 수 있는지와 직접 연결된다.
가져갈 한 문장
일관성을 낮출 수는 있지만, 사용자에게 어떤 상태인지 숨기면 신뢰가 깨진다.
Case 10. Scale Up, Scale Out, Sharding을 검토하는 상황
상황
API latency가 올라가자 팀에서 app instance를 2배 늘리자고 한다. 하지만 병목이 app CPU인지, DB connection인지, 특정 shard의 hot key인지는 아직 확인하지 않았다.
관찰할 증거 또는 확인할 단서
- 병목 계층과 포화 지표
- app scale out이 downstream에 만드는 추가 부하
- stateless 여부와 scheduler 중복 실행
- partition key, shard key, routing 비용
- rebalancing, backup, migration 운영 비용
먼저 생각해보기
확장은 서버 수를 늘리는 일이 아니라 병목의 위치를 바꾸는 일이다. 병목을 모른 채 scale out하면 DB나 외부 API를 더 빨리 망가뜨릴 수 있다.
찾아볼 문서
- 00. Scale Up Scale Out Partitioning Sharding 압축 정리
- 01. Scale Up과 Scale Out 판단 상세
- 02. Stateless App 수평 확장 상세
- 03. Partitioning과 Sharding 기본 상세
- 04. Rebalancing과 Hot Partition 상세
- 05. 확장 전후 운영 비용 상세
판단 흐름
- scale up과 scale out을 병목 위치 기준으로 비교한다.
- app 수평 확장 전에 로컬 상태, DB connection 총합, scheduler를 점검한다.
- partitioning과 sharding을 운영 의미 기준으로 구분한다.
- hot partition을 평균 지표가 아닌 key별 지표로 확인한다.
- 확장 이후의 관측, 배포, 백업, migration 비용을 계산한다.
정리
확장은 성능만 바꾸지 않는다. 정합성, 장애 격리, 운영 절차까지 같이 바꾼다.
가져갈 한 문장
Scale out은 병목을 없애는 선택이 아니라 병목이 이동할 다음 위치를 받아들이는 선택이다.
Case 11. Monolith 유지, Modular Monolith, MSA 분리를 고르는 상황
상황
주문, 결제, 배송 코드가 한 애플리케이션 안에 있다. 팀은 MSA 전환을 검토하지만 shared DB, 동기 fanout, 관측 체계, owner 분리는 아직 준비되지 않았다.
관찰할 증거 또는 확인할 단서
- 변경 결합도와 배포 독립성
- 단일 트랜잭션이 주는 장점
- module 경계를 강제할 수 있는지
- 데이터 owner와 shared DB 여부
- 분리 이후 장애 대응과 관측 준비도
먼저 생각해보기
Monolith, Modular Monolith, MSA는 성숙도 순서가 아니다. 현재 팀이 감당할 수 있는 운영 복잡도와 데이터 owner 기준으로 고르는 구조 선택이다.
찾아볼 문서
- 00. Monolith Modular Monolith MSA 압축 정리
- 01. Monolith의 장점과 한계 상세
- 02. Modular Monolith 설계 상세
- 03. MSA 분리 기준 상세
- 04. 분산 트랜잭션과 데이터 소유권 상세
- 05. MSA 도입 실패 신호 상세
판단 흐름
- Monolith가 제공하는 단일 트랜잭션과 운영 단순성을 인정한다.
- 코드 줄 수가 아니라 변경 결합도와 장애 영향 범위를 본다.
- MSA 전에 module 경계와 repository 접근 규칙을 강제한다.
- 분산 트랜잭션 대신 saga, outbox, 보상 작업을 설계할 준비가 있는지 본다.
- shared DB와 sync fanout이 남아 있으면 분산 Monolith 위험으로 표시한다.
정리
MSA는 구조를 나누는 일이 아니라 책임과 운영을 나누는 일이다. 책임이 나뉘지 않으면 서비스 수만 늘어난다.
가져갈 한 문장
서비스 분리는 데이터 owner, 배포 권한, 장애 대응 책임이 함께 분리될 때 의미가 있다.
Case 12. 외부 의존성 장애를 격리하는 상황
상황
배송 provider가 느려지자 주문 상세 API 전체가 느려졌다. 주문 상태와 결제 상태는 정상인데 배송 조회 때문에 로그인, 장바구니까지 영향을 받기 시작한다.
관찰할 증거 또는 확인할 단서
- 공유 thread pool, connection pool, queue 사용 여부
- failure rate, slow call rate, timeout
- circuit open, half-open trial, not permitted calls
- bulkhead rejected calls와 fallback reason
- fallback이 가짜 성공을 만들고 있는지
먼저 생각해보기
외부 의존성 장애는 그 의존성 하나의 문제가 아니다. 공유 리소스를 통해 정상 기능까지 굶기기 때문에 격리 경계가 필요하다.
찾아볼 문서
- 00. Circuit Breaker Bulkhead 장애 격리 압축 정리
- 01. 장애 전파와 Cascading Failure 상세
- 02. Circuit Breaker 상태 모델 상세
- 03. Bulkhead와 Resource Isolation 상세
- 04. Fallback Degradation 설계 상세
- 05. Resilience4j 실전 예시 상세
판단 흐름
- 장애가 전파되는 리소스 경로를 찾는다.
- timeout을 줄일지, circuit breaker를 열지, bulkhead로 분리할지 결정한다.
- fallback 가능한 데이터와 절대 fallback하면 안 되는 데이터를 나눈다.
- Resilience4j 설정값을 요구사항과 latency budget에서 끌어온다.
- HTTP 200 성공률과 fallback rate를 분리해 본다.
정리
장애 격리는 실패를 없애지 않는다. 실패가 번질 수 있는 리소스 경계를 제한한다.
가져갈 한 문장
Fallback은 성공을 흉내 내는 기능이 아니라 축소된 상태를 드러내는 API 계약이다.
Case 13. Read/Write 분리와 CQRS를 검토하는 상황
상황
관리자 매출 대시보드가 주문 생성 DB에 무거운 join을 계속 날린다. 주문 생성 p95가 흔들리고, 대시보드의 정확도 요구사항도 화면마다 다르다.
관찰할 증거 또는 확인할 단서
- 쓰기 모델이 지켜야 하는 불변식
- 읽기 화면의 조회 형태와 freshness 기준
- read replica, cache, view, projection의 단계
- projection lag와 rebuild 절차
- 운영팀이 감당할 수 있는 복잡도
먼저 생각해보기
Read/write 분리는 DB를 하나 더 두는 일이 아니다. 쓰기 모델의 안전성과 읽기 모델의 조회 비용을 분리하는 선택이다.
찾아볼 문서
- 00. Read Write 분리 CQRS 압축 정리
- 01. Read Write 분리 목적 상세
- 02. CQRS 기본과 적용 기준 상세
- 03. Projection Read Model 상세
- 04. 동기화 지연과 일관성 상세
- 05. 복잡도를 감당할 수 있는 기준 상세
판단 흐름
- read/write 분리 전에 실제 병목 증거를 확인한다.
- read replica로 충분한지, projection read model이 필요한지 구분한다.
- command model과 query model의 책임을 분리한다.
- projection lag를 사용자와 운영자에게 어떻게 드러낼지 정한다.
- rebuild, replay, DLQ, schema version까지 운영할 수 있는지 확인한다.
정리
CQRS는 성숙해 보이는 이름이지만, 실제로는 복잡도를 빌려 성능과 격리를 얻는 선택이다.
가져갈 한 문장
Read model을 만들면 조회는 편해지지만 lag, rebuild, replay라는 운영 책임이 따라온다.
Case 14. URL Shortener, Notification, Chat, Feed 설계를 비교하는 상황
상황
면접이나 설계 스터디에서 URL Shortener, Notification, Chat, Feed를 모두 비슷한 cache, queue, DB 문제로 처리하고 있다. 하지만 각 문제의 핵심 병목과 정합성 기준은 다르다.
관찰할 증거 또는 확인할 단서
- URL Shortener의 redirect read path
- Notification의 fanout, 수신 거부, provider 실패
- Chat의 저장된 메시지와 ordering
- Feed의 fanout on write/read, freshness
- 설계 리뷰에서 빠진 요구사항과 지표
먼저 생각해보기
같은 Redis와 queue를 써도 사례별 비용은 다르다. 중요한 것은 패턴 이름이 아니라 어떤 요구사항이 어떤 trade-off를 만든다는 설명이다.
찾아볼 문서
- 00. 실전 설계 사례 URL Shortener Notification Chat 압축 정리
- 01. URL Shortener 설계 상세
- 02. Notification System 설계 상세
- 03. Chat System 설계 상세
- 04. Feed Timeline 설계 상세
- 05. 설계 리뷰와 개선 포인트 상세
판단 흐름
- 사례별 핵심 성공 기준을 먼저 고정한다.
- 같은 컴포넌트라도 사례마다 다른 실패 비용을 적는다.
- redirect, delivery, message ordering, freshness 지표를 분리한다.
- 설계 리뷰에서 요구사항, 트래픽, 상태 owner, 실패 모델, API 계약을 점검한다.
정리
실전 사례는 정답 구조를 외우는 연습이 아니다. 같은 구성 요소가 문제마다 다른 의미를 갖는다는 것을 확인하는 연습이다.
가져갈 한 문장
유명한 설계 문제의 답은 컴포넌트 이름이 아니라 요구사항에서 trade-off를 끌어내는 과정에 있다.
Case 15. 설계 리뷰와 장애 대응을 runbook으로 회수하는 상황
상황
신규 기능 설계 리뷰와 장애 대응 회고가 매번 다른 형식으로 흩어진다. 장애 때는 원인 분석부터 시작하느라 사용자 영향과 즉시 완화가 늦어진다.
관찰할 증거 또는 확인할 단서
- 사용자 영향과 데이터 손상 가능성
- p95, error rate, queue age, cache hit ratio, DB CPU
- 즉시 완화와 근본 설계 변경의 분리
- rollback 조건과 운영 의사결정 기록
- 설계 문서에 남아야 할 결정과 포기한 대안
먼저 생각해보기
Runbook은 정답 절차가 아니다. 긴장된 상황에서 무엇을 먼저 판단할지 고정해 주는 순서표다.
찾아볼 문서
- 00. System Design 실전 플레이북 압축 정리
- 01. 신규 기능 설계 리뷰 Runbook 상세
- 02. 트래픽 급증 대응 설계 Runbook 상세
- 03. Cache 정합성 문제 대응 Runbook 상세
- 04. Queue 적체와 Retry 폭주 대응 Runbook 상세
- 05. 설계 문서 작성 템플릿 상세
판단 흐름
- 사용자 영향과 되돌릴 수 없는 데이터 손상 여부를 먼저 본다.
- 증거 지표와 임시 완화책을 분리한다.
- 캐시, queue, 트래픽 급증, 설계 리뷰 상황별 runbook을 고른다.
- 결정 문서에 선택 이유, 포기한 대안, 관측 지표, rollback 조건을 남긴다.
정리
좋은 runbook은 원인을 맞히는 문서가 아니라, 위험한 대응을 줄이고 판단 순서를 고정하는 문서다.
가져갈 한 문장
실전 대응은 원인 이름보다 사용자 영향, 증거, 완화, 정합성 손상 여부를 먼저 본다.
Case 16. 선착순 쿠폰 발급 API를 출시 전 리뷰하는 상황
상황
선착순 쿠폰 발급 이벤트를 앞두고 있다. 기능은 단순해 보이지만 피크 쓰기, 중복 요청, 재고 차감, rate limit, 실패 응답이 모두 얽힌다.
관찰할 증거 또는 확인할 단서
- 쿠폰 발급 성공의 정확한 의미
- peak write TPS와 hot row 가능성
- idempotency key와 중복 클릭 처리
- 사용자별/API별 제한 정책
- 보류해야 하는 설계 답변
먼저 생각해보기
선착순 기능에서 가장 위험한 답은 “Redis로 빠르게 처리한다”처럼 기술로 시작하는 답이다. 먼저 재고 불변식과 중복 요청 계약을 고정해야 한다.
찾아볼 문서
- 01. 요구사항에서 구조로 가는 흐름 상세
- 05. 요구사항 리뷰 템플릿 상세
- 02. Read Write 비율과 Peak Traffic 상세
- 03. Idempotency Key와 중복 요청 방어 상세
- 04. 사용자별 API별 제한 정책 상세
- 01. 신규 기능 설계 리뷰 Runbook 상세
판단 흐름
- 발급 성공, 이미 발급됨, 품절, 처리 중을 API 응답으로 나눈다.
- peak write가 같은 coupon row에 몰리는지 확인한다.
- 중복 클릭과 retry를 같은 business intent로 묶는다.
- 사용자별 제한과 idempotency를 서로 다른 장치로 둔다.
- 출시 전 보류 기준을 runbook에 맞춰 정한다.
정리
선착순 설계는 속도보다 불변식이 먼저다. 빠르게 틀리면 운영자가 고칠 수 없는 데이터가 남는다.
가져갈 한 문장
쿠폰 발급은 QPS 문제가 아니라 재고 불변식과 중복 요청 계약의 문제다.
Case 17. 상품 상세 이벤트 페이지가 느려져 cache와 fallback을 검토하는 상황
상황
이벤트 시작 직후 상품 상세 페이지 p95가 치솟았다. Redis를 붙이자는 의견과 배송/추천 영역을 fallback하자는 의견이 동시에 나온다.
관찰할 증거 또는 확인할 단서
- 상품 상세 중 최신이어야 하는 필드
- cache hit ratio, miss storm, hot key
- DB CPU, connection pool, downstream API latency
- fallback 가능한 부가 영역
- traffic shed 기준
먼저 생각해보기
상품 상세의 모든 필드가 같은 정합성을 요구하지 않는다. 가격과 재고, 배송 예상, 추천 영역의 freshness 기준을 나눠야 한다.
찾아볼 문서
- 03. 성능 확장성 신뢰성 트레이드오프 상세
- 04. Capacity Planning과 여유율 상세
- 03. Cache Stampede와 Hot Key 상세
- 04. 정합성 깨짐과 사용자 경험 상세
- 04. Fallback Degradation 설계 상세
- 02. 트래픽 급증 대응 설계 Runbook 상세
판단 흐름
- 페이지를 핵심 명령 경로와 부가 조회 경로로 나눈다.
- hot key와 stampede를 먼저 확인한다.
- 오래된 값 허용 범위를 필드별로 나눈다.
- fallback이 가능한 영역만 축소 응답으로 드러낸다.
- traffic shed와 cache bypass의 조건을 별도로 정한다.
정리
캐시와 fallback은 모두 사용자 경험을 바꾼다. 그래서 기술 대응 전에 어떤 값을 틀리게 보여도 되는지부터 정해야 한다.
가져갈 한 문장
느린 화면은 한 덩어리로 보이지만, 설계는 최신이어야 하는 영역과 축소 가능한 영역을 나누는 데서 시작한다.
Case 18. 알림이 중복 발송되고 일부는 늦게 도착하는 상황
상황
프로모션 알림 발송 후 일부 사용자는 같은 알림을 두 번 받았고, 일부는 30분 뒤 받았다. provider 429와 consumer retry가 함께 발생했다.
관찰할 증거 또는 확인할 단서
- producer 발행 성공과 consumer 처리 성공의 분리
- message id, idempotency key, inbox/outbox
- retry rate, DLQ, oldest message age
- provider rate limit과 429 계약
- notification 상태 모델
먼저 생각해보기
알림 시스템의 성공은 queue에 넣는 순간이 아니다. 사용자에게 언제 어떤 채널로 도착해야 하는지, 실패한 발송을 어떻게 다시 찾을지가 중요하다.
찾아볼 문서
- 02. Queue Producer Consumer 기본 상세
- 04. Retry DLQ Ordering 중복 상세
- 02. Retry Backoff Jitter 상세
- 05. 429 응답과 클라이언트 계약 상세
- 02. Notification System 설계 상세
- 04. Queue 적체와 Retry 폭주 대응 Runbook 상세
판단 흐름
- 중복 발송과 지연 발송을 별도 증상으로 분리한다.
- 메시지 중복을 기본값으로 보고 consumer idempotency를 확인한다.
- retry가 provider 429를 증폭하고 있는지 본다.
- DLQ가 단순 저장소가 아니라 운영 절차를 갖는지 확인한다.
- 알림 상태를 requested, sent, delivered, failed처럼 나눌지 판단한다.
정리
알림 장애는 queue 처리량만의 문제가 아니다. 중복, 지연, provider 제한, 사용자 수신 경험이 함께 움직인다.
가져갈 한 문장
알림에서 발행 성공은 사용자 도착이 아니며, retry는 중복과 지연을 동시에 만들 수 있다.
Case 19. 결제 Provider Timeout 이후 주문 상태가 애매해진 상황
상황
결제 요청 중 provider read timeout이 발생했다. 사용자는 실패 화면을 봤지만 provider에서는 결제가 승인되었을 가능성이 있다. 운영자는 주문을 실패로 돌릴지 대기 상태로 둘지 결정해야 한다.
관찰할 증거 또는 확인할 단서
- connect timeout과 read timeout의 차이
- provider idempotency와 상태 조회 API
- 내부 주문 상태의 UNKNOWN 또는 PENDING 필요성
- 결제 승인에 fallback을 쓰면 안 되는 이유
- partition 상황에서 실패와 가능한 응답의 기준
먼저 생각해보기
결제 timeout은 실패가 확정된 상태가 아니다. 응답이 없을 뿐이고, side effect는 이미 발생했을 수 있다.
찾아볼 문서
- 01. Timeout 계층 설계 상세
- 03. Idempotency Key와 중복 요청 방어 상세
- 04. 외부 API 호출 안정성 상세
- 04. 분산 환경의 실패 모델 상세
- 04. Fallback Degradation 설계 상세
- 04. 분산 트랜잭션과 데이터 소유권 상세
판단 흐름
- timeout이 어느 구간에서 났는지 분리한다.
- idempotency key로 같은 결제 의도를 재사용할 수 있는지 본다.
- provider 조회나 webhook으로 상태를 보정할 수 있는지 확인한다.
- 내부 주문 상태를 UNKNOWN으로 두고 후속 확인 job을 둘지 결정한다.
- 결제 승인에는 가짜 성공 fallback을 쓰지 않는다.
정리
외부 결제 장애에서 가장 위험한 대응은 실패를 확정하는 것이다. 확인 가능한 중간 상태가 필요하다.
가져갈 한 문장
Timeout은 실패 응답이 아니라 불확실성 신호다.
Case 20. 피드 Timeline Freshness와 Fanout 비용을 조정하는 상황
상황
피드가 느리고, 인기 계정의 글이 올라오면 worker와 read model이 흔들린다. 최신성을 높이면 비용이 커지고, 비용을 줄이면 사용자는 오래된 timeline을 본다.
관찰할 증거 또는 확인할 단서
- write fanout과 read fanout 비용
- follower graph 크기와 유명 계정 예외
- queue lag, projection lag, timeline freshness
- read model rebuild 가능성
- stale 표시와 UX 계약
먼저 생각해보기
Feed는 단순 조회가 아니다. fanout 시점을 write 쪽에 둘지 read 쪽에 둘지에 따라 성능, 정합성, 운영 복잡도가 크게 바뀐다.
찾아볼 문서
- 03. Storage Bandwidth Memory 추정 상세
- 03. Event-driven 설계와 느슨한 결합 상세
- 05. 일관성 선택이 UX에 미치는 영향 상세
- 03. Projection Read Model 상세
- 04. 동기화 지연과 일관성 상세
- 04. Feed Timeline 설계 상세
판단 흐름
- fanout on write, fanout on read, hybrid 전략의 비용을 비교한다.
- 유명 계정과 일반 계정을 같은 경로에 둘지 분리한다.
- projection lag와 queue lag를 UX freshness 지표로 연결한다.
- rebuild와 replay가 가능한 read model인지 확인한다.
- 오래된 피드가 보일 때 사용자에게 어떻게 드러낼지 정한다.
정리
Feed의 핵심은 빠른 조회와 최신성 사이의 계약이다. 둘 다 완벽하게 얻으려면 운영 복잡도가 급격히 커진다.
가져갈 한 문장
Timeline은 저장된 목록이 아니라 freshness, fanout, rebuild 비용의 결과다.
Case 21. 대형 채팅방에서 Ordering과 Presence가 흔들리는 상황
상황
대형 채팅방에서 메시지가 순서대로 보이지 않고, 접속 중 표시도 자주 틀린다. WebSocket 서버를 더 늘리자는 의견이 있지만, 상태와 순서 기준은 아직 정리되지 않았다.
관찰할 증거 또는 확인할 단서
- 최종 진실의 원천으로서 저장된 메시지
- room별 ordering 기준
- presence TTL과 session 상태
- 대형방 fanout 비용과 backpressure
- shard key와 hot partition 가능성
먼저 생각해보기
Chat에서 실시간 전달은 중요하지만 최종 진실은 저장된 메시지다. Presence는 영구 상태가 아니라 TTL을 가진 부드러운 상태로 봐야 한다.
찾아볼 문서
- 01. Stateless 서버와 Session 분리 상세
- 03. Backpressure와 Queue 적체 상세
- 03. Partitioning과 Sharding 기본 상세
- 04. Rebalancing과 Hot Partition 상세
- 03. Bulkhead와 Resource Isolation 상세
- 03. Chat System 설계 상세
판단 흐름
- 메시지 저장 성공과 실시간 전달 성공을 분리한다.
- ordering 범위를 global이 아니라 room 또는 partition 기준으로 좁힌다.
- presence를 TTL 상태로 두고 틀릴 수 있는 범위를 표시한다.
- 대형방 fanout이 특정 worker나 shard를 과열시키는지 본다.
- backpressure와 bulkhead로 실시간 경로의 실패가 전체 기능을 굶기지 않게 한다.
정리
채팅은 실시간성이 눈에 띄지만, 운영에서는 저장, 순서, TTL 상태, 대형방 fanout이 더 자주 문제를 만든다.
가져갈 한 문장
Chat의 실시간 전달은 최종 진실이 아니라 저장된 메시지를 빠르게 보여주는 경로다.
Case 22. 가격 변경 후 일부 화면에 오래된 값이 보이는 상황
상황
상품 가격을 내렸는데 상세 화면에는 새 가격이 보이고, 추천 영역과 검색 결과에는 예전 가격이 남아 있다. 고객 문의가 들어오기 시작했다.
관찰할 증거 또는 확인할 단서
- 오래된 값인지, 틀린 값인지, 권한 밖의 값인지
- 캐시 key 범위와 version
- invalidation 누락 또는 순서 문제
- read model과 검색 색인의 반영 지연
- 전체 캐시 삭제의 위험
먼저 생각해보기
캐시 정합성 사고는 Redis만 보면 부족하다. 사용자에게 어떤 값이 어디서 어떻게 다르게 보였는지가 먼저다.
찾아볼 문서
- 02. TTL Eviction Invalidation 상세
- 04. 정합성 깨짐과 사용자 경험 상세
- 05. Redis Cache 실전 설계 상세
- 02. Strong Eventual Consistency 상세
- 03. Projection Read Model 상세
- 03. Cache 정합성 문제 대응 Runbook 상세
판단 흐름
- 사용자 영향이 결제, 가격, 권한처럼 되돌리기 어려운 영역인지 확인한다.
- 원본 데이터와 캐시, read model, 검색 색인을 순서대로 추적한다.
- key 단위 invalidation이 가능한지 보고, 전체 삭제는 마지막 선택으로 둔다.
- stale 허용 화면과 허용하지 않는 command를 분리한다.
- 재발 방지를 위해 key 설계와 invalidation owner를 문서화한다.
정리
가격은 단순 표시 정보가 아니다. 잘못 보이면 주문과 고객 신뢰로 이어진다.
가져갈 한 문장
캐시 사고 대응은 삭제 명령보다 영향 분류와 원본 추적이 먼저다.
Case 23. Queue Age가 늘고 Retry가 Provider를 더 압박하는 상황
상황
외부 메시지 provider가 429를 반환하고 있다. consumer는 실패한 메시지를 계속 retry하고, queue age는 계속 증가한다. consumer 수를 늘리자는 의견이 나온다.
관찰할 증거 또는 확인할 단서
- oldest message age와 enqueue/dequeue rate
- retry 후 성공률
- provider 429와 Retry-After
- DLQ 전환 기준
- producer 제한 또는 일시 중단 가능성
먼저 생각해보기
Queue 적체에서 consumer를 늘리는 것은 항상 좋은 대응이 아니다. 병목이 provider quota라면 consumer 증설은 downstream을 더 세게 때린다.
찾아볼 문서
- 04. Retry DLQ Ordering 중복 상세
- 05. Spring과 메시지 처리 설계 상세
- 05. 장애 증폭을 막는 호출 설계 상세
- 03. Backpressure와 Queue 적체 상세
- 02. Notification System 설계 상세
- 04. Queue 적체와 Retry 폭주 대응 Runbook 상세
판단 흐름
- 적체가 처리량 부족인지 downstream 제한인지 분리한다.
- retry 후 성공률이 낮으면 retry를 줄이거나 멈춘다.
- producer 유입 제한과 우선순위 분리를 먼저 검토한다.
- DLQ로 보낼 메시지와 나중에 replay할 메시지를 나눈다.
- replay 전에 정상 트래픽과 복구 트래픽이 충돌하지 않게 한다.
정리
Queue는 장애를 흡수하지만, retry 정책이 나쁘면 장애를 증폭한다.
가져갈 한 문장
Queue 적체의 첫 질문은 “consumer를 늘릴까?”가 아니라 “downstream이 더 받아도 되는가?”다.
Case 24. 주문 생성 후 목록에 바로 보이지 않는 상황
상황
사용자는 주문 생성 성공 응답을 받았는데, 주문 목록을 새로고침해도 방금 주문이 보이지 않는다. 관리자 통계도 몇 분 뒤 반영된다.
관찰할 증거 또는 확인할 단서
- read-after-write가 필요한 화면
- replica lag와 projection lag
- primary routing 또는 version token 전략
- stale 표시와 pending 상태
- command model과 query model의 분리
먼저 생각해보기
모든 화면에서 즉시 최신을 보장할 필요는 없다. 하지만 사용자가 방금 만든 주문은 곧바로 확인할 수 있어야 할 가능성이 높다.
찾아볼 문서
- 03. Read-after-write와 Monotonic Read 상세
- 05. 일관성 선택이 UX에 미치는 영향 상세
- 01. Read Write 분리 목적 상세
- 02. CQRS 기본과 적용 기준 상세
- 04. 동기화 지연과 일관성 상세
- 05. 복잡도를 감당할 수 있는 기준 상세
판단 흐름
- 주문 상세, 주문 목록, 관리자 통계의 일관성 요구를 분리한다.
- 방금 쓴 값만 primary로 읽을지, version token을 쓸지 판단한다.
- projection lag를 응답이나 화면 상태로 드러낸다.
- stale data를 숨기지 않고 pending, syncing 같은 상태로 표현한다.
- CQRS 복잡도를 운영할 준비가 있는지 확인한다.
정리
Read/write 분리 이후의 UX 문제는 자연스러운 비용이다. 비용을 숨길지, 계약으로 드러낼지가 설계 품질을 가른다.
가져갈 한 문장
사용자가 방금 쓴 값은 성능 최적화보다 신뢰의 문제다.
Case 25. App Instance를 늘렸는데 DB와 특정 Shard만 더 느려진 상황
상황
트래픽 증가에 대응해 Spring app pod를 2배로 늘렸다. app CPU는 낮아졌지만 DB connection이 포화되고, 특정 tenant가 있는 shard만 느려졌다.
관찰할 증거 또는 확인할 단서
- app pod 수와 connection pool 총합
- downstream budget
- tenant별, key별, shard별 latency
- hot partition 탐지 지표
- rebalancing과 routing table 변경 위험
먼저 생각해보기
App scale out은 app 병목에는 효과가 있지만 downstream 병목에는 더 많은 요청을 보낸다. hot partition은 전체 평균 지표로 잘 보이지 않는다.
찾아볼 문서
- 02. Stateful Component와 복구 비용 상세
- 01. Scale Up과 Scale Out 판단 상세
- 02. Stateless App 수평 확장 상세
- 03. Partitioning과 Sharding 기본 상세
- 04. Rebalancing과 Hot Partition 상세
- 05. 확장 전후 운영 비용 상세
판단 흐름
- scale out 이후 downstream 요청량이 얼마나 늘었는지 계산한다.
- connection pool 총합이 DB 한계를 넘었는지 확인한다.
- 평균 latency 대신 shard/key별 분포를 본다.
- hot key split, tenant 이동, routing table 변경의 비용을 비교한다.
- 확장 전후 운영 비용을 설계 문서에 남긴다.
정리
Scale out이 성공하려면 app만 stateless이면 부족하다. downstream도 늘어난 유입을 감당할 수 있어야 한다.
가져갈 한 문장
Pod를 늘리는 순간 DB와 shard는 더 많은 요청을 받는다.
Case 26. 배송 Provider 장애가 주문 상세 전체 지연으로 번지는 상황
상황
배송 provider가 2초 이상 느려지면서 주문 상세 API p95가 함께 올라갔다. 주문 상태와 결제 상태는 정상인데도 사용자는 주문 상세 전체를 느리게 경험한다.
관찰할 증거 또는 확인할 단서
- 배송 조회 latency와 주문 상세 전체 latency
- slow call rate와 timeout
- 공유 thread pool과 bulkhead rejected calls
- circuit breaker 상태 변화
shipping.status=DEGRADED같은 API 계약
먼저 생각해보기
부가 정보가 느려졌다고 핵심 화면 전체를 실패시킬 필요는 없다. 하지만 fallback을 정상 값처럼 숨기면 더 큰 신뢰 문제가 생긴다.
찾아볼 문서
- 05. 장애 증폭을 막는 호출 설계 상세
- 01. 장애 전파와 Cascading Failure 상세
- 02. Circuit Breaker 상태 모델 상세
- 03. Bulkhead와 Resource Isolation 상세
- 04. Fallback Degradation 설계 상세
- 05. Resilience4j 실전 예시 상세
판단 흐름
- 배송 provider 호출이 어느 리소스를 잡고 있는지 본다.
- 주문 핵심 데이터와 배송 부가 데이터를 분리한다.
- circuit breaker와 bulkhead 지표를 분리해서 본다.
- fallback reason을 API 응답에 드러낸다.
- retry를 줄일지, open 상태를 유지할지, half-open 시험을 제한할지 결정한다.
정리
외부 provider 장애는 감출수록 내부 장애처럼 보인다. 축소된 상태를 드러내야 사용자는 무엇이 정상인지 알 수 있다.
가져갈 한 문장
장애 격리는 실패를 숨기는 것이 아니라 실패 범위를 좁히는 일이다.
Case 27. “MSA로 나누면 확장된다”는 제안을 리뷰하는 상황
상황
팀에서 주문, 결제, 알림을 MSA로 나누면 성능과 개발 속도가 좋아질 것이라고 주장한다. 하지만 shared DB를 유지하고, 한 요청이 세 서비스를 동기로 호출하는 구조가 초안에 남아 있다.
관찰할 증거 또는 확인할 단서
- 분리 후보의 owner와 팀 책임
- shared DB 접근 여부
- sync fanout과 장애 전파
- 분산 transaction 대신 보상 처리 설계
- 관측, 배포, rollback 준비도
먼저 생각해보기
서비스 수를 늘리는 것과 운영 독립성을 얻는 것은 다르다. 데이터 owner와 배포 책임이 분리되지 않으면 MSA가 아니라 분산 Monolith에 가깝다.
찾아볼 문서
- 04. Data Ownership과 Service Boundary 상세
- 02. Modular Monolith 설계 상세
- 03. MSA 분리 기준 상세
- 04. 분산 트랜잭션과 데이터 소유권 상세
- 05. MSA 도입 실패 신호 상세
- 05. 설계 리뷰와 개선 포인트 상세
판단 흐름
- 분리 이유가 성능인지 변경 독립성인지 장애 격리인지 분리한다.
- shared DB와 cross-service table update가 남아 있는지 본다.
- sync 호출 사슬이 장애 전파를 키우는지 확인한다.
- Monolith 안에서 module 경계를 먼저 강제할 수 있는지 검토한다.
- MSA 도입 실패 신호를 checklist처럼 적용한다.
정리
MSA 제안은 “나누자”가 아니라 “무엇을 독립적으로 책임질 수 있는가”로 리뷰해야 한다.
가져갈 한 문장
MSA의 핵심은 서비스 개수가 아니라 owner와 운영 책임의 분리다.
Case 28. URL Shortener 설계 답안을 운영 관점에서 다듬는 상황
상황
스터디에서 URL Shortener 설계 답안을 냈다. DB, cache, redirect API는 있지만 click log, abuse, key 충돌, cache 정합성, 운영 지표가 약하다.
관찰할 증거 또는 확인할 단서
- redirect p95와 read path
- short key 충돌 방어
- cache aside와 TTL 기준
- click log의 비동기 분리
- abuse와 rate limit
먼저 생각해보기
URL Shortener는 생성 API보다 redirect read path가 핵심이다. 사용자는 redirect가 느리면 서비스 전체가 느리다고 느낀다.
찾아볼 문서
- 01. QPS TPS DAU MAU 추정 상세
- 01. Cache Aside Read Through Write Through 상세
- 01. Rate Limit 목적과 알고리즘 상세
- 01. URL Shortener 설계 상세
- 05. 설계 리뷰와 개선 포인트 상세
- 05. 설계 문서 작성 템플릿 상세
판단 흐름
- redirect latency와 availability를 먼저 고정한다.
- key 충돌의 최종 안전장치를 DB unique constraint로 확인한다.
- redirect path에서 click log를 동기 처리할지 분리할지 결정한다.
- abuse 요청을 rate limit으로 제어한다.
- 설계 문서에 선택 이유와 포기한 대안을 남긴다.
정리
URL Shortener는 작아 보이지만 read path, cache, abuse, log 분리가 잘 드러나는 문제다.
가져갈 한 문장
URL Shortener의 중심 경로는 생성이 아니라 redirect다.
Case 29. 설계 문서를 운영 인수인계 가능한 형태로 고치는 상황
상황
설계 문서에 “Redis 사용”, “Kafka 사용”, “MSA 전환” 같은 결론만 남아 있다. 왜 그 선택을 했고, 언제 되돌릴 수 있으며, 어떤 지표를 볼지는 빠져 있다.
관찰할 증거 또는 확인할 단서
- 배경과 문제, 목표와 비목표
- 선택지 비교와 포기한 대안
- 바뀌는 설계 축
- 관측 지표와 알림 기준
- rollback 조건과 미해결 질문
먼저 생각해보기
설계 문서는 승인을 받기 위한 보고서가 아니라 나중에 결정을 회수하기 위한 기록이다. 장애 때 이해할 수 없는 결정은 운영 비용이 된다.
찾아볼 문서
- 04. 개인 프로젝트와 기업 설계 수준 비교 상세
- 05. 요구사항 리뷰 템플릿 상세
- 05. 추정값을 설계 결정으로 연결 상세
- 05. 설계 리뷰와 개선 포인트 상세
- 01. 신규 기능 설계 리뷰 Runbook 상세
- 05. 설계 문서 작성 템플릿 상세
판단 흐름
- 결론 앞에 문제와 목표를 다시 쓴다.
- 선택지 비교 없이 기술 이름만 있는 문장을 찾는다.
- 각 결정이 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 바꾸는지 적는다.
- 지표와 rollback 조건을 붙인다.
- 미해결 질문을 숨기지 않고 다음 검증 항목으로 남긴다.
정리
운영 가능한 설계 문서는 멋진 구조도가 아니라 되짚을 수 있는 결정 기록이다.
가져갈 한 문장
좋은 설계 문서는 “무엇을 썼다”보다 “왜 그렇게 했고 언제 바꿀 것인가”를 남긴다.
Case 30. System Design 모의 면접 답변을 한 번 더 끌어올리는 상황
상황
모의 면접에서 컴포넌트 그림은 그렸지만 질문자가 “왜 queue인가요?”, “stale data는 허용되나요?”, “장애 때 무엇을 포기하나요?”라고 묻자 답변이 약해졌다.
관찰할 증거 또는 확인할 단서
- 요구사항을 구조로 번역했는지
- 트래픽 숫자가 구조 선택으로 이어졌는지
- 상태 owner와 일관성 선택이 있는지
- 장애 격리와 운영 지표가 있는지
- 설계 리뷰 질문에 답할 수 있는지
먼저 생각해보기
모의 면접 답변을 올리는 방법은 더 많은 컴포넌트를 외우는 것이 아니다. 각 선택의 이유와 비용을 말할 수 있어야 한다.
찾아볼 문서
- 00. System Design 전체 지도 압축 정리
- 05. System Design 학습 로드맵 상세
- 00. Traffic Estimation Capacity Planning 압축 정리
- 00. 실전 설계 사례 URL Shortener Notification Chat 압축 정리
- 05. 설계 리뷰와 개선 포인트 상세
- 00. System Design 실전 플레이북 압축 정리
판단 흐름
- 답변의 첫 문장을 사용자 성공 기준으로 바꾼다.
- 숫자를 말한 뒤 그 숫자가 cache, queue, shard, replica 중 무엇으로 이어지는지 설명한다.
- 각 컴포넌트가 새로 만드는 실패 비용을 같이 말한다.
- 장애 중 포기할 기능과 지킬 기능을 분리한다.
- 마지막에 관측 지표와 후속 runbook을 붙인다.
정리
좋은 System Design 답변은 정답 구조가 아니라 판단의 흔적을 보여준다.
가져갈 한 문장
컴포넌트 그림은 답변의 시작이 아니라 요구사항과 trade-off를 설명한 뒤 남는 결과다.
커버리지 지도
| 기존 문서 | 연결된 Case | 다시 읽는 이유 |
|---|---|---|
| 00. System Design 전체 지도 압축 정리 | Case 01, Case 30 | 전체 판단 축과 설계 언어를 회수한다. |
| 01. 요구사항에서 구조로 가는 흐름 상세 | Case 01, Case 16 | 기능 문장을 API, 상태, 병목, 장애 경계로 바꾼다. |
| 02. 백엔드 시스템 구성 요소 지도 상세 | Case 01 | 컴포넌트 이름보다 책임 계약을 먼저 본다. |
| 03. 성능 확장성 신뢰성 트레이드오프 상세 | Case 01, Case 17 | 성능 개선이 정합성, 장애 격리, 운영 복잡도를 어떻게 바꾸는지 본다. |
| 04. 개인 프로젝트와 기업 설계 수준 비교 상세 | Case 01, Case 29 | 같은 구조를 학습용 설명과 운영용 증거로 구분한다. |
| 05. System Design 학습 로드맵 상세 | Case 01, Case 30 | 기술 암기 대신 판단 순서로 학습 흐름을 잡는다. |
| 00. Functional Non-functional Requirement 압축 정리 | Case 02 | 기능과 품질 기준을 분리하는 전체 틀을 잡는다. |
| 01. Functional Requirement 정리 상세 | Case 02 | 기능을 endpoint, 상태 전이, 실패 응답으로 바꾼다. |
| 02. Non-functional Requirement 상세 | Case 02 | 빠름, 안정성 같은 표현을 측정 가능한 품질 속성으로 바꾼다. |
| 03. Latency Throughput Availability 상세 | Case 02 | latency, throughput, availability가 서로 다른 설계 질문임을 확인한다. |
| 04. Constraint Assumption 질문법 상세 | Case 02 | 제약과 가정을 분리하고 가정에 검증 계획을 붙인다. |
| 05. 요구사항 리뷰 템플릿 상세 | Case 02, Case 16, Case 29 | 빠진 요구사항을 강제로 드러내는 리뷰 장치로 쓴다. |
| 00. Traffic Estimation Capacity Planning 압축 정리 | Case 03, Case 30 | 사용자 수를 QPS, 저장량, 여유율, 설계 선택으로 연결한다. |
| 01. QPS TPS DAU MAU 추정 상세 | Case 03, Case 28 | 사용자 수를 초당 요청과 행동 빈도로 바꾼다. |
| 02. Read Write 비율과 Peak Traffic 상세 | Case 03, Case 16 | read/write peak와 hot row 위험을 분리한다. |
| 03. Storage Bandwidth Memory 추정 상세 | Case 03, Case 20 | 원본 저장량뿐 아니라 파생 데이터, 대역폭, cache memory를 본다. |
| 04. Capacity Planning과 여유율 상세 | Case 03, Case 17 | headroom, autoscaling 한계, backlog drain time을 판단한다. |
| 05. 추정값을 설계 결정으로 연결 상세 | Case 03, Case 29 | 숫자를 cache, queue, replica, partition, rate limit 결정으로 연결한다. |
| 00. Stateless Stateful Data Ownership 압축 정리 | Case 04 | 상태와 owner 판단의 전체 기준을 회수한다. |
| 01. Stateless 서버와 Session 분리 상세 | Case 04, Case 21 | 요청 사이 상태를 인스턴스 밖으로 옮기는 기준을 본다. |
| 02. Stateful Component와 복구 비용 상세 | Case 04, Case 25 | 상태 저장소별 RTO, RPO, 복구 비용을 붙인다. |
| 03. Data Ownership과 Service Boundary 상세 | Case 04, Case 27 | 최종 변경 권한과 service boundary를 결정한다. |
| 04. Session Store와 Sticky Session 판단 상세 | Case 04 | sticky, session store, token 방식을 전환 경로로 비교한다. |
| 05. 상태 관리 설계 실수 상세 | Case 04 | local lock, TTL 누락, ThreadLocal 같은 숨은 상태를 찾는다. |
| 00. Cache 전략과 정합성 압축 정리 | Case 05 | cache가 성능과 정합성을 함께 바꾸는 기준을 잡는다. |
| 01. Cache Aside Read Through Write Through 상세 | Case 05, Case 28 | cache fill, write 반영, 장애 fallback 책임 위치를 비교한다. |
| 02. TTL Eviction Invalidation 상세 | Case 05, Case 22 | TTL, eviction, invalidation을 서로 다른 문제로 분리한다. |
| 03. Cache Stampede와 Hot Key 상세 | Case 05, Case 17 | miss storm, hot key, penetration을 장애 경로로 본다. |
| 04. 정합성 깨짐과 사용자 경험 상세 | Case 05, Case 17, Case 22 | stale data를 사용자 경험과 API 계약으로 바꾼다. |
| 05. Redis Cache 실전 설계 상세 | Case 05, Case 22 | key, value, TTL, 장애 정책, 운영 지표를 설계 문서로 정리한다. |
| 00. Queue Async Event-driven Design 압축 정리 | Case 06 | queue 도입이 지연, 중복, 순서, 재처리 책임을 만든다는 큰 틀을 본다. |
| 01. Sync Async 처리 경계 상세 | Case 06 | 동기/비동기 경계를 사용자 성공 시점 기준으로 정한다. |
| 02. Queue Producer Consumer 기본 상세 | Case 06, Case 18 | 발행 성공, 저장, 처리, ack 성공을 분리한다. |
| 03. Event-driven 설계와 느슨한 결합 상세 | Case 06, Case 20 | 이벤트 계약, version, replay, backfill 책임을 본다. |
| 04. Retry DLQ Ordering 중복 상세 | Case 06, Case 18, Case 23 | retry, DLQ, ordering, 중복 처리의 trade-off를 본다. |
| 05. Spring과 메시지 처리 설계 상세 | Case 06, Case 23 | listener transaction, ack, 예외, concurrency를 운영 기준과 연결한다. |
| 00. Retry Timeout Idempotency 압축 정리 | Case 07 | timeout, retry, idempotency를 함께 봐야 하는 이유를 회수한다. |
| 01. Timeout 계층 설계 상세 | Case 07, Case 19 | 계층별 timeout 순서와 사용자 경험을 설계한다. |
| 02. Retry Backoff Jitter 상세 | Case 07, Case 18 | retry 대상, backoff, jitter, retry budget을 정한다. |
| 03. Idempotency Key와 중복 요청 방어 상세 | Case 07, Case 16, Case 19 | 같은 비즈니스 의도를 중복 실행하지 않게 한다. |
| 04. 외부 API 호출 안정성 상세 | Case 07, Case 19 | 외부 API timeout, UNKNOWN 상태, provider idempotency를 설계한다. |
| 05. 장애 증폭을 막는 호출 설계 상세 | Case 07, Case 23, Case 26 | retry storm과 replay 폭주를 제한한다. |
| 00. Rate Limit Backpressure 압축 정리 | Case 08 | rate limit과 backpressure의 전체 목적을 회수한다. |
| 01. Rate Limit 목적과 알고리즘 상세 | Case 08, Case 28 | 제한의 목적과 위치를 보호 대상 기준으로 정한다. |
| 02. Token Bucket Sliding Window 상세 | Case 08 | burst 허용과 최근 사용량 계산 방식에 맞는 알고리즘을 고른다. |
| 03. Backpressure와 Queue 적체 상세 | Case 08, Case 21, Case 23 | queue depth보다 처리율과 oldest age를 함께 본다. |
| 04. 사용자별 API별 제한 정책 상세 | Case 08, Case 16 | 사용자, tenant, API별 정책 기준을 나눈다. |
| 05. 429 응답과 클라이언트 계약 상세 | Case 08, Case 18 | 429가 retry storm이 되지 않게 Retry-After와 클라이언트 계약을 둔다. |
| 00. Consistency Availability Partition 압축 정리 | Case 09 | consistency와 availability 선택의 큰 틀을 본다. |
| 01. CAP를 실무 언어로 이해하기 상세 | Case 09 | partition 상황을 API 응답과 사용자 경험 언어로 바꾼다. |
| 02. Strong Eventual Consistency 상세 | Case 09, Case 22 | 데이터별 strong/eventual 선택과 conflict 비용을 비교한다. |
| 03. Read-after-write와 Monotonic Read 상세 | Case 09, Case 24 | 방금 쓴 값과 사용자가 본 값의 일관성 보장을 설계한다. |
| 04. 분산 환경의 실패 모델 상세 | Case 09, Case 19 | timeout, partial failure, unknown 상태를 정상 설계에 포함한다. |
| 05. 일관성 선택이 UX에 미치는 영향 상세 | Case 09, Case 20, Case 24 | eventual consistency를 UX 상태와 문구로 보상한다. |
| 00. Scale Up Scale Out Partitioning Sharding 압축 정리 | Case 10 | 확장 선택의 전체 trade-off를 회수한다. |
| 01. Scale Up과 Scale Out 판단 상세 | Case 10, Case 25 | 병목 위치 기준으로 scale up과 scale out을 비교한다. |
| 02. Stateless App 수평 확장 상세 | Case 10, Case 25 | pod 수 증가 전 local state, scheduler, connection 총합을 본다. |
| 03. Partitioning과 Sharding 기본 상세 | Case 10, Case 21, Case 25 | partitioning, sharding, shard key, routing 비용을 구분한다. |
| 04. Rebalancing과 Hot Partition 상세 | Case 10, Case 21, Case 25 | 평균 지표 뒤에 숨은 hot partition과 rebalancing 비용을 본다. |
| 05. 확장 전후 운영 비용 상세 | Case 10, Case 25 | 확장 후 배포, 관측, 백업, migration, 장애 대응 비용을 계산한다. |
| 00. Monolith Modular Monolith MSA 압축 정리 | Case 11 | 구조 선택을 성숙도 순서가 아닌 운영 판단으로 본다. |
| 01. Monolith의 장점과 한계 상세 | Case 11 | monolith의 단일 transaction과 운영 단순성을 인정한다. |
| 02. Modular Monolith 설계 상세 | Case 11, Case 27 | process는 하나로 두고 module 경계를 강제하는 기준을 본다. |
| 03. MSA 분리 기준 상세 | Case 11, Case 27 | service 분리 후보를 owner, 계약, 배포 책임으로 평가한다. |
| 04. 분산 트랜잭션과 데이터 소유권 상세 | Case 11, Case 19, Case 27 | 분산 transaction 대신 saga, outbox, idempotency, 보상을 설계한다. |
| 05. MSA 도입 실패 신호 상세 | Case 11, Case 27 | shared DB, sync fanout, owner 부재, 관측 부재를 실패 신호로 본다. |
| 00. Circuit Breaker Bulkhead 장애 격리 압축 정리 | Case 12 | circuit breaker, bulkhead, fallback의 전체 역할을 회수한다. |
| 01. 장애 전파와 Cascading Failure 상세 | Case 12, Case 26 | 외부 장애가 공유 리소스를 통해 전파되는 경로를 찾는다. |
| 02. Circuit Breaker 상태 모델 상세 | Case 12, Case 26 | closed/open/half-open 상태와 slow call 판단을 운영 지표로 본다. |
| 03. Bulkhead와 Resource Isolation 상세 | Case 12, Case 21, Case 26 | thread, connection, queue, tenant 리소스를 격리한다. |
| 04. Fallback Degradation 설계 상세 | Case 12, Case 17, Case 19, Case 26 | fallback이 가짜 성공이 되지 않게 축소 상태를 드러낸다. |
| 05. Resilience4j 실전 예시 상세 | Case 12, Case 26 | 요구사항에서 Resilience4j 설정, metric, 테스트를 끌어온다. |
| 00. Read Write 분리 CQRS 압축 정리 | Case 13 | read/write 분리와 CQRS의 전체 기준을 회수한다. |
| 01. Read Write 분리 목적 상세 | Case 13, Case 24 | 읽기 부하를 쓰기 불변식에서 떼어낼 근거를 확인한다. |
| 02. CQRS 기본과 적용 기준 상세 | Case 13, Case 24 | command model과 query model의 책임을 분리한다. |
| 03. Projection Read Model 상세 | Case 13, Case 20, Case 22 | projection, rebuild, lag, 멱등 갱신을 설계한다. |
| 04. 동기화 지연과 일관성 상세 | Case 13, Case 20, Case 24 | lag를 API와 UX 계약으로 드러낸다. |
| 05. 복잡도를 감당할 수 있는 기준 상세 | Case 13, Case 24 | projection worker, replay, DLQ, version을 운영할 준비를 확인한다. |
| 00. 실전 설계 사례 URL Shortener Notification Chat 압축 정리 | Case 14, Case 30 | 실전 사례별 핵심 trade-off를 비교한다. |
| 01. URL Shortener 설계 상세 | Case 14, Case 28 | redirect path, short key, click log, abuse를 설계한다. |
| 02. Notification System 설계 상세 | Case 14, Case 18, Case 23 | notification 상태, channel, provider 실패, retry를 본다. |
| 03. Chat System 설계 상세 | Case 14, Case 21 | 저장된 메시지, ordering, presence, 대형방 fanout을 본다. |
| 04. Feed Timeline 설계 상세 | Case 14, Case 20 | fanout on write/read, timeline 저장, freshness를 판단한다. |
| 05. 설계 리뷰와 개선 포인트 상세 | Case 14, Case 27, Case 28, Case 29, Case 30 | 답안의 요구사항, 병목, 상태 owner, 실패 모델, API 계약을 리뷰한다. |
| 00. System Design 실전 플레이북 압축 정리 | Case 15, Case 30 | 실전 대응에서 사용자 영향, 증거, 완화, 후속 조치를 먼저 본다. |
| 01. 신규 기능 설계 리뷰 Runbook 상세 | Case 15, Case 16, Case 29 | 신규 기능 승인·보류 기준과 위험한 빈칸을 확인한다. |
| 02. 트래픽 급증 대응 설계 Runbook 상세 | Case 15, Case 17 | 급증 상황에서 증설보다 병목과 traffic shed를 먼저 본다. |
| 03. Cache 정합성 문제 대응 Runbook 상세 | Case 15, Case 22 | 캐시 사고에서 영향 분류, key 추적, invalidation을 순서대로 수행한다. |
| 04. Queue 적체와 Retry 폭주 대응 Runbook 상세 | Case 15, Case 18, Case 23 | queue 적체, retry 폭주, DLQ, replay 순서를 정한다. |
| 05. 설계 문서 작성 템플릿 상세 | Case 15, Case 28, Case 29 | 결정 이유, 포기한 대안, 지표, rollback, 미해결 질문을 남긴다. |
마무리 사용법
이 문서를 읽고 끝내지 않는다. 실제 설계나 장애 상황을 만나면 먼저 비슷한 Case를 고른다. 그 다음 관찰할 증거를 채우고, 연결된 기존 문서를 다시 읽는다.
Case가 여러 문서를 가리키는 이유는 실제 문제가 한 챕터 안에서 끝나지 않기 때문이다. 캐시는 성능만 바꾸지 않고, queue는 비동기만 만들지 않으며, MSA는 서비스 수만 늘리지 않는다. 각 선택이 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 바꾸는지 끝까지 따라가야 한다.