이 지도를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 요구사항은 어떻게 DB, Cache, Queue, Scale-out 같은 설계 선택으로 이어지는가?
  • 성능, 정합성, 가용성, 운영 복잡도는 어떤 trade-off를 만드는가?
  • Cache와 Queue는 언제 도입하고, 언제 오히려 복잡도를 키우는가?
  • 설계 선택이 잘못되었는지 어떤 지표로 확인할 수 있는가?

flowchart TB

subgraph REQUIREMENT["Requirement"]
direction TB
FUNC["[FunctionalReq]<br/>무엇을 해야 하는가<br/>예: 주문, 알림"]
NFR["[NonFunctionalReq]<br/>어느 수준이어야 하는가<br/>예: latency, availability"]
TRAFFIC["[TrafficEstimate]<br/>읽기·쓰기·피크 추정<br/>예: QPS, TPS"]
end

subgraph CORE_DECISION["Core Design Choice"]
direction TB
DB[("[DB]<br/>정합성·영속성 중심<br/>예: transaction")]
CACHE[("[Cache]<br/>읽기 지연 감소<br/>예: Redis")]
QUEUE[["[Queue]<br/>비동기·장애 격리<br/>예: event, command"]]
SCALE["[ScaleOut]<br/>처리량 확장<br/>예: stateless app"]
end

subgraph RELIABILITY["Reliability Choice"]
direction TB
CONSISTENCY{"[Consistency]<br/>강한/최종 정합성 선택<br/>예: serializable, eventual"}
IDEMPOTENCY{"[Idempotency]<br/>중복 요청·재시도 방어<br/>예: request id"}
RETRY[["[RetryTimeout]<br/>실패 제어<br/>예: backoff, timeout"]]
AVAIL["[Availability]<br/>부분 장애에도 서비스 유지<br/>예: degrade mode"]
end

subgraph OBSERVE["Validation"]
direction TB
METRICS[["[Metrics]<br/>설계 검증 지표<br/>예: latency, error"]]
SLO[["[SLO]<br/>사용자 기준 목표<br/>예: 99.9 percent"]]
REVIEW["[DesignReview]<br/>선택 이유·위험 기록<br/>예: ADR, RFC"]
end

FUNC -->|define behavior| DB
NFR -->|set target| SLO
TRAFFIC -->|estimate load| CACHE
TRAFFIC -->|estimate async load| QUEUE
TRAFFIC -->|estimate capacity| SCALE
DB ==>|transaction boundary| CONSISTENCY
CACHE -.->|may return stale data| CONSISTENCY
QUEUE -.->|eventual processing| CONSISTENCY
QUEUE -.->|redelivery risk| IDEMPOTENCY
RETRY -.->|may duplicate request| IDEMPOTENCY
SCALE -->|requires statelessness| AVAIL
CONSISTENCY -->|trade-off record| REVIEW
IDEMPOTENCY -->|safe retry design| REVIEW
AVAIL -->|degrade mode| REVIEW
CACHE -.->|observe hit/stale| METRICS
QUEUE -.->|observe depth/lag| METRICS
DB ==>|observe lock/pool| METRICS
SCALE -.->|observe saturation| METRICS
METRICS -->|compare target| SLO
SLO -->|feedback| REVIEW

classDef server fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20;
classDef data fill:#FFF3E0,stroke:#EF6C00,color:#E65100;
classDef event fill:#F3E5F5,stroke:#6A1B9A,color:#4A148C;
classDef ops fill:#ECEFF1,stroke:#455A64,color:#263238;
classDef alert fill:#FFEBEE,stroke:#C62828,color:#B71C1C;

class FUNC,NFR,TRAFFIC,SCALE,AVAIL server;
class DB,CACHE data;
class QUEUE,RETRY event;
class METRICS,SLO,REVIEW ops;
class CONSISTENCY,IDEMPOTENCY alert;

linkStyle 0,1,2,3,4,10,18,19 stroke:#2E7D32,stroke-width:2px;
linkStyle 5,16 stroke:#EF6C00,stroke-width:3px;
linkStyle 6,7,8,9,14,15,17 stroke:#6A1B9A,stroke-width:2px,stroke-dasharray:5 5;
linkStyle 11,12,13 stroke:#455A64,stroke-width:2px;

이 지도는 시스템 설계를 기술 나열이 아니라 요구사항에서 선택으로 이어지는 흐름으로 보는 관점이다. 기능 요구사항은 무엇을 해야 하는지를 정하고, 비기능 요구사항은 얼마나 빠르고 안정적이어야 하는지를 정한다. 트래픽 추정은 그 요구를 감당하기 위해 어떤 구성요소가 필요한지 판단하게 해준다.

