리버스 프록시와 API 게이트웨이
리버스 프록시와 API 게이트웨이¶
개요¶
이 문서에서는 리버스 프록시의 역할과 API 게이트웨이 패턴을 다룹니다. SSL 종료, 압축, 캐싱 등 리버스 프록시의 핵심 기능과 인증/인가, 라우팅, Rate Limiting 알고리즘을 학습합니다.
난이도: ⭐⭐⭐ 예상 학습 시간: 2-3시간 선수 지식: 04_Load_Balancing.md
목차¶
1. 리버스 프록시란?¶
1.1 Forward Proxy vs Reverse Proxy¶
┌─────────────────────────────────────────────────────────────────┐
│ Forward Proxy vs Reverse Proxy │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Forward Proxy (정방향 프록시) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ "클라이언트를 대신하여 서버에 요청" │ │
│ │ │ │
│ │ ┌──────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Client│───▶│ Forward │───▶│ Server │ │ │
│ │ │ │ │ Proxy │ │ (Web) │ │ │
│ │ └──────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ 용도: 익명성, 접근 제어, 캐싱 │ │
│ │ 예: 회사 방화벽, VPN, Squid │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Reverse Proxy (역방향 프록시) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ "서버를 대신하여 클라이언트 요청을 처리" │ │
│ │ │ │
│ │ ┌──────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Client│───▶│ Reverse │───▶│ Backend │ │ │
│ │ │ │ │ Proxy │ │ Servers │ │ │
│ │ └──────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ 클라이언트는 실제 서버를 모름! │ │
│ │ │ │
│ │ 용도: 로드밸런싱, SSL 종료, 캐싱, 보안 │ │
│ │ 예: Nginx, HAProxy, AWS ALB │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 리버스 프록시 위치¶
┌─────────────────────────────────────────────────────────────────┐
│ 리버스 프록시 아키텍처 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Internet │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Firewall │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Reverse │ ◀── SSL 종료, 캐싱 │
│ │ Proxy │ 압축, 보안 │
│ │ (Nginx) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ App │ │ App │ │ App │ │
│ │ Server │ │ Server │ │ Server │ │
│ │ 1 │ │ 2 │ │ 3 │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ 클라이언트 → 프록시 IP만 알고, 실제 서버 IP 모름 │
│ │
└─────────────────────────────────────────────────────────────────┘
2. 리버스 프록시 핵심 기능¶
2.1 SSL/TLS 종료 (SSL Termination)¶
┌─────────────────────────────────────────────────────────────────┐
│ SSL 종료 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SSL 종료 없이 (End-to-End Encryption): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Client ═══HTTPS═══▶ Proxy ═══HTTPS═══▶ Server │ │
│ │ │ │
│ │ • 모든 서버에 인증서 설치 필요 │ │
│ │ • 서버 부하 증가 (암호화/복호화) │ │
│ │ • 인증서 관리 복잡 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ SSL 종료 (프록시에서 처리): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Client ═══HTTPS═══▶ Proxy ───HTTP───▶ Server │ │
│ │ │ │ │
│ │ SSL 종료 │ │
│ │ │ │
│ │ • 인증서는 프록시에만 설치 │ │
│ │ • 서버 부하 감소 │ │
│ │ • 중앙 집중식 인증서 관리 │ │
│ │ │ │
│ │ Nginx 설정: │ │
│ │ server { │ │
│ │ listen 443 ssl; │ │
│ │ ssl_certificate /path/to/cert.pem; │ │
│ │ ssl_certificate_key /path/to/key.pem; │ │
│ │ │ │
│ │ location / { │ │
│ │ proxy_pass http://backend; # HTTP로 백엔드 연결 │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 주의: 내부 네트워크가 신뢰할 수 없으면 SSL Passthrough 고려 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 응답 압축¶
┌─────────────────────────────────────────────────────────────────┐
│ 응답 압축 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "네트워크 대역폭 절약, 응답 속도 향상" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 압축 전: 100KB HTML │ │
│ │ 압축 후: 20KB (gzip) → 80% 절약! │ │
│ │ │ │
│ │ Client Proxy Server │ │
│ │ │ │ │ │ │
│ │ │──Request ──────────▶│ │ │ │
│ │ │ Accept-Encoding: │──────────────────▶ │ │
│ │ │ gzip, deflate │ │ │ │
│ │ │ │◀─────────────────│ │ │
│ │ │ │ 원본 응답 │ │ │
│ │ │ │ (100KB) │ │ │
│ │ │ │ │ │ │
│ │ │◀── 압축 응답 ───────│ │ │ │
│ │ │ Content-Encoding: │ (프록시에서 │ │ │
│ │ │ gzip │ gzip 압축) │ │ │
│ │ │ (20KB) │ │ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Nginx 설정: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ gzip on; │ │
│ │ gzip_types text/plain text/css application/json │ │
│ │ application/javascript text/xml; │ │
│ │ gzip_min_length 1000; # 1KB 이상만 압축 │ │
│ │ gzip_comp_level 6; # 압축 레벨 (1-9) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 압축 알고리즘 비교: │
│ • gzip: 널리 지원, 좋은 압축률 │
│ • Brotli: 더 나은 압축률, 현대 브라우저 지원 │
│ • zstd: 빠른 압축/해제, 최신 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.3 캐싱¶
┌─────────────────────────────────────────────────────────────────┐
│ 프록시 캐싱 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "반복 요청에 대해 캐시된 응답 반환" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 첫 번째 요청 (Cache Miss): │ │
│ │ │ │
│ │ Client ──▶ Proxy ──▶ Server │ │
│ │ ◀────────◀──── (응답 저장) │ │
│ │ ▼ │ │
│ │ [Cache] │ │
│ │ │ │
│ │ 두 번째 요청 (Cache Hit): │ │
│ │ │ │
│ │ Client ──▶ Proxy │ │
│ │ ◀──── (캐시에서 응답, 서버 안 감!) │ │
│ │ ▲ │ │
│ │ [Cache] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Nginx 캐싱 설정: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ # 캐시 경로 정의 │ │
│ │ proxy_cache_path /var/cache/nginx levels=1:2 │ │
│ │ keys_zone=my_cache:10m │ │
│ │ max_size=10g inactive=60m; │ │
│ │ │ │
│ │ server { │ │
│ │ location / { │ │
│ │ proxy_cache my_cache; │ │
│ │ proxy_cache_valid 200 60m; # 200 응답 60분 캐시 │ │
│ │ proxy_cache_valid 404 1m; # 404 응답 1분 캐시 │ │
│ │ proxy_cache_use_stale error timeout; │ │
│ │ add_header X-Cache-Status $upstream_cache_status; │ │
│ │ proxy_pass http://backend; │ │
│ │ } │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 캐시 상태 헤더: │
│ • X-Cache-Status: HIT (캐시 적중) │
│ • X-Cache-Status: MISS (캐시 미스) │
│ • X-Cache-Status: EXPIRED (만료됨) │
│ • X-Cache-Status: STALE (오래된 캐시 사용) │
│ │
└─────────────────────────────────────────────────────────────────┘
2.4 보안 기능¶
┌─────────────────────────────────────────────────────────────────┐
│ 리버스 프록시 보안 기능 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. IP 기반 접근 제어 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ location /admin { │ │
│ │ allow 10.0.0.0/8; # 내부 네트워크만 허용 │ │
│ │ allow 192.168.1.100; # 특정 IP 허용 │ │
│ │ deny all; # 나머지 거부 │ │
│ │ proxy_pass http://backend; │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 2. 헤더 보안 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ # 민감한 헤더 숨기기 │ │
│ │ proxy_hide_header X-Powered-By; │ │
│ │ proxy_hide_header Server; │ │
│ │ │ │
│ │ # 보안 헤더 추가 │ │
│ │ add_header X-Frame-Options "SAMEORIGIN"; │ │
│ │ add_header X-Content-Type-Options "nosniff"; │ │
│ │ add_header X-XSS-Protection "1; mode=block"; │ │
│ │ add_header Strict-Transport-Security "max-age=31536000"; │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 3. 요청 크기 제한 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ client_max_body_size 10m; # 최대 업로드 크기 │ │
│ │ client_body_timeout 60s; # 요청 본문 타임아웃 │ │
│ │ client_header_timeout 60s; # 헤더 타임아웃 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 4. 백엔드 서버 숨기기 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 클라이언트가 보는 것: proxy.example.com │ │
│ │ 실제 백엔드: 10.0.1.5:8080 (외부 노출 안 됨) │ │
│ │ │ │
│ │ → 직접 공격 방지 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3. API 게이트웨이 패턴¶
3.1 API 게이트웨이란?¶
┌─────────────────────────────────────────────────────────────────┐
│ API 게이트웨이 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "마이크로서비스의 단일 진입점 (Single Entry Point)" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ API 게이트웨이 없이: │ │
│ │ │ │
│ │ Client ──▶ User Service (users.example.com) │ │
│ │ ──▶ Order Service (orders.example.com) │ │
│ │ ──▶ Product Service (products.example.com) │ │
│ │ │ │
│ │ 문제: │ │
│ │ • 클라이언트가 여러 서비스 URL 알아야 함 │ │
│ │ • 각 서비스마다 인증 로직 중복 │ │
│ │ • 클라이언트-서비스 강결합 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ API 게이트웨이 적용: │ │
│ │ │ │
│ │ ┌───────────────────────────────┐ │ │
│ │ │ API Gateway │ │ │
│ │ Client ──▶│ api.example.com │ │ │
│ │ │ ┌─────────────────────────┐ │ │ │
│ │ │ │ • 인증/인가 │ │ │ │
│ │ │ │ • 라우팅 │ │ │ │
│ │ │ │ • Rate Limiting │ │ │ │
│ │ │ │ • 요청/응답 변환 │ │ │ │
│ │ │ │ • 로깅/모니터링 │ │ │ │
│ │ │ └─────────────────────────┘ │ │ │
│ │ └───────────────┬───────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────────┼─────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ User Service Order Service Product Service │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 API 게이트웨이 핵심 기능¶
┌─────────────────────────────────────────────────────────────────┐
│ API 게이트웨이 핵심 기능 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 요청 라우팅 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ /api/users/* ──────────▶ User Service │ │
│ │ /api/orders/* ──────────▶ Order Service │ │
│ │ /api/products/* ──────────▶ Product Service │ │
│ │ /api/v2/* ──────────▶ New Service (버전 관리) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 2. 인증/인가 (Authentication/Authorization) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Client ──▶ Gateway │ │
│ │ │ │ │
│ │ ├─ JWT 토큰 검증 │ │
│ │ ├─ API Key 확인 │ │
│ │ ├─ OAuth2 처리 │ │
│ │ └─ 권한 확인 후 ──▶ Backend Service │ │
│ │ │ │
│ │ 장점: 백엔드 서비스에서 인증 로직 제거 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 3. 요청/응답 변환 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Protocol 변환: │ │
│ │ REST (외부) ←─────────▶ gRPC (내부) │ │
│ │ │ │
│ │ 데이터 형식 변환: │ │
│ │ JSON ←─────────▶ Protobuf │ │
│ │ │ │
│ │ 응답 집계 (Aggregation): │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ GET /api/dashboard │ │ │
│ │ │ │ │ │ │
│ │ │ ├──▶ User Service (사용자 정보) │ │ │
│ │ │ ├──▶ Order Service (최근 주문) │ │ │
│ │ │ └──▶ Stats Service (통계) │ │ │
│ │ │ │ │ │
│ │ │ ◀── 모든 응답 합쳐서 반환 ── │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 4. 로깅 및 모니터링 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 모든 API 호출 로깅 │ │
│ │ • 응답 시간 측정 │ │
│ │ • 에러율 모니터링 │ │
│ │ • 분산 트레이싱 (Trace ID 전파) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 API 게이트웨이 제품 비교¶
┌─────────────────────────────────────────────────────────────────┐
│ API 게이트웨이 제품 비교 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Kong │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • Nginx + Lua 기반 │ │
│ │ • 풍부한 플러그인 에코시스템 │ │
│ │ • 오픈소스 / Enterprise │ │
│ │ • 높은 성능 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ AWS API Gateway │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 완전 관리형 │ │
│ │ • Lambda, DynamoDB 통합 │ │
│ │ • WebSocket 지원 │ │
│ │ • 사용량 기반 과금 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Envoy (Istio) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 클라우드 네이티브 │ │
│ │ • 서비스 메시 사이드카 │ │
│ │ • gRPC 네이티브 지원 │ │
│ │ • 고급 트래픽 관리 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Nginx Plus / Nginx │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 경량, 고성능 │ │
│ │ • 설정 기반 (코드 불필요) │ │
│ │ • 광범위한 사용 사례 │ │
│ │ • Plus: 상용 기능 (헬스체크, 모니터링) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 선택 기준: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 클라우드 환경: AWS API Gateway, GCP Cloud Endpoints │ │
│ │ • 온프레미스: Kong, Nginx │ │
│ │ • Kubernetes: Envoy, Istio, NGINX Ingress │ │
│ │ • 간단한 요구: Nginx │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4. Rate Limiting¶
4.1 Rate Limiting이란?¶
┌─────────────────────────────────────────────────────────────────┐
│ Rate Limiting │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "단위 시간당 요청 수를 제한" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 왜 필요한가? │ │
│ │ │ │
│ │ • DDoS 방어 │ │
│ │ • 서비스 안정성 보호 │ │
│ │ • 공정한 리소스 분배 │ │
│ │ • 비용 제어 │ │
│ │ • API 남용 방지 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 제한 기준: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • IP 주소별: 100 req/min per IP │ │
│ │ • 사용자별: 1000 req/hour per user │ │
│ │ • API 키별: 10000 req/day per API key │ │
│ │ • 엔드포인트별: /login은 5 req/min │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 응답 헤더: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ X-RateLimit-Limit: 100 # 제한 │ │
│ │ X-RateLimit-Remaining: 45 # 남은 요청 수 │ │
│ │ X-RateLimit-Reset: 1640000000 # 리셋 시간 (Unix) │ │
│ │ │ │
│ │ 제한 초과 시: 429 Too Many Requests │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.2 토큰 버킷 (Token Bucket)¶
┌─────────────────────────────────────────────────────────────────┐
│ 토큰 버킷 알고리즘 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "버킷에 토큰이 있으면 요청 허용, 없으면 거부" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ Token │ ◀── 일정 속도로 토큰 추가 │ │
│ │ │ Bucket │ (예: 10개/초) │ │
│ │ │ ●●●●● │ │ │
│ │ │ ●●● │ ◀── 버킷 용량 (예: 100개) │ │
│ │ └────┬─────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Request ──▶ 토큰 있음? ──Yes──▶ 허용 (토큰 소비) │ │
│ │ │ │ │
│ │ No │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 거부 (429) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 특징: │
│ • 버스트 허용 (버킷에 토큰이 쌓여있으면) │
│ • 평균 속도 제한 │
│ • AWS, Stripe 등에서 사용 │
│ │
│ 예시: │
│ 버킷 크기: 100, 충전 속도: 10/초 │
│ → 순간 100개 요청 가능 (버스트) │
│ → 이후 초당 10개 요청 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
4.3 리키 버킷 (Leaky Bucket)¶
┌─────────────────────────────────────────────────────────────────┐
│ 리키 버킷 알고리즘 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "일정한 속도로만 요청 처리 (버스트 없음)" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Requests ──▶ ┌──────────┐ │ │
│ │ │ Queue │ ◀── 요청을 큐에 저장 │ │
│ │ │ ○○○○○ │ │ │
│ │ │ ○○○ │ ◀── 큐 크기 제한 │ │
│ │ └────┬─────┘ │ │
│ │ │ │ │
│ │ ● │ │ │
│ │ ● ◀───┘ 일정 속도로 "새어나감" │ │
│ │ ● (예: 10개/초) │ │
│ │ ▼ │ │
│ │ 처리됨 │ │
│ │ │ │
│ │ 큐가 가득 찼을 때: │ │
│ │ 새 요청 ──▶ 거부 (429) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 특징: │
│ • 출력 속도가 일정 (트래픽 쉐이핑) │
│ • 버스트 불가 │
│ • 네트워크 트래픽 조절에 적합 │
│ │
│ 토큰 버킷 vs 리키 버킷: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 토큰 버킷: 버스트 허용, 평균 속도 제한 │ │
│ │ 리키 버킷: 버스트 불가, 고정 속도 출력 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.4 고정 윈도우 (Fixed Window)¶
┌─────────────────────────────────────────────────────────────────┐
│ 고정 윈도우 알고리즘 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "시간 윈도우마다 카운터 리셋" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1분 윈도우, 제한 100개: │ │
│ │ │ │
│ │ 00:00 ~ 00:59 01:00 ~ 01:59 │ │
│ │ ┌────────────────────┐ ┌────────────────────┐ │ │
│ │ │ Count: 0 → 100 │ │ Count: 0 (리셋) │ │ │
│ │ │ ●●●●●●●●●●●●●●●●● │ │ ●●●●●●●●● │ │ │
│ │ └────────────────────┘ └────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 문제점 (경계 문제): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 00:00 00:30 01:00 01:30 │ │
│ │ │ │ │ │ │ │
│ │ └──────────────┴──────────────┴──────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ 00:30~00:59 01:00~01:29 │ │
│ │ 100개 요청 100개 요청 │ │
│ │ │ │
│ │ → 1분 사이에 200개 요청 가능! │ │
│ │ (00:30 ~ 01:30) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 장점: 구현 단순, 메모리 효율 │
│ 단점: 윈도우 경계에서 버스트 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
4.5 슬라이딩 윈도우 (Sliding Window)¶
┌─────────────────────────────────────────────────────────────────┐
│ 슬라이딩 윈도우 알고리즘 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "시간에 따라 윈도우가 이동 (경계 문제 해결)" │
│ │
│ 슬라이딩 윈도우 로그: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 각 요청의 타임스탬프를 저장 │ │
│ │ [00:30:15, 00:30:20, 00:30:45, 00:31:00, ...] │ │
│ │ │ │
│ │ 현재 시간: 01:30:00 │ │
│ │ 윈도우: 00:30:00 ~ 01:30:00 (1분) │ │
│ │ │ │
│ │ 윈도우 내 요청 수 계산 │ │
│ │ │ │
│ │ 단점: 메모리 사용량 높음 (모든 타임스탬프 저장) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 슬라이딩 윈도우 카운터 (최적화): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 이전 윈도우와 현재 윈도우의 가중 평균 │ │
│ │ │ │
│ │ 현재: 01:15 (1분 윈도우) │ │
│ │ │ │
│ │ ├── 이전 윈도우 (00:00~01:00): 80개 ──┤ │ │
│ │ ├── 현재 윈도우 (01:00~02:00): 40개 ──┤ │ │
│ │ │ │
│ │ 01:15는 현재 윈도우의 25% 지점 │ │
│ │ 가중치: 이전 75%, 현재 25% │ │
│ │ │ │
│ │ 예상 요청 수 = 80 * 0.75 + 40 * 0.25 = 70개 │ │
│ │ │ │
│ │ 장점: 메모리 효율 (카운터 2개만) │ │
│ │ 경계 문제 완화 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.6 알고리즘 비교¶
| 알고리즘 | 버스트 | 메모리 | 정확도 | 사용 사례 |
|---|---|---|---|---|
| 토큰 버킷 | 허용 | 작음 | 높음 | API 제한 |
| 리키 버킷 | 불가 | 작음 | 높음 | 트래픽 쉐이핑 |
| 고정 윈도우 | 경계 버스트 | 작음 | 낮음 | 단순 구현 |
| 슬라이딩 윈도우 | 허용 | 큼/중간 | 높음 | 정확한 제한 |
5. 연습 문제¶
문제 1: 리버스 프록시 설계¶
다음 요구사항을 만족하는 Nginx 설정을 작성하세요.
- HTTPS 수신 (포트 443)
- HTTP → HTTPS 리다이렉트
- 백엔드: http://localhost:8080
- Gzip 압축 활성화
- 정적 파일 캐싱 (1일)
문제 2: API 게이트웨이 설계¶
마이크로서비스 환경에서 API 게이트웨이를 설계하세요.
서비스: - User Service: /api/users/ - Order Service: /api/orders/ - Auth Service: /api/auth/*
요구사항: - JWT 인증 (Auth 제외) - Rate Limiting: 1000 req/min per user - 응답 로깅
문제 3: Rate Limiting 선택¶
다음 시나리오에 적합한 Rate Limiting 알고리즘을 선택하세요.
a) 공개 API (버스트 허용, 평균 제한) b) 실시간 스트리밍 서비스 c) 로그인 엔드포인트 (브루트 포스 방지) d) 단순한 요구사항, 빠른 구현
문제 4: Rate Limiting 구현¶
토큰 버킷 알고리즘을 의사 코드로 구현하세요.
조건: - 버킷 크기: 100 - 충전 속도: 10 토큰/초
정답¶
문제 1 정답¶
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Gzip 압축
gzip on;
gzip_types text/plain text/css application/json
application/javascript text/xml;
gzip_min_length 1000;
# 정적 파일 캐싱
location /static/ {
expires 1d;
add_header Cache-Control "public, immutable";
}
# 백엔드 프록시
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
문제 2 정답¶
API Gateway 설계:
┌─────────────────────────────────────────────────┐
│ API Gateway │
├─────────────────────────────────────────────────┤
│ │
│ 1. 요청 수신 │
│ │ │
│ 2. Rate Limiting 체크 (Redis 기반) │
│ │ key: user_id, limit: 1000/min │
│ │ │
│ 3. 경로 확인 │
│ ├── /api/auth/* → Auth Service (인증 Skip)│
│ ├── /api/users/* → JWT 검증 → User Service│
│ └── /api/orders/*→ JWT 검증 → Order Service│
│ │
│ 4. 응답 로깅 (ELK Stack) │
│ - timestamp, user_id, path, status, │
│ response_time │
│ │
└─────────────────────────────────────────────────┘
기술 스택:
- Kong Gateway + JWT Plugin + Rate Limiting Plugin
- 또는 AWS API Gateway + Lambda Authorizer
문제 3 정답¶
a) 공개 API: 토큰 버킷
- 버스트 허용으로 사용자 경험 좋음
- 평균 속도 제한으로 남용 방지
b) 실시간 스트리밍: 리키 버킷
- 일정한 출력 속도로 품질 유지
- 버스트 불가로 안정적인 전송
c) 로그인: 고정 윈도우 또는 슬라이딩 윈도우
- 엄격한 제한 (예: 5회/분)
- 경계 문제보다 단순함 우선
d) 단순한 요구: 고정 윈도우
- 구현 가장 단순
- 카운터 + 타임스탬프만 필요
문제 4 정답¶
class TokenBucket:
def __init__(self, capacity=100, refill_rate=10):
self.capacity = capacity # 버킷 최대 크기
self.refill_rate = refill_rate # 초당 토큰 추가
self.tokens = capacity # 현재 토큰 수
self.last_refill = current_time()
def allow_request(self):
self.refill()
if self.tokens >= 1:
self.tokens -= 1
return True
else:
return False
def refill(self):
now = current_time()
elapsed = now - self.last_refill
# 경과 시간에 비례하여 토큰 추가
tokens_to_add = elapsed * self.refill_rate
self.tokens = min(self.capacity, self.tokens + tokens_to_add)
self.last_refill = now
# 사용 예
bucket = TokenBucket(capacity=100, refill_rate=10)
if bucket.allow_request():
process_request()
else:
return 429 # Too Many Requests
6. 다음 단계¶
리버스 프록시와 API 게이트웨이를 이해했다면, 캐싱 전략을 학습하세요.
다음 레슨¶
관련 레슨¶
- 04_Load_Balancing.md - 트래픽 분산
- 07_Distributed_Cache_Systems.md - Redis, Memcached
추천 실습¶
- Nginx 리버스 프록시 설정 실습
- Kong Gateway 설치 및 플러그인 테스트
- Rate Limiting 직접 구현해보기
7. 참고 자료¶
도구¶
문서¶
알고리즘¶
문서 정보 - 최종 수정: 2024년 - 난이도: ⭐⭐⭐ - 예상 학습 시간: 2-3시간