수직 확장은 보통 CPU/RAM을 추가하는 것에 가깝습니다. 수평 확장은 조정, 파티셔닝, 일관성 유지와 더 많은 운영 작업을 필요로 하며—그게 왜 더 어려운지 설명합니다.

확장이란 “넘어지지 않으면서 더 많은 것을 처리하는 것”을 의미합니다. 그 "더 많은 것"은 다음과 같을 수 있습니다:
사람들이 확장에 대해 이야기할 때 보통 개선하려는 것은 다음 중 하나 이상입니다:
대부분의 경우 핵심은 한 가지 테마로 귀결됩니다: 스케일 업(수직 확장)은 ‘단일 시스템’ 느낌을 유지하는 반면, 스케일 아웃(수평 확장)은 시스템을 독립적인 머신들이 조정하는 집단으로 바꾼다 — 그리고 그 조정이 어려움이 폭발적으로 늘어나는 지점입니다.
수직 확장은 한 대의 머신을 더 강하게 만드는 것을 의미합니다. 기본 아키텍처는 그대로 두고 서버(또는 VM)를 업그레이드합니다: 더 많은 CPU 코어, 더 많은 RAM, 더 빠른 디스크, 더 높은 네트워크 처리량.
큰 트럭을 사는 것과 비슷합니다: 운전자는 하나이고 차량은 하나지만 더 많은 짐을 실을 수 있습니다.
수평 확장은 더 많은 머신이나 인스턴스를 추가하고 작업을 분할해서 처리하는 것을 의미합니다—보통 로드 밸런서 뒤에서 운영합니다. 하나의 더 강한 서버 대신 여러 서버가 함께 동작합니다.
여러 대의 트럭을 쓰는 것과 비슷합니다: 전체 화물은 더 많이 옮길 수 있지만 스케줄링, 라우팅, 조정에 신경 써야 합니다.
일반적인 촉발 요인은 다음과 같습니다:
팀들은 보통 먼저 수직 확장을 합니다(상자를 업그레이드하는 것이 빠르기 때문). 그런 다음 단일 머신이 한계에 다다르거나 더 높은 가용성이 필요할 때 수평 확장을 합니다. 성숙한 아키텍처는 병목에 따라 더 큰 노드와 더 많은 노드를 혼합해서 사용합니다.
수직 확장은 시스템을 한 곳에 묶어두기 때문에 매력적입니다. 단일 노드에서는 메모리와 로컬 상태의 단일 진실 소스가 있는 경우가 많습니다. 하나의 프로세스가 인메모리 캐시, 작업 큐, 세션 저장소(세션이 메모리에 있을 경우), 임시 파일을 소유합니다.
한 서버에서는 노드 간 조정이 거의 없기 때문에 운영이 대체로 간단합니다:
스케일 업하면 익숙한 레버를 당깁니다: CPU/RAM 추가, 더 빠른 스토리지 사용, 인덱스 개선, 쿼리와 설정 튜닝. 데이터를 어떻게 분산시키거나 여러 노드가 "다음에 무슨 일이 일어나는지" 합의하는 방식을 재설계할 필요가 없습니다.
수직 확장이 ‘공짜’인 것은 아닙니다—단지 복잡성이 제한된다는 뜻입니다.
결국 한계에 도달합니다: 임대할 수 있는 가장 큰 인스턴스, 수익 감소 구간, 또는 고비용 곡선. 또한 다운타임 리스크가 커집니다: 하나의 큰 머신이 실패하거나 유지보수에 들어가면 시스템의 큰 부분이 같이 내려갈 수 있습니다(중복을 두지 않았다면).
수평 확장하면 단순히 “서버가 더 많아지는” 것이 아닙니다. 각기 독립적인 행위자들이 누가 언제 어떤 데이터를 사용해 어떤 작업을 담당할지 합의해야 합니다.
하나의 머신에서는 조정이 암묵적일 때가 많습니다: 하나의 메모리 공간, 하나의 프로세스, 상태를 확인할 곳도 하나입니다. 여러 머신에서는 조정이 설계해야 하는 기능이 됩니다.
일반적인 도구와 패턴에는 다음이 포함됩니다:
조정 관련 버그는 깔끔한 크래시처럼 보이지 않습니다. 보통 다음과 같이 나타납니다:
이런 문제는 실제 부하가 걸리거나 배포 시, 또는 부분 장애(한 노드가 느려짐, 스위치가 패킷을 드롭함, 단일 가용 영역이 잠깐 중단됨)에서만 드러나는 경우가 많습니다.
수평 확장할 때 모든 데이터를 한 곳에 둘 수 없는 경우가 많습니다. 데이터를 여러 머신(샤드)에 나눠 여러 노드가 병렬로 저장하고 서빙하게 합니다. 이 분할 지점에서 복잡성이 시작됩니다: 모든 읽기와 쓰기가 ‘이 레코드는 어느 샤드에 있는가?’에 의존합니다.
레인지 파티셔닝은 정렬된 키 기준으로 데이터를 그룹화합니다(예: 사용자 A–F는 샤드1, G–M은 샤드2). 직관적이고 범위 쿼리에 좋습니다(“지난주 주문 보기”). 단점은 부하의 불균형: 특정 범위가 인기가 많아지면 그 샤드가 병목이 됩니다.
해시 파티셔닝은 키를 해시 함수에 넣어 샤드에 분산시킵니다. 트래픽을 고르게 퍼뜨리지만 관련 레코드가 흩어지기 때문에 범위 쿼리가 어려워집니다.
노드를 추가하면 새로운 용량을 활용하려면 일부 데이터를 옮겨야 합니다. 노드를 제거하면(계획적이든 장애든) 다른 샤드가 이를 떠맡아야 합니다. 리밸런싱은 큰 전송, 캐시 워밍, 일시적 성능 저하를 유발할 수 있습니다. 이동 중에는 오래된 읽기나 잘못된 라우팅된 쓰기를 방지해야 합니다.
해시를 써도 실제 트래픽은 균일하지 않습니다. 유명 계정, 인기 상품, 시간 기반 접근 패턴이 특정 샤드에 읽기/쓰기 집중을 일으킬 수 있습니다. 한 샤드가 뜨거워지면 전체 시스템 처리량을 제한할 수 있습니다.
샤딩은 라우팅 규칙 유지, 마이그레이션 실행, 스키마 변경 후 백필, 분할/병합 계획 등 지속적인 책임을 가져옵니다.
수평 확장하면 단순히 서버를 더 추가하는 게 아니라 애플리케이션의 복제본을 더 만드는 것입니다. 어려운 부분은 상태입니다: 요청 사이 또는 작업 진행 중 애플리케이션이 기억하는 모든 것.
사용자가 서버 A에서 로그인했는데 다음 요청이 서버 B로 가면 B가 사용자를 알아야 할까요?
캐시는 속도를 높이지만 여러 서버는 여러 캐시를 가집니다. 이제 다음을 다뤄야 합니다:
많은 워커가 있을 때는 잡이 두 번 실행될 수 있습니다. 일반적으로 큐, 리스/락, 또는 아이덴포턴트한 잡 로직이 필요합니다(예: 청구서 전송이나 카드 결제가 중복되지 않도록).
단일 노드(또는 단일 프라이머리 DB)가 있을 때는 보통 명확한 ‘진실의 원천’이 있습니다. 수평 확장하면 데이터와 요청이 여러 머신에 흩어져 모두를 동기화하는 일이 지속적인 고민이 됩니다.
점진적 일관성은 대규모에서 더 빠르고 비용이 낮지만 놀라운 엣지 케이스를 초래합니다.
일반적인 문제들:
실패를 제거할 수는 없지만 설계로 완화를 할 수 있습니다:
서비스 간 트랜잭션(주문 + 재고 + 결제)은 여러 시스템이 합의해야 합니다. 한 단계가 실패하면 보상 동작(compensating actions)과 세심한 장부 관리가 필요합니다. 네트워크와 노드가 독립적으로 실패하는 상황에서 전통적인 "전부 아니면 전무" 동작을 구현하는 것은 어렵습니다.
정확성이 필수적인 항목(결제, 계정 잔액, 재고 수, 좌석 예약 등)에는 강한 일관성을 사용하세요. 분석이나 추천 같은 덜 중요한 데이터에는 점진적 일관성이 적절할 수 있습니다.
스케일 업일 때는 많은 호출이 같은 프로세스 내 함수 호출입니다: 빠르고 예측 가능. 스케일 아웃이면 같은 상호작용이 네트워크 호출이 되어 지연, 지터, 실패 모드를 추가합니다.
네트워크 호출은 고정 오버헤드(직렬화, 큐잉, 홉 수)와 가변 오버헤드(혼잡, 라우팅, noisy neighbor)를 가집니다. 평균 지연이 괜찮아도 꼬리 지연(가장 느린 1–5%)이 사용자 경험을 지배할 수 있습니다. 단일 느린 종속성이 전체 요청을 막기 때문입니다.
대역폭과 패킷 손실도 제약입니다: 고빈도 요청에서는 작은 페이로드도 누적되고 재전송은 조용히 부하를 증가시킵니다.
타임아웃이 없으면 느린 호출이 쌓여 스레드가 갇힙니다. 타임아웃과 재시도로 복구할 수 있지만 재시도가 부하를 증폭시키면 문제가 악화됩니다.
일반적인 실패 패턴은 재시도 폭풍입니다: 백엔드가 느려져 클라이언트가 타임아웃하고 재시도함 → 재시도가 부하를 늘려 백엔드가 더 느려짐.
안전한 재시도는 보통 다음을 필요로 합니다:
여러 인스턴스가 있으면 클라이언트는 어디로 요청을 보낼지 알아야 합니다—로드 밸런서든 서비스 디스커버리+클라이언트 사이드 밸런싱이든. 이로 인해 헬스 체크, 연결 드레이닝, 불균형한 트래픽 분배, 반쯤 망가진 인스턴지로 라우팅되는 위험 같은 움직이는 부품이 추가됩니다.
오버로드가 전파되는 것을 막으려면 역압력: 한정된 큐, 서킷 브레이커, 레이트 리미팅이 필요합니다. 목표는 시스템 전체 사고로 가기보다 빠르고 예측 가능하게 실패하게 하는 것입니다.
수직 확장은 실패 방식이 단순한 편입니다: 큰 머신 하나가 단일 실패 지점이 됩니다. 느려지거나 크래시 나면 영향이 명확합니다.
수평 확장은 수학을 바꿉니다. 많은 노드가 있을 때는 일부 머신이 건강하지 않은 것이 정상입니다. 시스템은 "업"이지만 사용자에게는 오류, 느린 페이지, 불일치가 보일 수 있습니다. 이것이 부분 장애이며, 설계할 때 기본 상태로 받아들여야 합니다.
스케일 아웃 환경에서 서비스는 다른 서비스에 의존합니다: DB, 캐시, 큐, 외부 API. 작은 문제가 파급됩니다:
부분 장애를 견디려면 중복성을 추가합니다:
이는 가용성을 높이지만 스플릿 브레인, 오래된 복제본, 쿼럼 불가 상황에서의 처리를 포함한 엣지 케이스를 만들기도 합니다.
자주 쓰이는 패턴:
단일 머신에서는 시스템 이야기가 한 곳에 있습니다: 하나의 로그, 하나의 CPU 그래프, 하나의 프로세스. 수평 확장에서는 이야기가 흩어집니다.
각 노드는 로그, 메트릭, 트레이스의 또 다른 스트림을 추가합니다. 수집이 문제는 아니더라도 이들을 연관시키는 것이 어렵습니다. 결제 실패는 웹 노드에서 시작해 두 개의 서비스를 호출하고 캐시를 거치고 특정 샤드에서 읽어야 했던 단서를 여러 장소와 시간선에 남깁니다.
문제는 선택적이 됩니다: 한 노드만 잘못된 설정을 가졌거나 한 샤드가 과열됐거나 한 존의 지연이 높을 수 있습니다. 디버깅은 대부분의 경우 정상적으로 보이다가 어떤 상황에서만 문제가 재현되므로 랜덤하게 느껴집니다.
분산 추적은 요청에 트래킹 번호를 붙이는 것과 같습니다. 상관 ID는 그 번호입니다. 서비스를 통과할 때 이를 전달하고 로그에 포함하면 하나의 ID로 끝에서 끝까지 여정을 볼 수 있습니다.
컴포넌트가 많아지면 경보도 많아집니다. 튜닝 없이 경보를 많이 받으면 팀이 무감각해집니다. 액션 가능한 경보에 집중하세요:
용량 문제는 실패보다 먼저 나타납니다. CPU, 메모리, 큐 깊이, 커넥션 풀 사용량 같은 포화 신호를 모니터링하세요. 포화가 일부 노드에서만 보이면 밸런싱, 샤딩, 설정 드리프트를 의심하세요.
수평 확장에서는 배포가 더 이상 “한 박스 교체”가 아닙니다. 여러 머신에서 변경을 조정하면서 서비스를 가용 상태로 유지해야 합니다.
수평 배포는 보통 롤링 업데이트(노드를 점진적으로 교체), 카나리(소수의 트래픽만 신규 버전으로 보내기), 블루/그린(전체 환경을 스위치) 등을 사용합니다. 이들은 블래스트 반경을 줄이지만 트래픽 전환, 헬스 체크, 연결 드레이닝, 계속 진행할지 판단하기 위한 "충분히 괜찮음"의 정의가 필요합니다.
점진적 배포 중에는 구버전과 신버전이 동시에 실행됩니다. 버전 스큐는 시스템이 혼합 동작을 견뎌야 함을 의미합니다:
API는 올바른 것뿐 아니라 이전/이후 버전과의 호환성을 갖춰야 합니다. DB 스키마 변경은 가능한 한 추가적(additive)이어야 합니다(필드를 nullable로 추가한 뒤에 필수로 만들기). 메시지 포맷은 버전 관리해서 소비자가 오래된 이벤트와 새 이벤트를 모두 읽을 수 있게 하세요.
코드 롤백은 쉬운 편이지만 데이터 롤백은 그렇지 않습니다. 마이그레이션이 필드를 드롭하거나 재작성하면 이전 코드가 충돌하거나 레코드를 잘못 처리할 수 있습니다. "확장/축소(expand/contract)" 마이그레이션 패턴을 사용하세요: 두 스키마를 모두 지원하는 코드를 배포한 뒤 데이터를 옮기고 이후에 옛 경로를 제거합니다.
노드가 많아지면 구성 관리도 배포의 일부가 됩니다. 오래된 구성, 잘못된 기능 플래그, 만료된 자격증명이 있는 단일 노드가 불안정하고 재현하기 어려운 실패를 만들 수 있습니다.
수평 확장은 단위당 가격이 낮아 보일 수 있습니다: 작은 인스턴스 여러 대, 각 인스턴스의 시급이 낮음. 하지만 총비용은 단순한 컴퓨트 비용이 아닙니다. 노드를 추가하면 네트워킹, 모니터링, 조정, 그리고 많은 머신을 일관되게 만드는 데 드는 시간 비용이 늘어납니다.
수직 확장은 지출을 더 적은 머신에 집중시키는 경향이 있습니다—패치할 호스트가 적고, 에이전트도 적고, 로그 전송/메트릭 스크랩도 적습니다.
스케일 아웃에서는 단위당 가격이 낮아도 다음에 비용을 지불할 수 있습니다:
스파이크를 안전하게 처리하려면 분산 시스템은 종종 언더풀로(빈 상태로) 동작합니다. 여러 계층(웹, 워커, DB, 캐시)에 헤드룸을 유지하느라 수십~수백 인스턴스에 대한 유휴 비용을 지불할 수 있습니다.
수평 확장은 온콜 부담을 늘리고 성숙한 툴을 요구합니다: 알람 튜닝, 런북, 사고 대응 훈련, 교육. 또한 소유권 경계(누가 어떤 서비스를 책임지는가?)와 사고 시 조정 작업에 팀 시간이 더 들어갑니다.
결과적으로: “단위당 더 싸다”가 인건비, 운영 리스크, 그리고 많은 머신을 하나처럼 보이게 만들기 위한 작업을 포함하면 전체적으로 더 비쌀 수 있습니다.
수직 확장(더 큰 머신)과 수평 확장(더 많은 머신) 사이의 선택은 단지 가격 문제가 아닙니다. 워크로드의 성격과 팀이 감당할 수 있는 운영 복잡성의 수준에 관한 문제입니다.
워크로드에서 시작하세요:
일반적이고 합리적인 경로:
많은 팀은 데이터베이스는 수직(또는 가볍게 클러스터)로 유지하면서 무상태 앱 계층을 수평 확장합니다. 이렇게 하면 샤딩의 고통을 줄이면서 웹 용량을 빨리 늘릴 수 있습니다.
다음이 있으면 수평으로 옮길 준비가 된 것입니다: 탄탄한 모니터링과 알람, 테스트된 페일오버, 부하 테스트, 반복 가능한 배포와 안전한 롤백.
많은 확장 고통은 단순히 “아키텍처” 문제가 아니라 운영 루프입니다: 안전하게 반복하고, 신뢰성 있게 배포하고, 계획이 현실과 다를 때 빠르게 롤백하는 과정입니다.
웹, 백엔드, 모바일 시스템을 만들며 빠르게 움직이되 통제력을 잃고 싶지 않다면 Koder.ai는 스케일 결정 과정을 구현하기 전에 프로토타입을 빠르게 만들고 배포하는 데 도움을 줄 수 있습니다. Koder.ai는 채팅을 통해 애플리케이션을 구축하는 바이브-코딩 플랫폼이며, 내부적으로 에이전트 기반 아키텍처를 사용합니다. 실무적으로는 다음을 할 수 있습니다:
Koder.ai는 AWS 글로벌 인프라에서 실행되므로 지연 및 데이터 전송 제약을 충족하기 위해 다른 리전으로 배포하는 것도 지원합니다—멀티존 또는 멀티리전 가용성이 확장 스토리의 일부가 될 때 유용합니다.
수직 확장은 단일 머신을 더 크게 만드는 것을 의미합니다(CPU/RAM/빠른 디스크 추가). 수평 확장은 더 많은 머신을 추가하고 작업을 분산시키는 것입니다.
수직 확장은 시스템이 여전히 ‘하나의 시스템’처럼 동작한다고 느껴지기 때문에 단순하게 느껴지는 반면, 수평 확장은 여러 시스템이 조정되고 일관성을 유지해야 합니다.
노드가 여러 개가 되는 순간 명시적인 조정이 필요하기 때문입니다:
단일 머신은 이러한 분산 시스템 문제의 많은 부분을 기본적으로 회피합니다.
여러 대의 머신을 하나처럼 동작하게 만들기 위해 쓰는 시간과 로직을 말합니다:
각 노드는 단순해도, 전체 시스템의 동작은 부하나 장애 상황에서 이해하기 어려워집니다.
샤딩(파티셔닝)은 데이터를 여러 노드에 나누어 저장/서비스하도록 하는 것입니다. 어려운 이유는:
또한 마이그레이션, 백필(backfill), 샤드 맵 관리 같은 운영 작업이 늘어납니다.
상태(state)는 요청 간 또는 작업 진행 중에 애플리케이션이 '기억'하는 모든 것을 말합니다(세션, 인메모리 캐시, 임시 파일, 작업 진행 상태 등).
수평 확장에서는 요청이 다른 서버로 갈 수 있으므로, 일반적으로 공유 상태 저장소(Redis/DB)를 두거나 스티키 세션 같은 트레이드오프를 수용해야 합니다.
여러 워커가 같은 잡을 가져갈 수 있거나 재시도 때문에 중복 처리될 수 있습니다.
일반적인 완화책:
강한 일관성(Strong consistency)은 쓰기가 성공하면 모든 리더가 즉시 최신 값을 보는 것을 의미합니다. 점진적 일관성(Eventual consistency)은 업데이트가 전파되는 데 시간이 걸려 잠깐 오래된 값을 볼 수 있음을 의미합니다.
정확성이 절대적으로 중요한 데이터(결제, 계정 잔액, 재고)는 강한 일관성이 필요하고, 분석이나 추천 같은 곳은 점진적 일관성으로 버틸 수 있습니다.
분산 시스템에서는 호출이 네트워크 호출이 되고, 네트워크는 지연(latency), 지터(jitter), 실패 모드를 추가합니다.
일반적으로 중요한 사항:
타임아웃과 재시도는 잘못 설계하면 재시도 폭풍(retry storm)을 만들어 문제를 악화시킵니다.
부분 장애(partial failure)는 일부 구성요소가 느리거나 비정상인데 다른 부분은 정상인 상태를 말합니다. 시스템은 ‘업(Up)’ 상태이지만 에러, 타임아웃 또는 일관성 문제를 일으킬 수 있습니다.
복구 설계는 복제, 쿼럼, 멀티존 배포, 서킷 브레이커, 그레이스풀 디그레이데이션 등을 포함합니다.
여러 서버에 걸쳐 증거(로그, 메트릭, 트레이스)가 흩어집니다. 실무적 조치:
이런 신호들이 문제를 빨리 찾아냅니다.