1. 라이브러리
  2. HTTP와 웹
  3. 웹 아키텍처

업데이트됨 1개월 전

단일 서버에는 한계가 있습니다. 처리할 수 있는 연결 수, 요청 수, 사용자 수가 정해져 있습니다. 그 한계를 넘어서면 서버는 느려지고, 버벅거리고, 결국 멈춥니다. 단 하나의 기계가 버티지 못해 애플리케이션 전체가 죽는 것입니다.

로드 밸런서는 바로 이 한계 때문에 존재합니다. 여러 서버 앞에 자리를 잡고 들어오는 요청을 모든 서버에 분산시킵니다. 어떤 단일 서버도 전체 부하를 혼자 짊어지지 않습니다. 그리고 서버 하나가 장애를 일으키더라도—서버는 언젠가 반드시 죽습니다—나머지 서버들은 계속 동작합니다. 사용자는 아무것도 느끼지 못합니다.

단일 서버는 장애가 날 수 있습니다. 로드 밸런서 뒤에 천 대의 서버가 있다면? 언제나 살아 있는 서버가 있습니다.

로드 밸런서가 해결하는 문제

애플리케이션이 서버 한 대를 넘어 성장하면, 두 가지 선택지가 있습니다.

수직 확장은 더 큰 서버를 구매하는 것입니다. CPU를 늘리고, RAM을 늘리고, 디스크를 빠르게 합니다. 어느 수준까지는 통합니다. 하지만 돈으로 살 수 있는 가장 강력한 서버에도 한계는 있습니다. 그리고 여전히 서버는 하나입니다—장애가 나면 모든 것이 멈춥니다.

수평 확장은 서버를 더 추가하는 것입니다. 한계가 없습니다. 용량이 더 필요하면? 서버를 추가하면 됩니다. 하나가 장애가 나면? 나머지가 계속 동작합니다. 하지만 수평 확장은 새로운 문제를 만들어냅니다: 사용자는 어느 서버에 연결해야 할까요?

사용자가 직접 서버를 고르게 할 수는 없습니다. 사용자를 서버에 수동으로 배정할 수도 없습니다. 외부에는 단일 주소를 제공하면서, 내부적으로는 여러 머신에 작업을 분산시키는 무언가가 필요합니다.

그 무언가가 바로 로드 밸런서입니다.

로드 밸런서의 작동 방식

외부에서 보면, 사용자는 하나의 주소만 봅니다. loadbalancer.example.com에 접속하지만, 그 뒤에 열 대의 서버가 있다는 사실은 전혀 모릅니다.

로드 밸런서는 들어오는 각 연결을 받아 결정을 내립니다: 어떤 백엔드 서버가 이 요청을 처리해야 할까? 선택한 서버로 요청을 전달하고, 응답을 받아 사용자에게 돌려보냅니다.

백엔드 서버가 죽으면, 로드 밸런서가 감지합니다. 그쪽으로 트래픽을 보내는 것을 중단합니다. 요청은 살아 있는 서버들로 흘러갑니다. 사용자는 아무것도 느끼지 못합니다—장애난 요청이 정상 서버에서 재시도되는 몇 밀리초의 지연이 있을 수도 있지만요.

이것이 핵심 가치입니다: 추상화. 사용자는 하나의 엔드포인트만 봅니다. 서버를 추가하고, 제거하고, 교체해도 사용자는 전혀 알 수 없습니다. 로드 밸런서는 하나의, 불멸의 애플리케이션이라는 환상을 유지합니다.

요청을 처리할 서버 선택하기

각 요청을 어느 서버에서 처리할지 결정하는 알고리즘은 생각보다 훨씬 중요합니다.

라운드 로빈이 가장 단순합니다. 첫 번째 요청은 서버 A로, 두 번째는 서버 B로, 세 번째는 서버 C로, 그리고 다시 A로 돌아옵니다. 모든 서버가 동일한 트래픽을 받습니다. 모든 서버가 동일하고 모든 요청이 대략 비슷한 처리 비용을 가질 때 잘 동작합니다.

