스터디 노트

Chapter 01

사용자 수에 따른 규모 확장성

단일 서버에서 시작해 수백만 사용자를 지원하는 시스템으로 확장하는 과정. 수직/수평 확장, 로드 밸런서, DB 복제, 캐시, CDN, 무상태 계층, 데이터 센터, 메시지 큐, 로깅·모니터링 등을 단계별로 도입.

개요

단일 서버 구성에서 출발해 수백만 사용자를 지원하는 시스템으로 점진적으로 확장하는 과정을 따라간다. 각 단계에서 어떤 병목이 발생하고, 그것을 어떻게 해소하며 다음 단계로 나아가는지가 핵심이다.

단일 서버

모든 컴포넌트(웹 서버, 애플리케이션, DB, 캐시 등)가 한 대의 서버에 모여 있는 가장 단순한 구성이다. 요청 흐름은 다음과 같다.

  1. 사용자가 도메인 이름으로 접속 → DNS에 질의해 IP를 얻는다.
  2. 얻은 IP로 HTTP 요청 → 웹 서버가 HTML/JSON을 돌려준다.

장점은 단순함, 단점은 명확하다 — 트래픽이 조금만 늘어도 한계에 부딪히고, 서버가 죽으면 서비스 전체가 중단된다.

데이터베이스

사용자가 늘어나면 가장 먼저 해야 할 일은 웹 계층과 데이터 계층을 분리하는 것이다. 각 계층을 독립적으로 확장할 수 있기 때문이다.

어떤 데이터베이스를 쓸 것인가?

  • 관계형(RDBMS): MySQL, PostgreSQL, Oracle 등. 행과 열로 구성된 테이블에 저장하고 SQL로 조인·집계를 수행한다. 대부분의 애플리케이션에 적합한 안전한 선택.
  • 비관계형(NoSQL): DynamoDB, Cassandra, MongoDB 등. 키-값, 문서, 그래프, 컬럼 스토어 등 다양한 모델. 아래 경우에 고려한다.
    • 아주 낮은 응답 지연이 필요할 때
    • 데이터가 비정형이거나 관계가 없을 때
    • JSON/XML/YAML 같은 형식만 직렬화·역직렬화하면 될 때
    • 엄청난 양의 데이터를 저장해야 할 때

수직적 규모 확장 vs 수평적 규모 확장

  • 수직적 확장(Scale Up): 한 서버의 CPU·메모리·디스크를 더 좋은 것으로 교체한다. 트래픽이 적을 때는 단순하고 효과적이지만 — 한계가 있고(하드웨어 상한), 장애 복구/다중화가 어렵다.
  • 수평적 확장(Scale Out): 서버 대수를 늘려 부하를 분산한다. 대규모 서비스에서 사실상 유일한 선택지. 대신 로드밸런싱, 세션 관리, DB 동기화 같은 새로운 문제가 생긴다.

로드밸런서

부하 분산 장치는 여러 웹 서버 앞에 두고, 들어오는 요청을 고르게 나눠준다. 사용자는 로드밸런서의 공개 IP에만 접속하고 실제 서버는 사설 IP로 숨긴다. 서버 한 대가 다운돼도 나머지로 트래픽이 돌아가므로 고가용성이 확보된다.

데이터베이스 다중화(Replication)

웹 계층은 로드밸런서로 분산했지만 DB는 여전히 단일 장애점(SPOF)이다. 주(Master) – 부(Slave) 구조로 복제한다.

  • Master: 쓰기(INSERT/UPDATE/DELETE) 전담. 보통 1대.
  • Slave: Master의 변경을 복제해 읽기(SELECT) 전담. 여러 대로 늘려 읽기 성능을 확장.

이점:

  • 성능 — 대부분의 워크로드가 읽기 위주이므로 읽기 부하를 Slave로 분산.
  • 안정성 — 한 데이터 센터가 파괴돼도 다른 지역의 복제본으로 데이터 보존.
  • 가용성 — Master 장애 시 Slave 하나를 승격해 계속 운영.

캐시

값비싼 연산 결과나 자주 읽히는 데이터를 메모리에 저장해 재사용한다. 응답 시간을 크게 줄이고 DB 부하를 완화한다.

캐시 계층

웹 서버와 DB 사이에 별도의 캐시 서버(Redis, Memcached)를 둔다. 흔히 쓰이는 패턴은 Read-through다 — 캐시에서 먼저 찾고, 없으면 DB에서 읽어 캐시에 저장한 뒤 반환한다.

캐시 사용 시 유의할 점

  • 언제 쓰나 — 데이터 갱신은 드물고 참조는 빈번할 때.
  • 무엇을 저장하나 — 휘발성 메모리이므로 영속 저장소의 대체재로 쓰면 안 된다.
  • 만료 정책(TTL) — 너무 짧으면 DB 부하 증가, 너무 길면 데이터 신선도 하락.
  • 일관성 — 데이터 저장소와 캐시를 단일 트랜잭션으로 갱신하기 어려워 불일치가 생길 수 있다.
  • 장애 대처 — 캐시 서버 1대면 SPOF. 여러 지역에 분산.
  • 캐시 크기·축출 정책 — LRU가 보편적, 사용 패턴에 따라 LFU/FIFO 선택.

