개요

Nginx는 웹 서버이자 리버스 프록시, 로드 밸런서로 널리 사용됩니다. 이 글에서는 Nginx를 활용한 고급 리버스 프록시 설정과 로드밸런싱 전략을 다룹니다. SSL/TLS 종단, 헬스 체크, 세션 어피니티, 캐싱 전략 등 실무에서 바로 적용 가능한 설정을 중심으로 설명합니다.

핵심 개념

리버스 프록시는 클라이언트 요청을 받아 백엔드 서버로 전달하고 응답을 중계하는 역할을 합니다. 로드밸런싱은 여러 백엔드 서버에 트래픽을 분산하여 가용성과 확장성을 높입니다. Nginx는 이벤트 기반 아키텍처로 높은 동시성을 효율적으로 처리합니다.

1. 기본 리버스 프록시 설정

# /etc/nginx/sites-available/myapp
upstream backend {
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
    server 192.168.1.103:8080;
}

server {
    listen 80;
    server_name myapp.example.com;

    location / {
        proxy_pass http://backend;
        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;

        # 타임아웃 설정
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

2. SSL/TLS 종단 설정

백엔드는 HTTP로 두고 Nginx에서만 HTTPS를 처리하여 인증서 관리를 단순화합니다.

server {
    listen 443 ssl http2;
    server_name myapp.example.com;

    # SSL 인증서 (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem;

    # 최신 TLS 설정 (2025 권장)
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;

    # HSTS (Strict-Transport-Security)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/myapp.example.com/chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;

    # 세션 캐시
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    location / {
        proxy_pass http://backend;
        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 https;
    }
}

# HTTP to HTTPS 리다이렉트
server {
    listen 80;
    server_name myapp.example.com;
    return 301 https://$server_name$request_uri;
}

3. 로드밸런싱 전략

Nginx는 다양한 로드밸런싱 알고리즘을 지원합니다.

# 1. Round Robin (기본값)
upstream backend_rr {
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

# 2. Least Connections (최소 연결)
upstream backend_lc {
    least_conn;
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

# 3. IP Hash (세션 어피니티)
upstream backend_ip {
    ip_hash;
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

# 4. Weighted (가중치)
upstream backend_weighted {
    server 192.168.1.101:8080 weight=3;  # 더 많은 트래픽
    server 192.168.1.102:8080 weight=1;
    server 192.168.1.103:8080 backup;    # 백업 서버
}

# 5. Generic Hash (커스텀 키)
upstream backend_hash {
    hash $request_uri consistent;  # URL 기반 분산
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
}

4. 헬스 체크와 장애 조치

upstream backend_health {
    server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.103:8080 backup;

    # Nginx Plus에서만 사용 가능 (능동 헬스 체크)
    # zone backend 64k;
}

# max_fails: 실패 횟수 임계값
# fail_timeout: 서버를 다시 시도하기 전 대기 시간
# backup: 모든 주 서버 실패 시에만 사용
# down: 일시적으로 서버 제외 (유지보수 시)

실전 예제

1. 마이크로서비스 라우팅

경로별로 다른 백엔드 서비스로 라우팅합니다.

upstream auth_service {
    server 192.168.1.101:8081;
}

upstream api_service {
    least_conn;
    server 192.168.1.102:8082;
    server 192.168.1.103:8082;
}

upstream frontend_service {
    server 192.168.1.104:3000;
}

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

    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    # 인증 API
    location /auth {
        proxy_pass http://auth_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # REST API
    location /api {
        proxy_pass http://api_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # CORS 헤더
        add_header Access-Control-Allow-Origin * always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
    }

    # 프론트엔드
    location / {
        proxy_pass http://frontend_service;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # WebSocket 지원
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

2. 캐싱 설정

정적 콘텐츠와 API 응답을 캐싱하여 백엔드 부하를 줄입니다.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m use_temp_path=off;

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # 정적 파일 캐싱 (1년)
    location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        proxy_pass http://backend;
        proxy_cache api_cache;
        proxy_cache_valid 200 365d;
        add_header X-Cache-Status $upstream_cache_status;
        expires 1y;
        access_log off;
    }

    # API 응답 캐싱 (5분)
    location /api/products {
        proxy_pass http://backend;
        proxy_cache api_cache;
        proxy_cache_valid 200 5m;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_bypass $http_cache_control;
        add_header X-Cache-Status $upstream_cache_status;
    }

    # POST 요청은 캐싱 안 함
    location /api {
        proxy_pass http://backend;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
    }
}

3. Rate Limiting

IP별 요청 제한으로 DDoS와 브루트포스를 방어합니다.

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # 일반 API (초당 10개, 버스트 20개)
    location /api {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://backend;
    }

    # 로그인 (분당 5개)
    location /api/login {
        limit_req zone=login_limit;
        limit_req_status 429;

        proxy_pass http://auth_service;
    }

    # 429 에러 페이지 커스터마이징
    error_page 429 /rate_limit.html;
    location = /rate_limit.html {
        internal;
        default_type application/json;
        return 429 '{"error":"Too Many Requests","retry_after":60}';
    }
}

활용 팁

  • 로그 분석: GoAccess 같은 도구로 Nginx 로그를 실시간 분석하면 트래픽 패턴을 쉽게 파악할 수 있습니다.
  • 성능 튜닝: worker_processes는 CPU 코어 수와 동일하게, worker_connections는 시스템 ulimit에 맞춰 조정합니다.
  • 보안 헤더: X-Frame-Options, X-Content-Type-Options, Content-Security-Policy 등을 add_header로 추가합니다.
  • A/B 테스팅: split_clients 지시어로 트래픽을 비율별로 분산하여 새 버전을 테스트할 수 있습니다.
  • Gzip 압축: gzip on으로 텍스트 기반 콘텐츠를 압축하면 대역폭을 크게 절약할 수 있습니다.
  • 모니터링: nginx-prometheus-exporter와 Grafana를 연동하면 메트릭을 시각화할 수 있습니다.

마무리

Nginx는 단순한 웹 서버를 넘어 강력한 리버스 프록시와 로드 밸런서로 활용할 수 있습니다. SSL 종단, 헬스 체크, 캐싱, Rate Limiting 등의 기능을 조합하면 백엔드 서버를 보호하면서도 높은 성능을 유지할 수 있습니다. 설정 변경 후에는 반드시 nginx -t로 문법을 검증하고, reload로 무중단 적용하세요. 프로덕션 환경에서는 upstream 서버를 점진적으로 추가/제거하며 트래픽 패턴을 관찰한 후 최적의 로드밸런싱 전략을 선택하는 것이 중요합니다.