하지만 요청은 동등하지 않습니다. 어떤 요청은 데이터베이스를 많이 사용합니다. 어떤 요청은 캐시된 데이터를 즉시 반환합니다. 라운드 로빈은 이를 무시합니다. 무거운 쿼리를 처리 중인 서버도 캐시 응답을 반환하는 서버와 동일한 트래픽을 받습니다. 부하가 불균등해집니다.

가중 라운드 로빈은 서버 간 차이를 고려합니다. 가중치 3인 서버는 가중치 1인 서버보다 세 배 많은 요청을 받습니다. 풀에 더 강력한 서버를 추가할 때는 더 높은 가중치를 부여하면 됩니다.

최소 연결은 현재 활성 연결 수가 가장 적은 서버로 각 요청을 보냅니다. 느린 요청을 처리 중인 서버는 자연스럽게 연결이 쌓입니다. 최소 연결은 이를 인식하고 새 요청을 다른 곳으로 보냅니다. 부하가 스스로 균형을 맞춥니다.

최소 응답 시간은 더 나아갑니다: 연결이 가장 적으면서 최근 응답이 가장 빠른 서버로 보냅니다. 유휴 상태이면서 빠른 서버가 우선순위를 갖습니다.

IP 해시는 클라이언트의 IP 주소 해시를 사용해 서버를 선택합니다. 같은 클라이언트는 항상 같은 서버에 도달합니다(그 서버가 장애나지 않는 한). 세션 어피니티에 중요한데—이 부분은 나중에 다시 다룰 것입니다. 함정이 있으니까요.

최소 대역폭은 현재 가장 적은 데이터를 전송 중인 서버로 보냅니다. 요청 수가 아닌 대역폭이 제약인 미디어 서버에 유용합니다.

대부분의 애플리케이션은 최소 연결이나 라운드 로빈을 사용합니다. 정교한 알고리즘은 대규모에서 중요해지지만, 근본적인 아키텍처 문제를 해결해줄 수는 없습니다.

Layer 4 vs. Layer 7

로드 밸런서는 네트워크 스택의 다른 수준에서 동작할 수 있습니다.

Layer 4 로드 밸런싱은 IP 주소와 포트만 봅니다. HTTP를 이해하지 못합니다. "이 IP에서 이 포트로 연결이 왔다"는 것만 보고 어디로 보낼지 결정합니다. 빠르고, 효율적이고, 아는 게 없습니다.

Layer 7 로드 밸런싱은 HTTP를 이해합니다. URL을 읽고, 헤더를 검사하고, 쿠키를 살펴보고, 심지어 요청 본문도 볼 수 있습니다. 이를 통해 정교한 라우팅이 가능합니다: /api/ 요청은 API 서버로, /images/는 미디어 서버로, Authorization 헤더를 기반으로 라우팅하는 것도 가능합니다.

단점은 속도입니다. Layer 7은 HTTP를 파싱해야 하는데, 이는 Layer 4의 단순한 패킷 전달보다 처리 비용이 더 듭니다. 하지만 현대적인 Layer 7 로드 밸런서는 초당 수십만 건의 요청을 처리합니다. 유연성은 거의 언제나 그 비용을 정당화합니다.

대부분의 웹 애플리케이션은 Layer 7을 사용합니다. 콘텐츠 기반 라우팅, 중앙 집중식 SSL 종료, 헤더 추가/수정 기능은 약간의 속도 이득을 위해 포기하기에는 너무 소중합니다.

상태 확인: 무엇이 살아 있는지 파악하기

죽은 서버에 요청을 보내는 로드 밸런서는 없는 것보다 나쁩니다. 상태 확인이 이를 방지합니다.

능동적 상태 확인: 로드 밸런서가 주기적으로 각 백엔드 서버에 핑을 보냅니다. TCP 연결 시도, /health에 대한 HTTP 요청, 또는 데이터베이스 연결성을 확인하는 더 정교한 검사일 수 있습니다. 정상 서버는 응답합니다. 비정상 서버는 응답하지 않습니다. 장애난 서버는 풀에서 제거됩니다.

