10 minute read

리버스 프록시, 로드 밸런서, API 게이트웨이는 시스템 구성도에서 거의 같은 모양으로 그려진다. 사용자와 서버 사이, 같은 자리에 상자 하나, 같은 방향의 화살표. 그래서 자주 섞어 쓰고, “로드 밸런서로 게이트웨이를 대신할 수 있나” 같은 질문에서 막힌다.

이 글은 서버 한 대로 시작한 앱이 커지면서 차례로 만나는 세 가지 문제를 따라가며, 각 구성 요소가 들어오는 요청에 대해 어떤 질문에 답하는지로 셋을 구분한다. 개념 설명은 RFC와 Microsoft Learn 문서를 기준으로 정리했고, 다이어그램은 직접 작성했다. 마지막 장에는 ADFS 팜에서 세 역할이 실제로 어디에 배치되는지를 붙였다. API 게이트웨이의 정책 측면은 API 설계 패턴 8가지 6장에서 다뤘다.


1. 출발점: 서버 한 대

서버 한 대에서 전체 구성까지

처음에는 서버 한 대가 공인 IP로 직접 요청을 받는다. 사용자가 접속하는 주소가 곧 실제 서버의 주소다. 이 구성에서 차례로 세 가지 문제가 생긴다.

단계 문제 추가하는 역할
1 실제 서버가 인터넷에 그대로 노출됨 리버스 프록시
2 서버 한 대가 트래픽을 감당하지 못함 로드 밸런서
3 앱이 여러 서비스로 나뉘고, 공통 기능이 서비스마다 중복됨 API 게이트웨이

2. 리버스 프록시: 서버를 숨기고 앞에서 대신 받는다

리버스 프록시

해결하려는 문제

실제 서버의 주소가 공개돼 있으면 공격자도 그 주소로 직접 접근할 수 있다. 서버의 OS·웹 서버 취약점, 관리 포트가 모두 인터넷에 노출된다.

개념

실제 서버 앞에 서버 한 대를 두고, 외부에는 그 앞단 서버의 주소만 공개한다. 사용자는 앞단 서버와 통신하고, 앞단 서버가 요청을 뒤의 실제 서버로 전달한 뒤 응답을 돌려준다. 사용자는 실제 서버의 존재를 알 수 없다. 이 앞단 서버가 리버스 프록시다.

직접 접근을 막는 것은 프록시 자체가 아니라 네트워크 구성이다. 실제 서버에 공인 IP를 주지 않고, 방화벽이나 보안 그룹에서 프록시가 보낸 트래픽만 허용해야 우회가 막힌다. 프록시는 통제된 출입구이고, 우회로를 없애는 것은 네트워크 규칙이다.

포워드 프록시와의 차이

항목 포워드 프록시 리버스 프록시
위치 클라이언트 쪽 (사내망 출구) 서버 쪽 (서비스 입구)
대리하는 대상 클라이언트 서버
숨기는 것 클라이언트의 IP 서버의 존재와 주소
주 용도 사내 인터넷 접속 통제, 캐시, 필터링 서버 보호, TLS 처리, 라우팅, 캐시
예 사내 웹 프록시(PAC 설정) NGINX, Web Application Proxy(WAP)

출입구에서 하는 일

모든 요청이 한 곳을 지나므로, 실제 서버에 닿기 전에 여기서 처리할 수 있는 일이 많다.

기능 내용
TLS 종료 인증서를 프록시에 두고 HTTPS를 여기서 복호화. 뒤 서버는 인증서 관리 부담이 줄어든다
캐시 정적 파일이나 자주 요청되는 응답을 프록시가 바로 반환
압축 gzip·brotli 압축을 프록시에서 처리
라우팅 호스트 이름이나 경로로 뒤 서버를 선택 (a.example.com → 서버 A)
필터링 요청 크기 제한, 허용 메서드 제한, WAF 규칙으로 악성 요청 차단

클라이언트 IP 전달

프록시를 거치면 뒤 서버에서 보이는 접속 IP는 사용자가 아니라 프록시의 IP다. 접근 로그, IP 기반 차단, 계정 잠금처럼 클라이언트 IP가 필요한 기능이 모두 프록시 IP를 기준으로 동작하게 된다. 그래서 프록시가 원래 클라이언트 IP를 헤더에 담아 전달한다.

