시스템 설계의 모든 결정은 하나의 질문으로 귀결된다
확장성과 가용성의 수학적 계산부터 CAP 트레이드오프, 데이터 모델 선택, 면접 프레임워크까지 — 분산 시스템 설계의 핵심 원칙을 추적한다.
- 01 시스템 설계의 모든 결정은 하나의 질문으로 귀결된다
- 02 트래픽 진입점 설계 — DNS부터 스토리지까지
- 03 시스템 설계 면접의 공통 문법
- 04 대규모 서비스 설계의 공통 언어
- 05 대규모 데이터 시스템, 무엇을 포기하고 무엇을 얻는가
- 06 시스템은 어떻게 실패하지 않는가
- 07 아키텍처 결정은 어떻게 기억되는가
서버를 늘렸는데 왜 느린가. 가용성을 99.99%로 높이려면 비용이 얼마나 드는가. MySQL 대신 MongoDB를 쓰면 정말 빠른가. 이 질문들은 표면적으로는 다 달라 보이지만, 사실 하나의 질문으로 수렴한다. “무엇을 포기하고 무엇을 얻는가?” 시스템 설계의 모든 결정은 트레이드오프다.
확장성: 서버를 늘리기 전에 물어야 할 것
수직 확장(Scale Up)은 단순하다. 서버 스펙을 올리면 된다. 문제는 비용이 선형이 아니라는 것이다. 성능 2배를 위해 비용 4배를 지불하고, 결국 물리적 상한에 도달한다.
수평 확장(Scale Out)은 이론적으로 무한하다. 하지만 조건이 있다 — 애플리케이션이 Stateless해야 한다. 세션을 서버 로컬 메모리에 저장하면, 어떤 서버가 요청을 받느냐에 따라 결과가 달라진다. 서버를 10대로 늘려도 같은 사용자는 항상 같은 서버(Sticky Session)로 보내야 하고, 특정 서버에 트래픽이 몰리면 수평 확장의 의미가 사라진다.
해결은 단순하다. 세션을 Redis로 옮기면 어느 서버가 요청을 받아도 동일한 결과를 반환한다. Stateless 전환이 수평 확장의 선결 조건이다.
로드밸런서 알고리즘도 같은 맥락이다. Round Robin은 서버 스펙이 동일하고 요청 처리 시간이 비슷할 때 유효하다. 처리 시간 편차가 크면 Least Connection이 낫고, 서버 로컬 캐시를 활용해야 하는 레거시라면 IP Hash가 현실적인 선택이다. “어떤 알고리즘이 최선인가”가 아니라 “지금 상황에서 무엇이 맞는가”를 묻는 것이 올바른 사고다.
가용성: 숫자 하나가 아키텍처를 바꾼다
“99% 가용성”은 괜찮게 들린다. 실제로는 연간 87.6시간, 약 3.6일이 다운이다. 99.9%와 99.99%의 차이는 “9” 하나지만, 연간 허용 다운타임은 8.77시간 대 52.6분이다.
가용성 계산에서 핵심은 직렬과 병렬의 차이다. 컴포넌트를 직렬로 연결하면 전체 가용성은 곱셈으로 떨어진다.
99.9% 서비스 5개를 직렬 호출하면 가 된다. 반면 병렬(이중화)은 지수적으로 향상된다. 99.9% 서버 2대를 병렬로 두면 전체 다운 확률은 , 가용성은 99.9999%다.
이중화 비용은 선형으로 증가하지 않는다. 99.9% → 99.99%로 올리는 것과 99.99% → 99.999%로 올리는 것의 비용 차이는 크다. 다운타임 1시간당 매출 손실이 이중화 추가 비용보다 클 때만 투자가 합리적이다.
SLA, SLO, SLI의 구분도 중요하다. SLI는 실제로 측정하는 지표, SLO는 내부 목표, SLA는 고객과의 계약이다. SLA는 SLO보다 느슨하게 설정해 버퍼를 확보한다. SLO가 99.9%면 SLA는 99.5%로 두어 예상치 못한 장애에도 계약 위반을 피한다.
일관성과 가용성: 네트워크 장애가 강제하는 선택
CAP Theorem은 분산 시스템에서 Consistency, Availability, Partition Tolerance 셋 중 둘만 선택할 수 있다고 말한다. 실제로는 네트워크 파티션(P)이 피할 수 없으므로, 선택지는 C와 A 사이의 트레이드오프다.
네트워크가 분리된 상황에서 한 노드에 쓰기가 발생하면 다른 노드는 오래된 값을 갖는다. CP 시스템(MySQL)은 이 상황에서 일관성을 위해 쓰기를 거부한다. AP 시스템(Redis Cluster)은 가용성을 위해 오래된 값이라도 응답한다.
설계 결정의 기준은 하나다. “이 데이터가 일시적으로 틀렸을 때 실제 비즈니스 피해가 있는가?” 결제 잔액이 1초라도 틀리면 이중 결제가 발생하므로 CP가 필요하다. SNS 타임라인이 2초 늦게 업데이트되어도 사용자는 모른다. 같은 서비스 안에서도 기능마다 다른 일관성 수준을 선택하는 것이 실용적이다.
PACELC는 CAP의 한계를 보완한다. 파티션 상황뿐 아니라 정상 상태에서도 Latency와 Consistency 사이의 트레이드오프가 존재한다. Cassandra는 AP처럼 보이지만 Consistency Level을 QUORUM으로 올리면 지연이 늘어난다. “이 시스템은 AP다”로 끝내는 것보다, 정상 상태의 동작까지 따져보는 것이 정확한 이해다.
지연시간과 처리량: 평균이 숨기는 것
“평균 응답시간 50ms”는 거짓말이다. 상위 1% 사용자가 5000ms를 경험하고 있다면 DAU 100만 기준 매일 1만 명이 5초를 기다린다. 평균은 이 사실을 완벽히 숨긴다.
P99 지연시간이 진짜 지표다. P50은 일반적인 사용자 경험, P99는 최악의 경험이다. SLO는 P95 또는 P99 기준으로 작성해야 실제 사용자 경험을 반영한다.
처리량과 지연시간은 반비례하지 않는다. 리틀의 법칙()에 따르면, 처리량을 최적점 이상으로 높이면 큐에 요청이 쌓이고 지연시간이 급증한다. 운영 목표는 최적점의 70~80% 수준에서 유지하는 것이다. 배치 처리는 처리량을 높이는 대신 지연시간을 희생한다. Kafka의 linger.ms 설정이 정확히 이 트레이드오프다.
데이터 모델: “NoSQL이 빠르다”는 절반만 맞다
NoSQL은 “RDB의 제약을 포기하는 대신 특정 영역에서 이득을 얻는 것”이다. Redis가 MySQL보다 빠른 것은 메모리 기반이기 때문이지, NoSQL이라서가 아니다. MongoDB vs MySQL을 단순 Primary Key 조회로 비교하면 MySQL이 더 빠른 경우도 많다.
선택 기준은 접근 패턴이다. Key로 단순 조회하면 Key-Value(Redis), 중첩 구조와 유연한 스키마가 필요하면 Document(MongoDB), 대용량 쓰기와 시계열이면 Column-Family(Cassandra), 관계 탐색이 핵심이면 Graph(Neo4j), 복잡한 관계와 트랜잭션이 필요하면 RDB(MySQL/PostgreSQL).
실제 대형 서비스는 하나의 DB만 쓰지 않는다. 핵심 비즈니스 데이터(결제, 계정)는 RDB로 정확성을 보장하고, 세션과 캐시는 Redis, 대용량 이벤트 로그는 Cassandra, 파일은 Object Storage로 분리한다. DB 선택은 나중에 되돌리기 매우 어렵다. 스키마 마이그레이션과 데이터 이전은 수개월의 작업이 된다.
정리
- 수평 확장의 선결 조건은 Stateless다. 세션을 외부로 빼지 않으면 서버를 아무리 늘려도 확장이 안 된다.
- 가용성은 직렬 연결로 떨어지고 병렬(이중화)로 올라간다. 숫자 하나 차이가 비용과 아키텍처를 완전히 바꾼다.
- CAP에서 C와 A의 선택 기준은 “데이터가 일시적으로 틀렸을 때 실제 피해가 있는가”다.
- 평균 지연시간은 거짓말한다. P95, P99를 봐야 실제 사용자 경험이 보인다.
- DB는 접근 패턴을 먼저 분석하고 선택하라. 나중에 바꾸는 비용은 처음 선택보다 훨씬 크다.
다음 글에서는 이 원칙들이 실제 인프라 컴포넌트(DNS, 로드밸런서, CDN)에서 어떻게 구현되는지 추적한다.