수동적 상태 확인: 로드 밸런서가 실제 트래픽을 모니터링합니다. 서버가 500 오류를 반환하거나 실제 요청에서 타임아웃이 발생하기 시작하면, 능동적 확인을 통과하더라도 비정상으로 표시합니다. 서버가 기술적으로는 살아 있지만 기능적으로 고장나는 경우가 있습니다.

서버가 장애나면 트래픽은 자동으로 정상 서버로 흘러갑니다. 장애난 서버가 복구되어 상태 확인을 다시 통과하면, 자동으로 풀에 다시 합류합니다. 사람의 개입이 필요 없습니다.

이것이 고가용성입니다. 개별 서버는 장애가 날 수 있습니다. 애플리케이션은 계속 동작합니다.

세션 지속성: 필요악

일부 애플리케이션은 서버 메모리에 사용자 세션을 저장합니다. 사용자가 서버 A에 로그인하면, 세션은 서버 A에 저장됩니다. 다음 요청이 서버 B로 가면, 사용자는 로그아웃된 것처럼 보입니다.

세션 지속성(또는 "스티키 세션")은 사용자의 요청이 일관되게 같은 서버에 도달하도록 보장합니다. 로드 밸런서는 어느 서버가 사용자의 첫 번째 요청을 처리했는지 추적하고, 이후 요청을 그 서버로 보냅니다.

이는 쿠키(로드 밸런서가 백엔드 서버를 식별하는 쿠키를 설정)나 IP 해시(같은 IP는 항상 같은 서버로)를 통해 구현할 수 있습니다.

문제가 있습니다: 세션 지속성은 로드 밸런서가 제공하는 모든 것을 약화시킵니다. 분산과 이중화를 원했지만, 이제 사용자를 특정 서버로 보내고 있습니다. 그 서버가 죽으면, 사용자는 세션을 잃습니다. 탈출하려던 단일 장애 지점을 다시 만들어낸 것입니다.

진짜 해결책: 서버에 세션을 저장하지 마세요. Redis나 데이터베이스, 또는 다른 공유 저장소에 저장하세요. 어느 서버든 어떤 요청이든 처리할 수 있습니다. 서버가 죽어도? 사용자는 세션이 유지된 채 다른 서버에서 원활하게 계속합니다. 스티키 세션이 불필요해집니다.

세션 지속성은 분산 환경을 고려하지 않은 아키텍처의 임시방편입니다. 처음부터 시작한다면, 처음부터 공유 상태를 위해 설계하세요.

글로벌 로드 밸런싱: 여러 데이터 센터

진정한 글로벌 애플리케이션에서는 TCP 연결이 시작되기도 전에 DNS 수준에서 로드 밸런싱이 이루어집니다.

도쿄의 사용자가 애플리케이션의 DNS를 쿼리합니다. DNS는 도쿄 데이터 센터의 IP 주소를 반환합니다. 런던의 사용자는 런던 데이터 센터의 IP를 받습니다. 사용자는 자동으로 가까운 서버에 연결되어 지연 시간이 줄어듭니다.

도쿄 데이터 센터가 장애가 나면, DNS는 그 IP 반환을 중단합니다. 도쿄 사용자는 그다음으로 가까운 데이터 센터로 라우팅됩니다. 지리적 수준의 장애 전환입니다.

함정이 있습니다: DNS 캐싱. DNS를 업데이트하면, 오래된 답변이 전 세계 캐시에 몇 분에서 몇 시간 동안 남아 있습니다. DNS 기반 장애 전환은 즉각적이지 않습니다. 대부분의 글로벌 애플리케이션은 DNS 수준의 지리적 라우팅과 각 지역 내 애플리케이션 수준의 로드 밸런싱을 결합합니다.

로드 밸런서의 가용성

로드 밸런서는 애플리케이션 서버의 단일 장애 지점을 해결합니다. 하지만 로드 밸런서 자체도 장애가 날 수 있습니다. 그렇게 되면, 멀쩡히 살아 있는 백엔드 서버 전체에 접근할 수 없게 됩니다.

