NGINX 공부하기

HelloPong·2025년 8월 4일

공부

목록 보기
8/39
post-thumbnail

🌀 NGINX란?

NGINX(엔진엑스)는 고성능 웹 서버이자 리버스 프록시, 로드 밸런서, HTTP 캐시 서버 역할을 할 수 있는 오픈소스 소프트웨어.

📌 한 줄 요약

NGINX는 정적 파일 서빙, 리버스 프록시, 로드밸런싱, 보안 강화까지 담당할 수 있는 다기능 웹 서버.

🔍 주요 역할

역할설명
1. 웹 서버HTML, CSS, JS, 이미지 같은 정적 파일을 직접 서빙
2. 리버스 프록시클라이언트 요청을 내부 서버(Spring, Node 등)로 중계
3. 로드 밸런서여러 백엔드 서버로 트래픽을 분산시킴
4. SSL 종료 (TLS Termination)HTTPS 연결의 암호 해제 처리를 담당함
5. 캐시 서버자주 요청되는 데이터를 캐싱하여 성능 향상

🧠 왜 많이 쓰일까?

  • 가볍고 빠르며, 동시접속 처리 능력이 우수함 (비동기 이벤트 기반)
  • 설정이 유연하고 직관적임 (nginx.conf)
  • 다른 웹 서버 (ex. Apache) 대비 리버스 프록시, SSL, 로드밸런싱 설정이 쉬움
  • Docker, Kubernetes, Spring Boot 배포 환경에서 프론트 게이트웨이 역할로 많이 사용됨

💎 주요 역할 톺아보기

🗂️ 정적 파일 서빙(Static File Serving)

개념

Nginx가 디스크의 파일(HTML/CSS/JS/이미지)을 직접 반환. 커널 sendfile, open_file_cache 등으로 매우 빠름.

핵심 지점

  • root vs alias: alias는 location 하위경로를 제거하고 매핑
  • SPA 라우팅: try_files $uri /index.html;
  • 캐시 헤더: 해시된 파일명에는 immutable 적극 사용
  • 대용량: sendfile on; tcp_nopush on; 로 처리 최적화
server {
    listen 443 ssl http2;
    server_name example.com;

    root /var/www/app/dist;          # 빌드 산출물 위치
    index index.html;

    # 정적 자원 캐시 정책
    location ~* \.(css|js|png|jpg|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000, immutable";
        try_files $uri =404;
    }

    # SPA 라우팅
    location / {
        try_files $uri /index.html;
    }

    # 큰 업로드 필요 시
    client_max_body_size 50m;

    # 파일 I/O 최적화
    sendfile on;
    tcp_nopush on;
    open_file_cache max=10000 inactive=60s;
    open_file_cache_valid 120s;
    open_file_cache_min_uses 2;
}

SPA 주의

  • try_files $uri /index.html; 없으면 /about 등 라우트가 404
  • alias 사용 시 마지막에 슬래시 규칙 주의(경로 중복/누락 이슈)

🚏 리버스 프록시(Reverse Proxy)

개념

클라이언트는 백엔드 주소를 모르고 항상 Nginx로만 접속. Nginx가 요청을 받아 라우팅·헤더정리·보안·버퍼링을 수행한 뒤 업스트림(백엔드) 으로 전달한다.

흐름

