이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 계산한 QPS, TPS, 저장량, 대역폭을 어떤 구조 선택으로 연결하는가?
- cache, queue, read replica, partition, rate limit은 각각 어떤 숫자에서 필요해지는가?
- 추정값이 불확실할 때 설계 문서에는 무엇을 남겨야 하는가?
개요
트래픽 추정의 실패는 계산을 못해서만 생기지 않는다.
더 흔한 실패는 계산한 숫자를 구조 결정으로 연결하지 못하는 것이다.
peak read 10,000 QPS라고 썼지만 cache hit ratio 목표가 없고, write 500 TPS라고 썼지만 idempotency나 DB write latency 기준이 없다면 설계 근거가 비어 있다.
추정값은 architecture diagram의 입력이어야 한다.
각 숫자는 어느 컴포넌트가 왜 필요한지, 그 컴포넌트를 넣으면 무엇이 새로 위험해지는지 설명해야 한다.
이 문서가 바꾸는 설계 축
| 축 | 바뀌는 내용 |
|---|---|
| 성능 | QPS, TPS, bandwidth를 cache, replica, queue, partition 기준으로 바꾼다. |
| 정합성 | cache, async, replica 도입으로 생기는 stale, duplicate, lag를 명시한다. |
| 장애 격리 | hot path와 batch, 핵심 기능과 부가 기능의 자원 경계를 나눈다. |
| 운영 복잡도 | 선택마다 필요한 metric, alert, rollback, 재처리 절차를 붙인다. |
판단 흐름
추정값을 설계로 연결할 때는 다음 순서가 좋다.
- 가장 큰 숫자를 찾는다.
- 가장 위험한 숫자를 따로 찾는다.
- 사용자가 기다려야 하는 경로와 기다리지 않아도 되는 경로를 나눈다.
- 병목 위치를 API, cache, DB, queue, external API, storage 중 하나로 가정한다.
- 구조 선택을 한 뒤 새로 생기는 실패 모드를 적는다.
- 운영 지표와 전환 조건을 남긴다. 가장 큰 숫자와 가장 위험한 숫자는 다를 수 있다. 피드 조회 20,000 QPS가 가장 크지만 결제 승인 200 TPS가 더 위험할 수 있다.
대표 시나리오
커머스 홈, 상품 상세, 주문, 알림을 함께 설계한다고 하자.
| 항목 | 추정값 |
|---|---|
| 홈 피드 조회 | 20,000 QPS |
| 상품 상세 조회 | 12,000 QPS |
| 검색 | 2,000 QPS |
| 주문 생성 | 500 TPS |
| 결제 승인 | 300 TPS |
| 알림 이벤트 발행 | 5,000 msg/sec |
| 신규 저장량 | 450GB/day |
| outbound traffic | 1.2Gbps peak |
이 숫자를 보고 바로 Redis, Kafka, Read Replica를 나열하면 부족하다. | |
| 각 선택이 어떤 숫자를 낮추고 어떤 비용을 만드는지 연결해야 한다. |
숫자에서 선택으로 가는 표
| 관찰한 숫자 | 먼저 의심할 병목 | 선택 후보 | 새로 생기는 위험 |
|---|---|---|---|
| read QPS가 DB read capacity를 넘는다 | DB read, connection pool | cache, read replica, precomputed read model | stale data, cache stampede, replica lag |
| write TPS가 lock wait를 만든다 | hot row, index write | queue, partition, optimistic lock, reservation | 대기, 중복, 순서 문제 |
| outbound가 높다 | network, egress cost | response trimming, compression, CDN | freshness, cache invalidation |
| 저장량 증가가 빠르다 | disk, backup, migration | retention, table partition, object storage | 조회 복잡도, 재처리 절차 |
| external API quota에 가깝다 | downstream limit | rate limit, bulkhead, async call | 지연, fallback 품질 |
| queue age가 증가한다 | consumer 부족 | consumer scale-out, batch size 조정 | 순서, 중복, downstream 압박 |
| 이 표는 정답표가 아니다. | |||
설계 리뷰에서 이 숫자 때문에 이 선택을 했다고 설명하기 위한 연결표다. |
cache 선택 기준
홈 피드 20,000 QPS를 DB가 직접 처리하지 못한다고 하자. 목표 DB read QPS를 1,000 이하로 두면 필요한 cache hit ratio는 다음과 같다.
peak_read_qps = 20,000
target_db_qps = 1,000
required_hit_ratio = 1 - (1,000 / 20,000)
= 0.95hit ratio 95%가 목표라면 TTL, key 설계, warm-up, stampede 방지가 필요하다. cache 도입은 성능을 바꾸지만 정합성과 운영 복잡도를 늘린다. 피드가 몇 초 늦어도 되는지, 사용자가 새 글을 쓴 직후 자신의 글을 볼 수 있어야 하는지 API 계약에 남겨야 한다.
read replica 선택 기준
상품 상세 조회 12,000 QPS가 대부분 DB read라면 read replica가 후보가 된다. 하지만 replica는 모든 읽기 문제의 답이 아니다.
primary_write_tps = 500
replication_lag_p95 = 800ms
product_detail_freshness_requirement = 5s
order_status_freshness_requirement = 0s상품 상세는 5초 지연을 허용할 수 있다. 주문 상태는 사용자가 방금 결제한 결과를 확인하므로 primary read나 read-your-write 전략이 필요하다. replica는 성능과 장애 격리에 도움을 주지만 read-after-write 정합성을 바꾼다.
queue 선택 기준
알림 이벤트 5,000 msg/sec를 사용자 요청 안에서 모두 처리하면 latency가 커진다. 알림 발송은 사용자가 주문 성공을 보는 데 필수인 작업이 아니다. 그래서 queue로 분리할 수 있다.
event_inflow = 5,000 msg/sec
consumer_capacity = 6,000 msg/sec
peak_duration = 15 minutes
downstream_provider_limit = 2,000 msg/secconsumer가 6,000 msg/sec를 처리할 수 있어도 provider limit이 2,000이면 실제 발송은 거기서 막힌다. queue는 API latency를 줄이지만 downstream 한계를 없애지 않는다. provider limit에 맞춘 rate limit과 oldest message age alert가 필요하다.
partition 선택 기준
저장량 450GB/day가 단일 테이블에 계속 쌓이면 backup, vacuum, index rebuild, 삭제가 어려워진다. 시간 기준 조회가 많고 오래된 데이터 삭제가 필요하다면 날짜 partition을 검토한다.
CREATE TABLE order_event (
event_id BIGINT NOT NULL,
order_id BIGINT NOT NULL,
occurred_at TIMESTAMP NOT NULL,
payload JSONB NOT NULL
) PARTITION BY RANGE (occurred_at);partition은 저장량 관리를 쉽게 하지만 query 조건과 운영 절차를 강제한다. 모든 쿼리가 partition key를 타지 않으면 오히려 성능이 나빠질 수 있다. partition 생성, 삭제, 통계 갱신을 운영 작업으로 관리해야 한다.
rate limit 선택 기준
검색 2,000 QPS가 외부 검색 cluster를 포화시킨다면 API 서버 증설로 해결되지 않는다. downstream capacity가 설계 기준이다.
limits:
search:
global-qps: 1800
per-user-qps: 5
burst: 10
retry-after-seconds: 2rate limit은 장애 격리를 높인다. 대신 일부 사용자는 429를 받거나 느린 응답을 경험한다. 그래서 retry-after, error body, client retry 정책까지 API 계약으로 남긴다.
결정 기록 예시
설계 문서에는 기술 이름보다 근거가 먼저 나와야 한다.
decision:
problem: home feed peak read exceeds DB read budget
estimate:
peak_read_qps: 20000
target_db_qps: 1000
required_cache_hit_ratio: 0.95
choice: cache-aside with 30s TTL and warm-up
trade_off:
improves: read latency, DB protection
worsens: freshness, invalidation complexity
metrics:
- feed_cache_hit_ratio
- feed_db_read_qps
- feed_stale_age_p95이 정도 기록이 있으면 나중에 숫자가 바뀌었을 때 결정을 다시 평가할 수 있다.
왜 Redis를 썼는가가 아니라 어떤 DB read budget을 지키려고 Redis를 썼는가가 남는다.
불확실한 값 다루기
초기 설계에서는 모든 숫자가 추정이다. 불확실성을 숨기지 말고 범위로 적는다.
| 값 | 낮은 추정 | 높은 추정 | 검증 방법 |
|---|---|---|---|
| peak home QPS | 8,000 | 20,000 | beta traffic, load test |
| order TPS | 100 | 500 | campaign 예약 수 |
| payload size | 1KB | 6KB | production sample |
| cache hit ratio | 80% | 95% | canary, dashboard |
| 범위를 쓰면 과한 설계와 부족한 설계를 비교할 수 있다. | |||
| 그리고 어느 값이 실제 측정으로 바뀌어야 하는지도 명확해진다. |
리뷰 체크리스트
- 가장 큰 QPS와 가장 위험한 TPS를 따로 골랐는가?
- 각 숫자가 어떤 컴포넌트의 한계를 건드리는지 적었는가?
- cache, queue, replica, partition, rate limit의 도입 이유가 숫자로 설명되는가?
- 선택마다 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇이 바뀌는지 적었는가?
- 새로 생긴 실패 모드에 대한 metric과 rollback 기준이 있는가?
- 추정값의 출처와 나중에 측정으로 바꿀 방법이 있는가?
개인 프로젝트 기준
- 숫자 하나당 구조 선택 하나 이상을 연결해 설명한다.
트래픽이 많아서 Redis 사용대신DB read를 1,000 QPS 이하로 낮추기 위해 hit ratio 95% 목표처럼 쓴다.- queue를 쓴다면 어떤 작업을 사용자 응답 밖으로 빼는지 명시한다.
- 복잡한 기술을 생략했다면 현재 추정값에서 생략 가능한 이유와 전환 조건을 남긴다.
기업 운영 기준
- 추정값, 선택, metric, owner, alert, rollback을 하나의 decision record로 관리한다.
- 출시 후 실제 트래픽으로 추정값을 보정하는 날짜를 둔다.
- 비용이 큰 선택은 낮은 추정과 높은 추정을 모두 검토한다.
- SLO에 영향을 주는 선택은 장애 시뮬레이션과 load test 결과를 붙인다.
- capacity 관련 결정은 제품 일정, 마케팅 캠페인, 데이터 보존 정책과 같이 리뷰한다.
위험 신호
- 계산한 QPS가 구조 그림 어디에도 연결되지 않는다.
- cache, queue, replica, shard를 기술 목록처럼 나열한다.
- 선택의 장점만 있고 stale, lag, duplicate, backlog 같은 비용이 없다.
- peak read와 critical write를 같은 방식으로 scale-out한다고 설명한다.
- external API quota를 내부 서버 증설로 해결하려 한다.
- 추정값이 틀렸을 때 어떤 metric으로 알 수 있는지 없다.
확인 질문
확인 질문
- 확인 질문: 추정값을 설계 결정으로 연결한다는 말의 의미는 무엇인가?
- 답변: 숫자가 어느 컴포넌트의 한계를 건드리는지 밝히고, 그 한계를 낮추기 위한 선택과 새로 생기는 비용을 함께 적는다는 뜻이다.
- 확인 질문: cache 도입을 숫자로 설명하려면 무엇이 필요한가?
- 답변: peak read QPS, 목표 DB read QPS, 필요한 cache hit ratio, freshness 허용 범위가 필요하다.
- 확인 질문: 이 문서의 핵심 trade-off는 무엇인가?
- 답변: 추정값을 구조 선택으로 바꾸면 성능과 장애 격리는 좋아질 수 있지만 정합성 약화와 운영 절차 증가를 함께 감당해야 한다는 점이다.