액티브-패시브: 두 대의 로드 밸런서. 하나가 트래픽을 처리합니다. 다른 하나는 대기하다가 주 서버가 장애나면 이어받습니다. 단순한 이중화지만, 로드 밸런싱 용량의 절반이 유휴 상태로 남습니다.

액티브-액티브: 여러 로드 밸런서가 동시에 트래픽을 처리합니다. DNS가 클라이언트를 분산시키거나, 네트워크 수준 라우팅(ECMP)이 연결을 분산시킵니다. 더 복잡하지만, 낭비되는 용량이 없습니다.

클라우드 로드 밸런서: AWS, Google Cloud, Azure는 분산 서비스로 로드 밸런싱을 제공합니다. 가용 영역 전반에 걸쳐 이미 이중화되어 있습니다. 장애 전환을 구성할 필요가 없습니다—이미 내장되어 있습니다.

대부분의 현대적인 배포는 클라우드 로드 밸런서를 사용하며 로드 밸런서 가용성에 대해 전혀 고민할 필요가 없습니다. 클라우드 제공업체가 처리합니다.

하드웨어, 소프트웨어, 아니면 클라우드?

하드웨어 로드 밸런서는 전용 어플라이언스입니다. F5, Citrix. 고성능, 특화된 기능, 비쌉니다. 확장은 더 많은 하드웨어를 구매하는 것을 의미합니다. 레거시 인프라를 가진 기업에서 주로 볼 수 있습니다.

소프트웨어 로드 밸런서는 일반 서버에서 실행됩니다. Nginx, HAProxy, Envoy, Traefik. 유연하고, 비용 효율적이며, 인스턴스를 추가해 확장할 수 있습니다. 직접 관리해야 합니다.

클라우드 로드 밸런서는 관리형 서비스입니다. AWS ALB/NLB, Google Cloud Load Balancing, Azure Load Balancer. 관리할 인프라가 없고, 자동으로 확장되며, 사용한 만큼 지불합니다. 자체 관리보다 요청당 비용이 더 높지만, 운영 부담이 전혀 없습니다.

업계는 소프트웨어와 클라우드 방향으로 이동했습니다. 하드웨어 로드 밸런서는 레거시 환경 외에서는 점점 보기 어려워지고 있습니다.

엣지에서의 보안

로드 밸런서는 네트워크 경계에 위치하므로, 보안을 구현하기에 자연스러운 장소입니다.

SSL/TLS 종료: 로드 밸런서가 HTTPS를 처리하고, 백엔드로 전달하기 전에 트래픽을 복호화합니다. 백엔드는 사설 네트워크에서 일반 HTTP를 받습니다. 인증서 관리가 모든 서버가 아닌 한 곳에서 이루어집니다.

DDoS 흡수: 로드 밸런서는 트래픽 급증을 흡수하고, 개별 클라이언트에 속도 제한을 적용하고, 명백히 악의적인 요청을 필터링할 수 있습니다. 첫 번째 방어선입니다.

웹 애플리케이션 방화벽: 일부 로드 밸런서는 WAF 기능을 포함합니다—SQL 인젝션, XSS 및 기타 애플리케이션 계층 공격이 서버에 도달하기 전에 차단합니다.

접근 제어: 로드 밸런서에서 IP 허용 목록과 차단 목록을 적용해 트래픽이 애플리케이션에 닿기 전에 차단합니다.

보안을 로드 밸런서에 집중시키면, 백엔드는 애플리케이션 로직에만 집중할 수 있습니다. 자신에게 도달하는 트래픽은 이미 검증되었다고 신뢰합니다.

로드 밸런서에 대해 자주 묻는 질문

서버 장애 시 처리 중인 요청은 어떻게 되나요?

해당 요청들은 실패합니다—마법 같은 복구는 없습니다. 하지만 로드 밸런서는 상태 확인을 통해 즉시 장애를 감지하고 그쪽으로 새 요청을 보내는 것을 중단합니다. 대부분의 클라이언트는 실패한 요청을 자동으로 재시도합니다. 사용자는 잠깐 불편함을 경험할 수 있지만, 이후 요청은 정상 서버에서 성공합니다.