Client ──HTTPS──▶ Nginx(경계)
                 ├─▶ /api/*  → upstream api_svc (Spring 등)
                 └─▶ /      → 정적파일(or SPA index.html)

핵심 설정 포인트

  • proxy_set_header 로 원요청 정보를 보존: Host, X-Forwarded-For, X-Forwarded-Proto
  • WebSocket/SSE는 업그레이드 헤더 & 버퍼링 off
  • 업/다운스트림 타임아웃과 버퍼 크기 튜닝
  • 큰 업로드는 client_max_body_size 조정

예시

map $http_upgrade $connection_upgrade { default upgrade; '' close; }

upstream api_svc {
    server api1:8080;
    server api2:8080;
    keepalive 64;                     # 재사용 커넥션
}

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

    # SSL 섹션은 아래 SSL 파트에서 자세히 설명

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

        # WebSocket 지원(필요 시)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_read_timeout 60s;
        proxy_connect_timeout 5s;

        # SSE/WebSocket이라면 버퍼링 끄기
        # proxy_buffering off;
    }
}

자주 생기는 문제/팁

  • 502/504: 백엔드 타임아웃↑, DNS/서비스명 확인, keepalive 켜기
  • WebSocket 끊김: Upgrade/Connection 헤더 누락, proxy_read_timeout 너무 짧음
  • 원 IP가 백엔드에서 127.0.0.1로 보임: X-Forwarded-For 사용하도록 백엔드 로깅 설정

⚖️ 로드밸런싱(Load Balancing)

개념

Nginx가 여러 백엔드로 트래픽을 분산. 기본은 라운드로빈.

기법

  • 기본: Round Robin
  • 가중치: server api1:8080 weight=3;
  • 적은 연결 우선: least_conn; (롱폴링/SSE에 유리)
  • 클라이언트 기반 고정(“세션 스티키” 유사):
    - 오픈소스: ip_hash; 또는 hash $cookie_session consistent;
    - Plus/모듈: sticky cookie

헬스체크

  • OSS는 패시브(실패 감지): max_fails, fail_timeout
    액티브 헬스체크는 NGINX Plus나 외부로 구현
upstream api_svc {
    least_conn;                       # 연결 적은 서버에 분산
    server api1:8080 max_fails=3 fail_timeout=10s;
    server api2:8080 max_fails=3 fail_timeout=10s;
    keepalive 64;
}

/* 고정 분산(해시) 예시
upstream api_svc {
    hash $cookie_session_id consistent;
    server api1:8080;
    server api2:8080;
}
*/

  • 긴 연결(SSE/WebSocket)은 least_conn 추천
  • 스티키가 필요하면 해시 기반(쿠키/헤더)을 우선 고려
  • 백엔드가 HTTP Keep-Alive를 잘 쓰도록 keepalive 활성화

🔐 SSL/TLS 적용(“HTTPS 종단”)

개념

Nginx가 SSL 종단(TLS Termination): 클라이언트와 TLS, 내부는 HTTP로 백엔드 연결(물론 내부도 TLS 가능).

필수 구성요소

  • 인증서/개인키: ssl_certificate, ssl_certificate_key
  • 강한 프로토콜/암호군: TLS 1.2/1.3
  • HTTP→HTTPS 리다이렉트, HSTS, OCSP stapling, HTTP/2
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;  # HTTP → HTTPS 강제
}

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

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

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 1d;

    # 성능/보안
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # OCSP stapling (체인/CA 파일 필요)
    ssl_stapling on;
    ssl_stapling_verify on;

    # 이하 location 블록들…
}

Let’s Encrypt(자동화)

  • certbot으로 발급/갱신 자동화
  • 도커/쿠버네티스 환경이면 웹루트/standalone 모드나 nginx 플러그인 사용

자주 겪는 문제

  • wrong version number: TLS로 접근했는데 백엔드가 평문 응답(포트/프로토콜 혼선)
  • 체인 불완전: fullchain.pem 대신 cert.pem만 쓴 경우
  • ALPN/HTTP2 미활성: listen 443 ssl http2; 빠짐

🚀 캐싱 서버(Reverse Proxy Cache)

개념

Nginx가 백엔드 응답을 디스크에 캐시. 다음 동일 요청은 Nginx가 백엔드를 거치지 않고 바로 응답. CDN의 엣지 캐시 같은 역할을 내부에서 수행.

언제 좋은가

  • 읽기 많은 API(변경 적음), 이미지/썸네일 변환, 외부 API 프록시, 공개 페이지
  • “동일한 요청 = 동일한 응답”일수록 캐시 효율 ↑