X-Forwarded-For: 203.0.113.10, 198.51.100.7
X-Forwarded-Proto: https
Forwarded: for=203.0.113.10;proto=https;by=198.51.100.7
  • X-Forwarded-For: 사실상 표준(de facto). 프록시를 하나 지날 때마다 IP가 뒤에 덧붙는다. 가장 왼쪽이 최초 클라이언트다.
  • Forwarded: RFC 7239로 표준화된 헤더. for(클라이언트), by(프록시), proto, host를 한 헤더에 담는다.
  • 이 헤더는 클라이언트도 임의로 넣을 수 있다. 서버는 자신이 신뢰하는 프록시가 붙인 값만 사용해야 한다. 예를 들어 X-Forwarded-For를 오른쪽부터 읽어, 신뢰하는 프록시 IP를 건너뛴 첫 번째 값을 클라이언트 IP로 본다.

NGINX 설정으로 보면 다음과 같다.

server {
    listen 443 ssl;
    server_name app.example.com;

    location / {
        proxy_pass http://10.0.1.10:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

3. 로드 밸런서: 같은 복제본 중 어디로 보낼까

로드 밸런서

해결하려는 문제

사용자가 늘어 서버 한 대로는 버티지 못한다. 같은 앱을 세 대 띄웠다. 이제 요청이 올 때마다 “세 대 중 어느 서버가 받을까”를 정해야 한다. 이 결정을 초당 수천 번 내리는 것이 로드 밸런서다.

L4와 L7

항목 L4 로드 밸런서 L7 로드 밸런서
판단 기준 IP, 포트 (TCP·UDP 연결) HTTP 내용 (호스트, 경로, 헤더, 쿠키)
분산 단위 연결 요청
TLS 보통 그대로 통과 종료 후 내용을 보고 분산 가능
경로 기반 라우팅 불가 가능
장점 빠르고 프로토콜 무관 세밀한 라우팅, 헤더 추가, WAF 연계
Azure 예 Azure Load Balancer Application Gateway, Front Door

L7 로드 밸런서는 사실상 분산 기능이 붙은 리버스 프록시다. 셋이 같은 상자로 그려지는 이유가 여기서 나온다.

분산 알고리즘

알고리즘 동작 적합한 경우
라운드 로빈 1 → 2 → 3 → 1 순서로 배분 서버 성능이 같고 요청 비용이 비슷할 때
가중 라운드 로빈 서버별 가중치 비율로 배분 서버 사양이 다를 때, 새 버전에 일부만 보낼 때
최소 연결 현재 연결이 가장 적은 서버로 요청마다 처리 시간이 크게 다를 때
해시 클라이언트 IP나 키의 해시로 고정 배분 같은 클라이언트를 같은 서버로 보내야 할 때

헬스 체크

로드 밸런서가 단순한 분배기가 아닌 이유가 헬스 체크다. 서버 한 대가 멈추면 로드 밸런서가 이를 감지하고 그 서버로 보내던 요청을 정상 서버로 돌린다. 사용자는 장애를 알아채지 못한다.

  • 능동 체크: 로드 밸런서가 주기적으로 확인용 요청을 보낸다(예: GET /health → 200 OK). 연속 몇 회 실패하면 제외하고, 다시 성공하면 복귀시킨다.
  • 수동 체크: 실제 요청의 실패(연결 거부, 타임아웃, 5xx)를 보고 판단한다.
  • 연결 드레이닝: 배포나 점검으로 서버를 뺄 때 새 요청만 막고, 처리 중인 요청은 끝날 때까지 기다린다.

헬스 체크 엔드포인트는 “프로세스가 떠 있는가”가 아니라 “요청을 처리할 수 있는가”를 반영해야 한다. 웹 서버는 응답하는데 DB 연결이 끊긴 서버를 정상으로 판정하면 장애 요청이 계속 들어간다.

세션 고정과 무상태

로그인 세션을 서버 메모리에 두는 앱은 같은 사용자의 요청이 항상 같은 서버로 가야 한다. 이를 세션 고정(sticky session, affinity)이라 한다. 쿠키나 IP 해시로 구현하지만, 서버가 빠지면 그 서버의 세션이 사라지고 부하도 고르게 퍼지지 않는다. 가능하면 세션을 공유 저장소(Redis 등)에 두거나 토큰 기반으로 바꿔 서버를 무상태로 만들고, 로드 밸런서는 자유롭게 분산하게 하는 편이 낫다. 세션과 토큰의 차이는 인증 개념 7가지에서 다뤘다.

TLS 처리 방식

방식 동작 특징
통과 (pass-through) 암호화된 채로 서버에 전달 (L4) 종단 간 암호화 유지, 로드 밸런서는 내용을 못 봄
종료 (termination, offloading) 로드 밸런서에서 복호화 후 평문으로 전달 서버 부담 감소, 내부 구간은 평문
재암호화 (re-encryption, bridging) 복호화 후 다시 TLS로 서버에 연결 내용 기반 라우팅 + 내부 구간 암호화

어떤 방식이 가능한지는 뒤 애플리케이션에 따라 다르다. 클라이언트 인증서 인증이나 채널 바인딩을 쓰는 서비스는 중간에서 TLS를 끊으면 동작하지 않는다. 7장의 ADFS가 대표적인 예다.


4. API 게이트웨이: 서로 다른 서비스 중 어디로, 어떤 정책으로

API 게이트웨이

해결하려는 문제

앱이 마이크로서비스로 나뉘었다. 로그인은 인증 서비스, 주문은 주문 서비스, 결제는 결제 서비스가 처리한다.

  • 클라이언트가 모든 서비스의 주소를 알아야 한다.
  • 인증, 요청 제한, 로깅을 서비스마다 각자 구현한다. 같은 코드가 여섯 벌, 열 벌 복사되고, 구현이 조금씩 달라진다.

로드 밸런서로는 해결되지 않는다. 로드 밸런서 뒤에는 같은 앱의 복제본이 있지만, 여기서는 뒤에 있는 서비스가 서로 다르기 때문이다.

개념

모든 서비스 앞에 출입구를 하나 두고, 요청 내용을 읽어 어느 서비스로 보낼지 정한다. 동시에 모든 서비스에 공통인 정책을 출입구에서 한 번 적용한다.

POST /login      → 인증 서비스
GET  /orders/981 → 주문 서비스
POST /payments   → 결제 서비스
공통 기능 내용
인증 1차 검증 Access Token 서명·만료 확인, 실패 시 401
요청 제한 클라이언트·API 키별 호출량 제한, 초과 시 429
로깅·추적 요청 ID 부여, 접근 로그, 지연 시간 지표
요청·응답 변환 헤더 추가·제거, 프로토콜 변환
API 키·구독 관리 외부 파트너별 키 발급과 사용량 집계 (API Management 계열)

주의사항

  • 게이트웨이가 인증을 맡는다고 서비스의 인가 검사가 사라지지 않는다. 게이트웨이는 “유효한 토큰인가”를 확인하고, 각 서비스는 “이 사용자가 이 주문을 볼 수 있는가”를 확인한다.
  • 비즈니스 로직은 게이트웨이에 넣지 않는다. 넣는 순간 모든 서비스 배포가 게이트웨이 배포에 묶인다.

5. 구분하는 법: 각자 어떤 질문에 답하는가

세 가지 질문

무엇을 하는지(기능 목록)로 비교하면 겹치는 부분이 많아 구분이 안 된다. 들어온 요청에 대해 어떤 질문에 답하는지로 보면 명확해진다.

역할 답하는 질문 뒤에 있는 것 핵심 판단 근거
리버스 프록시 이 요청을 어디로 전달할까 실제 서버 (숨김 대상) 설정된 전달 대상, 호스트·경로
로드 밸런서 어느 정상 인스턴스가 받을까 같은 앱의 복제본 여러 개 헬스 상태, 부하, 분산 알고리즘
API 게이트웨이 어느 서비스가 받고, 어떤 정책을 적용할까 서로 다른 서비스 여러 개 경로·메서드, 인증·요청 제한 정책

어느 서버로, 어느 인스턴스로, 어느 서비스로. 같은 자리에서 서로 다른 세 질문에 답한다.

왜 같아 보이는가

실제로 겹치기 때문이다. 리버스 프록시가 기반 개념이고, 나머지 둘은 그 위에 역할을 더한 것이다.

  • L7 로드 밸런서 = 리버스 프록시 + 복제본 간 분산 + 헬스 체크
  • API 게이트웨이 = 리버스 프록시 + 서비스별 라우팅 + API 정책(인증, 요청 제한)

제품이 아니라 역할로 본다

역할과 제품

하나의 제품이 여러 역할을 겸하는 경우가 흔하다. 같은 NGINX가 설정에 따라 리버스 프록시도, 로드 밸런서도, 간단한 게이트웨이도 된다. 그래서 “NGINX는 리버스 프록시인가 로드 밸런서인가”는 답이 없는 질문이다. 설치한 제품의 이름이 아니라, 그 상자가 지금 어떤 역할을 하는지를 봐야 한다.

제품 주된 역할 함께 할 수 있는 역할
NGINX 리버스 프록시 L7 로드 밸런싱, 단순 게이트웨이 기능
HAProxy L4·L7 로드 밸런서 리버스 프록시
Envoy L7 프록시 (서비스 메시 데이터 플레인) 로드 밸런싱, 게이트웨이 제품의 기반
Traefik, Kong 리버스 프록시·API 게이트웨이 로드 밸런싱
Azure Load Balancer L4 로드 밸런서 (L7 기능 없음)
Azure Application Gateway L7 로드 밸런서 리버스 프록시, TLS 종료, WAF, 경로 기반 라우팅
Azure Front Door 전역 L7 로드 밸런서·CDN 리버스 프록시, WAF
Azure API Management API 게이트웨이 백엔드 간 분산 (라운드 로빈·가중치·우선순위)
Web Application Proxy (WAP) 리버스 프록시 (ADFS·사내 앱 게시) 사전 인증

Microsoft도 API Management의 분산 기능은 이미 게이트웨이로 쓰고 있을 때만 활용하고, 분산만을 위해 도입하지 말라고 안내한다. 한 제품이 여러 역할을 할 수 있어도 책임은 구분해서 설계한다.


6. 실제 시스템에서의 배치

실제 배치 예시

정답 구성은 없다. 다음은 흔한 구성 중 하나다.

인터넷 → CDN → 로드 밸런서 → API 게이트웨이 → 서비스
                                    ├→ 인증 서비스 (앞에 리버스 프록시)
                                    ├→ 주문 서비스 (앞에 로드 밸런서 + 복제본 3대)
                                    └→ 결제 서비스 (단일 인스턴스)
  • 맨 앞의 로드 밸런서는 게이트웨이 인스턴스 여러 대에 트래픽을 나눈다. 게이트웨이 자체도 단일 장애 지점이 되면 안 되기 때문이다.
  • 게이트웨이 뒤에서도 서비스마다 필요에 따라 프록시나 로드 밸런서가 또 들어간다. 어떤 서비스에는 아무것도 없다.
  • 고정된 순서를 외울 필요는 없다. 각 상자는 그 역할이 필요한 자리에 나타난다.

7. 실무 예시: ADFS 팜에서의 세 역할

ADFS 팜 구성

ADFS를 외부에 공개하는 구성은 리버스 프록시와 로드 밸런서가 실제로 어떻게 쓰이는지 잘 보여 준다.

인터넷 → [LB] → WAP 1, WAP 2 (DMZ) → [LB] → ADFS 1, ADFS 2 (내부망) → DC
사내망 → [LB] → ADFS 1, ADFS 2
구성 요소 역할
WAP (Web Application Proxy) 리버스 프록시. DMZ에서 외부 요청을 받아 내부 ADFS로 전달. ADFS 서버는 인터넷에 노출되지 않음
WAP 앞 로드 밸런서 WAP 여러 대에 외부 트래픽 분산
ADFS 앞 로드 밸런서 WAP와 사내 클라이언트의 요청을 ADFS 서버 여러 대에 분산

외부 사용자는 ADFS 서비스 이름(예: sts.example.com)을 WAP 앞 로드 밸런서로, WAP와 사내 클라이언트는 같은 이름을 ADFS 앞 로드 밸런서로 해석하도록 DNS를 나눠 구성한다(split DNS).

Microsoft의 로드 밸런서 요구사항

ADFS FAQ는 ADFS·WAP 앞의 로드 밸런서에 대해 다음을 요구한다. 3장의 일반론이 실제 제품에서 어떻게 제약으로 나타나는지 보여 주는 예다.

요구사항 이유
IP 고정(affinity)을 쓰지 않는다 ADFS는 무상태라 고정이 필요 없고, Exchange Online 시나리오처럼 특정 IP에서 요청이 몰리면 일부 서버에 부하가 쏠린다
HTTPS를 종료하고 ADFS로 새 연결을 맺지 않는다 FAQ는 이유를 따로 적지 않지만, 클라이언트 인증서 인증과 Windows 통합 인증의 확장 보호(Extended Protection, 채널 바인딩)는 클라이언트와 ADFS 사이의 TLS 채널을 전제로 동작한다
클라이언트 원래 IP를 보존하거나 X-Forwarded-For에 넣는다 Banned IP, Extranet Smart Lockout 같은 IP 기반 보호 기능이 올바르게 동작하려면 필요. 잘못 구성하면 보안 수준이 낮아진다
SNI를 지원한다 지원하지 않으면 SNI 미지원 클라이언트용 HTTPS 바인딩을 ADFS에 따로 구성해야 한다
ADFS HTTP 헬스 프로브로 상태를 확인한다 200 OK가 아니면 해당 ADFS·WAP 서버를 제외

WAP 앞에서 TLS를 끊으면 생기는 일

WAP는 ADFS로 요청을 전달할 때 접속한 클라이언트의 IP를 X-MS-Forwarded-Client-IP 헤더에 담는다(MS-ADFSPIP 규격). 그런데 WAP 앞의 장비에서 TLS를 종료하면, WAP 입장에서 접속한 상대는 그 장비이므로 이 값에 사용자 IP가 아니라 장비 IP가 들어간다. ADFS는 모든 외부 로그인이 같은 IP에서 온 것으로 보게 되고, Extranet Smart Lockout이 사용자 단위가 아니라 그 장비 IP 하나를 기준으로 동작한다. Microsoft는 WAP 앞의 TLS 종료를 권장하지 않으며, ADFS와 WAP는 엔드포인트에서 TLS를 종료하지 않는 방화벽이라면 지원한다고 안내한다.

ADFS가 쓰는 IP 관련 클레임은 다음과 같다.

클레임 값
x-ms-client-ip ADFS에 직접 연결한 장치의 IP. 외부 요청이면 항상 WAP의 IP
x-ms-forwarded-client-ip WAP에 연결한 장치의 IP (+ Exchange Online이 전달한 값)
userip 외부 요청이면 x-ms-forwarded-client-ip 값, 내부 요청이면 x-ms-client-ip 값

ADFS 2016(최신 패치)부터는 X-Forwarded-For도 수집하므로, IP를 보존하지 못하는 장비는 이 헤더에 클라이언트 IP를 넣어야 한다. 2장의 “클라이언트 IP 전달”이 ADFS 운영에서는 계정 잠금 정책의 정확도 문제로 이어진다.


8. 자주 나오는 질문

인증을 각 마이크로서비스에 넣으면 안 되나

넣을 수는 있지만, 서비스가 여섯 개면 인증 구현도 여섯 벌이고 틀릴 기회도 여섯 번이다. 게이트웨이에서 한 번 처리하면 일관되고, 고칠 곳도 한 곳이다. 다만 게이트웨이가 서비스의 보안을 대신하는 것은 아니다. 각 서비스는 여전히 자기 리소스에 대한 인가를 직접 확인한다.

로드 밸런서로 API 게이트웨이를 대신할 수 있나

일반적으로는 아니다. 로드 밸런서는 같은 복제본 중 하나를 고르고, 게이트웨이는 서로 다른 서비스 중 하나를 고르면서 인증·요청 제한 같은 API 정책을 적용한다. L7 로드 밸런서가 경로 기반 라우팅까지는 할 수 있어도, API 키·토큰 검증·호출량 제한 같은 정책 계층은 게이트웨이의 책임이다.

API 게이트웨이가 로드 밸런서를 대신할 수 있나

제품에 따라 한 제품이 두 역할을 겸할 수 있다. 하지만 책임은 다르다. 게이트웨이는 어느 서비스로 보내고 어떤 정책을 적용할지를, 로드 밸런서는 인스턴스 사이에 트래픽을 어떻게 나눌지를 책임진다. 같은 제품이어도 설계할 때는 두 책임을 따로 본다.


9. 정리

전체 정리

항목 리버스 프록시 로드 밸런서 API 게이트웨이
해결하는 문제 서버 노출 트래픽 증가, 서버 장애 서비스 분리, 공통 기능 중복
질문 어디로 전달할까 어느 정상 인스턴스로 어느 서비스로, 어떤 정책으로
뒤에 있는 것 실제 서버 같은 앱의 복제본 서로 다른 서비스
핵심 기능 숨김, TLS 처리, 캐시, 필터링 분산 알고리즘, 헬스 체크 경로 라우팅, 인증, 요청 제한
실무 예 WAP, NGINX Azure Load Balancer, HAProxy, ADFS 앞단 L4 Azure API Management, Kong

세 상자의 이름을 외우기보다, 지금 그 자리의 상자가 어떤 결정을 내리는지를 보면 된다. 그 결정이 “어디로”면 프록시, “어느 복제본으로”면 로드 밸런서, “어느 서비스로, 어떤 규칙으로”면 게이트웨이다.


참고

Comments