로드 밸런서는 WebSocket 연결을 어떻게 처리하나요?

WebSocket은 장기 연결입니다. 로드 밸런서는 연결 매핑을 유지합니다—WebSocket이 백엔드 서버에 연결되면, 그 연결은 지속되는 동안 해당 서버에 머뭅니다. Layer 7 로드 밸런서는 라우팅 결정을 위해 최초 HTTP 업그레이드 요청을 검사할 수 있지만, 업그레이드가 완료되면 연결은 유지됩니다.

애플리케이션과 같은 서버에서 로드 밸런서를 실행할 수 있나요?

기술적으로는 가능하지만, 목적에 어긋납니다. 로드 밸런서는 애플리케이션 서버가 장애날 때도 살아 있어야 합니다. 둘을 함께 실행하면, 제거하려던 단일 장애 지점을 직접 만드는 셈입니다.

백엔드 서버가 몇 대 필요한가요?

최소 두 대—그렇지 않으면 이중화가 없습니다. 그 이상은 트래픽과 서버 장애 시 얼마나 많은 여유 용량을 원하느냐에 따라 다릅니다. 서버가 세 대 있고 하나가 장애나면, 33%의 용량을 잃습니다. 열 대라면, 10%를 잃습니다.

언제 여러 로드 밸런서를 사용해야 하나요?

로드 밸런서 자체가 병목이 되는 경우(현대 하드웨어/클라우드에서는 드뭅니다) 또는 로드 밸런서 자체의 이중화가 필요한 경우입니다. DNS 또는 ECMP 라우팅을 사용한 액티브-액티브 구성은 용량 증가와 장애 전환을 동시에 제공합니다.

이전

` 태그로 처리합니다. 전반적으로 이 번역은 이제 상당히 자연스럽고 전문적입니다. 한국 네트워크 엔지니어가 읽었을 때 번역체처럼 느껴지지 않는 수준에 도달했습니다. </reflect2> <square2> 한국어 네트워킹 전문가로서 2차 번역을 평가합니다. **개선된 부분:** - "극적으로" → "크게" ✅ - "경비원" → "문지기" ✅ — 이게 훨씬 자연스럽습니다. - "공격자는 주소를 지정할 수 없는 것에 도달할 수 없습니다" → "주소를 알 수 없으면 공격도 할 수 없습니다" ✅ — 이제 한국어다운 표현입니다. - "일관된 해싱" → "일관성 해싱" ✅ - "결과는 수평 확장성입니다" → "이렇게 하면 수평 확장이 가능해집니다" ✅ - "클라이언트에게는 응답만 있으면 됩니다" ✅ **전체적인 평가:** - 기술 용어 처리가 일관되고 정확합니다 - 문장이 전반적으로 자연스럽습니다 - 원문의 따뜻하고 직접적인 어조가 잘 살아있습니다 - 마크다운 구조가 완벽하게 보존되었습니다 - 한국어 네트워크 전문가가 쓴 것 같은 느낌이 납니다 추가로 체크할 사항: - "상세한 가시성" — "상세한 모니터링 기능" 또는 "상세한 옵저버빌리티"가 더 자연스러울 수 있지만, 이 맥락에서 "가시성"도 무난합니다. - description도 번역이 잘 필요합니다 (final 태그에서 처리). 전반적으로 이 번역은 한국어 네트워크 엔지니어가 처음부터 한국어로 쓴 것처럼 느껴집니다. 더 이상의 수정은 오히려 품질을 떨어뜨릴 수 있습니다. **결정: Limit** — 번역이 그 형태에 도달했습니다. </square2> <final> <title>리버스 프록시란 무엇인가?

다음

애플리케이션 서버란 무엇인가?

이 페이지가 도움이 되었나요?

😔
🤨
😃
로드 밸런서와 웹 트래픽 • 라이브러리 • Connected