핵심 포인트

  • proxy_cache_path 로 캐시존/디렉토리 정의
  • proxy_cache_key 로 캐시 식별자(호스트+경로+쿼리+헤더 등) 구성
  • Cache-Control, Expires, ETag, Vary 헤더를 원서버에서 제대로 설정하면 정책 통일 쉬움
  • 장애 시 stale 서빙과 cache lock으로 thundering herd 방지

예시(읽기 많은 /public-api 캐시)

# 1) 캐시 영역 정의 (디스크 경로, 용량, 만료 정책 인덱스)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:256m
                 max_size=20g inactive=30m use_temp_path=off;

map $request_method $cacheable {
    default 0;
    GET     1;
    HEAD    1;
}

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

    location /public-api/ {
        proxy_pass http://api_svc;

        # 2) 캐시 on/off
        proxy_cache api_cache;
        proxy_cache_methods GET HEAD;
        proxy_cache_key $scheme$host$request_uri;

        # 3) 조건부 캐시: GET/HEAD만
        proxy_no_cache $http_cache_control $http_authorization;
        proxy_cache_bypass $http_cache_control;

        # 4) 만료/스테일/락
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 updating;
        proxy_cache_lock on;                   # 동일 키 동시요청 하나만 백엔드로
        proxy_cache_lock_timeout 5s;
        proxy_cache_revalidate on;             # If-Modified-Since/ETag 재검증

        add_header X-Cache-Status $upstream_cache_status always;
    }
}

주의/베스트 프랙티스

  • 개인화/인증 헤더가 섞인 응답은 캐시 금지(Authorization, 세션 쿠키 등)
  • 응답의 Vary 헤더를 과하게 설정하면 캐시 효율 급감
  • 이미지/미디어는 별도 서브도메인(static.example.com) 과 긴 max-age + immutable 권장
  • I는 버전·쿼리·헤더가 키에 반영되도록 설계

🧩 올인원 샘플(요약형)

아래는 TLS 종단 + 정적 서빙 + /api 리버스프록시 + 로드밸런싱 + 캐시를 한 번에 보여주는 축약 버전:

map $http_upgrade $connection_upgrade { default upgrade; '' close; }
map $request_method $cacheable { default 0; GET 1; HEAD 1; }

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

upstream api_svc {
    least_conn;
    server api1:8080 max_fails=3 fail_timeout=10s;
    server api2:8080 max_fails=3 fail_timeout=10s;
    keepalive 64;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

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

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    root /var/www/app/dist;
    index index.html;

    # 정적 자원 캐시
    location ~* \.(css|js|png|jpg|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000, immutable";
        try_files $uri =404;
    }

    # SPA 라우팅
    location / {
        try_files $uri /index.html;
    }

    # 캐시가 유효한 공개 API
    location /public-api/ {
        proxy_pass http://api_svc;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_cache api_cache;
        proxy_cache_methods GET HEAD;
        proxy_cache_key $scheme$host$request_uri;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 updating;
        proxy_cache_lock on;
        proxy_cache_revalidate on;

        add_header X-Cache-Status $upstream_cache_status always;
    }

    # 인증 필요한 변경 API (캐시 금지)
    location /api/ {
        proxy_pass http://api_svc;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 60s;
        proxy_connect_timeout 5s;
        # proxy_buffering off;  # SSE/WebSocket이면 켜기
    }

    client_max_body_size 50m;
    sendfile on;
    tcp_nopush on;
}

✅ 실제 운영에서의 체크리스트

  • verse Proxy: 헤더 전달(Host/XFF/Proto), 타임아웃/버퍼, WebSocket 업그레이드
  • LB: least_conn(롱폴링 많으면), max_fails/fail_timeout, 필요 시 해시 기반 스티키
  • SSL: HTTP→HTTPS 리다이렉트, TLS1.2/1.3, HSTS, OCSP, http2
  • Static: SPA try_files, 해시파일에 immutable, sendfile/open_file_cache
  • Caching: 공개 GET만, 캐시키/무효화 정책, use_stale & cache_lock, X-Cache-Status로 관측

0개의 댓글