콘텐츠 전송 네트워크(CDN)

이미지·비디오·CSS·JS 같은 정적 콘텐츠를 전 세계에 분산된 엣지 서버에 캐싱해 사용자와 가까운 곳에서 응답한다. 지연 시간 감소, 원본 서버 부하 경감이 주된 효과.

  • 엣지에 없으면(cache miss) 원본에서 가져와 캐싱 후 응답.
  • TTL 만료 시점에 원본을 다시 조회.
  • 비용 문제 — 자주 쓰이지 않는 자산을 캐싱하면 오히려 비효율. 핵심 자산만 선별.
  • 장애 대비 — CDN 장애 시 원본으로 직접 요청하도록 폴백 구성.

무상태(stateless) 웹 계층

수평 확장을 제대로 하려면 세션 같은 상태 정보를 웹 서버에 두지 않아야 한다.

상태 정보 의존적 아키텍처

특정 사용자의 세션이 특정 서버에 묶여 있으면, 로드밸런서가 sticky session으로 그 서버에만 보내야 한다. 서버 추가/제거가 번거롭고 한 서버가 죽으면 해당 사용자의 세션이 사라진다.

무상태 아키텍처

세션·토큰 등 공유 상태는 모두 공유 저장소(RDB, Redis, NoSQL)로 옮긴다. 웹 서버는 어떤 요청이든 처리할 수 있게 된다 — 자유 자재로 추가·제거할 수 있고 자동 규모 조절도 가능.

데이터 센터

서비스가 글로벌로 성장하면 여러 데이터 센터(지역)를 사용한다. geoDNS가 사용자를 가까운 센터로 보낸다.

해결해야 할 기술 과제:

  • 트래픽 우회 — geoDNS로 지역 라우팅. 한 센터 장애 시 나머지로 페일오버.
  • 데이터 동기화 — 센터마다 사용자 데이터가 달라지지 않도록 비동기 복제.
  • 테스트·배포 — 서로 다른 위치에서도 정상 동작하는지 확인. 자동화된 배포 파이프라인 필수.

메시지 큐

생산자(producer)와 소비자(consumer)를 느슨하게 결합하는 비동기 통신 컴포넌트. 구성 요소 간 독립적 확장과 장애 격리가 가능해진다.

  • 생산자는 큐에 메시지를 넣기만 하면 된다.
  • 소비자가 장애로 내려가 있어도 메시지는 큐에 남아 있다가 소비자가 복구되면 처리된다.
  • 피크 부하 시 큐에 쌓아두고 소비자만 일시적으로 늘려 대응할 수 있다.

대표 예: 사진 업로드 후 커스터마이징·필터·썸네일·공유 같은 후처리 작업을 각각 독립된 워커가 큐에서 꺼내 처리.

로그, 메트릭 그리고 자동화

  • 로그 — 에러 원인 파악의 필수 자료. 서버마다 로컬에 쌓지 말고 중앙 로그 시스템(ELK, Splunk)으로 모은다.
  • 메트릭
    • 호스트 수준 — CPU, 메모리, 디스크 I/O
    • 종합 수준 — DB·캐시 계층 성능
    • 비즈니스 지표 — DAU, 매출, 리텐션
  • 자동화 — CI/CD, 지속적 통합·배포로 개발 생산성과 안정성을 동시에 확보.

데이터베이스의 규모 확장

수직적 확장

더 좋은 머신으로 교체하는 방식. AWS RDS 기준 최대 수십 TB 메모리까지 가능하지만 — 하드웨어 상한 존재, 가격이 기하급수적 증가, SPOF, 장애 복구 어려움의 단점이 있다.

수평적 확장(샤딩)

대규모 DB를 샤드라 불리는 작은 단위로 쪼개 여러 서버에 나눠 담는다. 모든 샤드는 같은 스키마를 공유하되 담긴 데이터는 겹치지 않는다.

도전 과제:

  • 샤드 키(파티션 키) 선택 — 분포가 고르게 되도록. user_id 해시가 흔한 선택.
  • 재샤딩(resharding) — 한 샤드가 가득 차거나 불균형이 심해지면 샤드 수를 늘려야 함. 안정 해시(5장)로 해결.
  • 유명인사 문제(hotspot key) — 특정 샤드에 트래픽이 몰림. 해당 대상만 전용 샤드에 배치.
  • 조인과 비정규화 — 여러 샤드에 걸친 조인이 어려워진다. 비정규화로 조인을 피함.

백만 사용자, 그리고 그 이상

무한한 확장이 가능한 시스템은 없다. 사용자가 늘수록 더 세밀한 튜닝과 새로운 전략이 필요하다. 요약된 확장의 기본 원칙:

  1. 웹 계층은 무상태로 유지
  2. 모든 계층에 다중화를 적용
  3. 가능한 한 많은 데이터를 캐시
  4. 여러 데이터 센터를 지원
  5. 정적 콘텐츠는 CDN으로 분산
  6. 데이터 계층은 샤딩으로 규모 확장
  7. 각 계층을 독립적인 서비스로 분할
  8. 시스템을 지속적으로 모니터링하고 자동화 도구 활용