DB는 정합성과 영속성을 담당하지만, 모든 부하를 DB에만 두면 병목이 될 수 있다. Cache는 읽기 지연을 줄이지만 stale data와 무효화 문제가 생긴다. Queue는 응답과 후속 작업을 분리하지만 중복 처리와 지연 처리 문제가 생긴다. Scale-out은 처리량을 늘리지만 stateless 설계와 운영 복잡도를 요구한다.

중요한 것은 선택 자체보다 선택의 이유와 trade-off를 기록하는 것이다. 설계 선택은 Metrics와 SLO로 검증되어야 한다. cache hit ratio, queue depth, DB lock wait, p95 latency 같은 지표가 설계가 버티는지 알려준다.

장애가 났을 때 먼저 확인할 것

  • 어떤 사용자 요구사항이나 SLO가 깨졌는지 확인한다.
  • latency 문제인지, error 문제인지, saturation 문제인지 먼저 나눈다.
  • DB lock/pool, cache miss/stale, queue depth/lag, app saturation을 비교한다.
  • retry가 장애를 줄이는지 증폭시키는지 확인한다.
  • 임시 조치 후 설계 선택의 trade-off가 기록되어 있는지 확인한다.

설계할 때 선택 기준

  • 강한 정합성이 필요하면 DB 트랜잭션과 제약을 우선한다.
  • 읽기 부하가 크고 약간의 stale을 허용하면 Cache를 검토한다.
  • 오래 걸리거나 재시도 가능한 작업은 Queue로 분리한다.
  • Scale-out은 앱이 stateless하거나 상태를 외부 저장소로 분리할 수 있을 때 효과적이다.
  • retry를 넣는 작업은 idempotency를 먼저 설계한다.

운영 중 볼 지표

  • p95/p99 latency, throughput, 4xx/5xx rate, timeout count
  • DB connection pool usage, lock wait time, slow query count
  • cache hit ratio, stale data report, eviction count
  • queue depth, consumer lag, retry count, DLQ count
  • SLO compliance, error budget burn, saturation trend

흔한 오해

  • Cache를 붙이면 성능 문제가 자동으로 해결된다고 생각하기 쉽다.
  • Queue를 쓰면 장애가 사라지는 것이 아니라 다른 실행 모델로 이동한다.
  • Scale-out은 DB나 외부 API 병목을 해결하지 못할 수 있다.
  • CAP 같은 개념은 외우는 것보다 사용자 경험과 데이터 정합성 선택으로 해석해야 한다.
  • 설계 문서가 없으면 나중에 왜 그 선택을 했는지 운영자가 알 수 없다.

실전 체크리스트

  • 기능 요구사항과 비기능 요구사항이 분리되어 있는가?
  • 읽기/쓰기 비율과 피크 트래픽을 추정했는가?
  • Cache, Queue, Scale-out을 도입하는 이유와 비용을 적었는가?
  • retry가 있는 곳에 idempotency가 있는가?
  • 설계 선택을 검증할 지표와 SLO가 정의되어 있는가?

질문에 대한 답변

  1. 요구사항은 어떻게 DB, Cache, Queue, Scale-out 같은 설계 선택으로 이어지는가?

    기능 요구사항은 필요한 데이터와 동작을 정하고, 비기능 요구사항과 트래픽 추정은 성능·가용성 목표를 정한다. 이 목표에 따라 DB 트랜잭션, Cache, Queue, Scale-out 같은 선택지가 결정된다.

  2. 성능, 정합성, 가용성, 운영 복잡도는 어떤 trade-off를 만드는가?

    Cache는 성능을 높이지만 정합성 비용이 생긴다. Queue는 가용성과 응답성을 높이지만 지연과 중복 처리를 감당해야 한다. Scale-out은 처리량을 늘리지만 운영 복잡도가 증가한다.

  3. Cache와 Queue는 언제 도입하고, 언제 오히려 복잡도를 키우는가?

    Cache는 반복 조회가 많고 stale을 허용할 때 유리하다. Queue는 오래 걸리거나 재시도 가능한 작업에 유리하다. 정합성이 강하게 필요하거나 작업 순서와 즉시 결과가 중요하면 오히려 복잡도를 키울 수 있다.

  4. 설계 선택이 잘못되었는지 어떤 지표로 확인할 수 있는가?

    cache hit ratio가 낮거나 stale report가 많고, queue depth와 lag가 계속 늘거나, DB lock wait과 pool usage가 치솟으면 설계 선택을 다시 봐야 한다. SLO 위반이 반복되는 지표가 가장 강한 신호다.

관련 상세 문서

참고한 자료