테마 전환

Nginx 성능 튜닝: gzip, 캐시, 커넥션 풀 설정

Easton editorial illustration: service mesh rail yard

지난주 이커머스 사이트의 홈 로딩 시간이 4초까지 치솟았다는 알림을 받았습니다. Chrome DevTools를 열어 보니 HTML 파일은 120KB, CSS와 JS를 합치면 350KB였는데 모두 압축되지 않은 원본 파일이었습니다. 더 심각한 문제는 모든 요청이 백엔드로 전달되고 캐시 적중률은 고작 12%에 불과했다는 점입니다. 그날 밤 야근하며 2시간 동안 Nginx 설정을 조정했습니다. gzip 압축을 켜고, 캐시 전략을 보완하고, 커넥션 풀 매개변수를 손봤습니다. 다음 날 아침 다시 확인하니 홈 로딩 시간은 1.6초로 줄었고 백엔드 QPS도 거의 절반으로 감소했습니다.

이런 문제는 너무 흔합니다. 많은 사람이 Nginx를 설치한 뒤 기본 상태로 실행합니다. gzip은 기본적으로 꺼져 있고, 캐시 설정은 대충 두 줄만 추가하며, 최대 연결 수는 기본값을 그대로 씁니다. 그러다 트래픽이 몰리면 서버가 버티지 못합니다.

이 글에서는 제가 프로덕션 환경에서 검증한 Nginx 성능 튜닝 설정을 정리합니다. gzip 압축은 전송 용량을 6080% 줄이고, 캐시 적중률이 95%라면 백엔드 부하는 90% 줄일 수 있습니다. 커넥션 풀을 적절히 설정하면 동시 처리 능력도 34배 높일 수 있습니다. 각 모듈의 설정 세부 사항과 직접 겪은 문제, 실측 데이터를 하나씩 자세히 살펴보겠습니다.

1장: gzip 압축 설정 — 전송 용량 줄이기

먼저 gzip이 왜 그렇게 중요한지 알아보겠습니다. 원본 크기가 100KB인 HTML 파일을 gzip으로 압축하면 2025KB까지 줄어들 수 있습니다. 절약한 7580KB의 대역폭은 사용자에게는 더 빠른 로딩 속도를, 운영자에게는 더 낮은 트래픽 비용을 의미합니다.

제가 이 문제를 처음 실감한 것은 한 고객의 프로젝트에서였습니다. 모바일 사용자가 60%를 넘었고 상당수가 4G 네트워크로 접속했습니다. 홈 로딩에 3~4초가 걸리면서 이탈률이 70%까지 치솟았습니다. gzip을 적용하자 전송 용량이 곧바로 70% 줄었고 첫 화면 로딩 시간도 약 1.5초로 단축됐습니다.

1.1 기본 설정: 우선 작동하게 만들기

Nginx의 gzip 설정은 사실 복잡하지 않습니다. 핵심은 몇 줄뿐입니다.

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

gzip on은 활성화 스위치이므로 별도 설명이 필요 없습니다. gzip_vary on은 매우 중요합니다. 응답 헤더에 Vary: Accept-Encoding을 추가해 CDN과 브라우저에 클라이언트의 압축 지원 여부에 따라 응답 내용이 달라진다는 사실을 알리고, 잘못된 캐시가 사용되는 것을 방지합니다.

gzip_min_length를 1000바이트로 설정하면 1KB보다 작은 파일은 압축하지 않습니다. 너무 작은 파일은 압축 효과가 크지 않고 오히려 CPU만 낭비합니다. gzip_types는 압축할 MIME 유형을 지정합니다. 기본값은 text/html만 압축하므로 CSS, JS, JSON, XML도 추가해야 합니다.

1.2 고급 설정: 압축 레벨과 MIME 유형 목록

압축 레벨은 균형이 필요한 선택입니다. Nginx의 gzip_comp_level은 1~9로 설정할 수 있습니다. 숫자가 클수록 압축률은 높아지지만 CPU 사용량도 증가합니다.

제가 진행한 테스트 결과는 다음과 같습니다.

압축 레벨HTML 압축률CPU 시간(ms)권장 환경
165%2CPU 자원이 빠듯한 환경
472%3균형형(권장)
675%5대역폭이 부족한 환경(권장)
978%12극단적인 환경

대부분의 경우 레벨 4와 6이 가장 좋은 선택입니다. 레벨 9는 CPU 사용량이 두 배로 늘지만 압축률은 몇 퍼센트포인트만 개선되므로 효율적이지 않습니다.

아래는 제가 프로덕션 환경에서 사용하는 전체 gzip 설정입니다.

# gzip 압축 설정
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml
    application/xml+rss
    application/xhtml+xml
    application/x-javascript;
gzip_disable "msie6";

gzip_proxied any는 놓치기 쉬운 매개변수입니다. Nginx를 리버스 프록시로 사용할 때 백엔드 응답 헤더에 Content-Length가 없으면 기본적으로 압축하지 않습니다. any로 설정하면 조건에 맞는 모든 응답을 강제로 압축할 수 있습니다.

gzip_disable "msie6"은 구형 IE6와의 호환성을 위한 설정입니다. IE6는 gzip 지원에 문제가 있습니다. 이제 IE6는 거의 사라졌으므로 사실 이 줄은 지워도 되지만, 저는 혹시 모를 상황에 대비해 습관적으로 남겨 둡니다.

1.3 어떤 파일 유형의 효과가 가장 큰가요?

제가 측정한 데이터에 따르면 파일 유형에 따라 압축 효과에 큰 차이가 있습니다.

파일 유형원본 크기압축 후압축률
HTML100KB20~25KB75~80%
CSS80KB24~28KB65~70%
JavaScript120KB36~42KB65~70%
JSON API50KB20~25KB50~60%
이미지/동영상이미 압축됨의미 없음0~5%
75~80%
HTML 파일 압축률
Source: 프로덕션 환경 실측 데이터

이미지와 동영상은 JPEG, PNG, MP4 같은 형식으로 이미 압축되어 있어 gzip을 다시 적용하면 오히려 용량이 커질 수 있습니다. 따라서 gzip_typesimage/*video/*를 절대 추가하지 마세요. 추가하면 역효과가 납니다.

한번은 다른 사람의 문제를 진단하다가 gzip_typesimage/jpeg를 추가해 둔 것을 발견했습니다. 그 결과 이미지 용량이 오히려 3~5% 커졌습니다. 저도 예전에 잘 몰랐을 때는 이런 초보적인 실수를 했을 겁니다. 당시에는 모든 MIME 유형을 전부 넣고 싶었으니까요.

2장: 캐시 전략 설정 — 정적 콘텐츠 가속

캐시는 성능 튜닝에서 가장 직접적인 효과를 내는 요소입니다. 제대로 설정하면 요청의 95%를 백엔드로 전달하지 않고 Nginx에서 바로 응답할 수 있습니다. 백엔드 서버는 과부하로 힘들어하는데 Nginx 캐시는 거의 활용되지 않는 시스템을 너무 많이 봤습니다. 캐시를 켜지 않은 것이 아니라 잘못 설정한 경우가 많았습니다.

2.1 proxy_cache와 fastcgi_cache 중 무엇을 선택할까요?

Nginx는 두 가지 캐시 메커니즘을 제공합니다.

  • proxy_cache: 업스트림 서버의 응답을 캐시하며, 리버스 프록시 환경에 적합합니다(예: Node.js, Python, Go 서비스).
  • fastcgi_cache: FastCGI 프로세스의 응답을 캐시하며, PHP-FPM 환경에 적합합니다.

무엇을 선택할지는 백엔드 기술 스택에 따라 달라집니다. PHP라면 fastcgi_cache를, Node.js, Python, Go 등을 사용한다면 proxy_cache를 선택하세요. 두 방식의 설정 논리는 거의 같으므로 여기서는 proxy_cache를 예로 들어 설명하겠습니다.

2.2 전체 proxy_cache 설정

먼저 http 블록에서 캐시 경로를 정의합니다.

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

각 줄의 의미는 다음과 같습니다.

  • levels=1:2: 캐시 디렉터리 계층입니다. 1:2는 2단계 디렉터리 구조를 뜻하며 한 디렉터리에 파일이 지나치게 많아지는 것을 방지합니다.
  • keys_zone=my_cache:10m: 캐시 영역 이름과 메타데이터 메모리 크기입니다. 10m에는 약 8만 개의 캐시 키를 저장할 수 있습니다.
  • max_size=10g: 전체 캐시 용량의 상한입니다. 이를 초과하면 LRU 알고리즘으로 항목을 제거합니다.
  • inactive=60m: 60분 동안 접근하지 않은 캐시는 정리됩니다.
  • use_temp_path=off: 임시 파일 이동 비용을 피하기 위해 캐시 디렉터리에 직접 기록합니다.

그런 다음 server 또는 location에서 캐시를 활성화합니다.

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;

        # 캐시 유효 기간 설정
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_valid any 1m;

        # 캐시 키 설계
        proxy_cache_key $scheme$request_method$host$request_uri;

        # 폴백 전략(뒤에서 자세히 설명)
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

        # 응답 헤더에 캐시 상태 추가(디버깅용)
        add_header X-Cache-Status $upstream_cache_status;
    }
}

proxy_cache_valid는 상태 코드별 캐시 기간을 정의하는 핵심 설정입니다.

  • 200 302 10m: 정상 응답을 10분간 캐시합니다.
  • 404 1m: 악의적인 요청이 백엔드를 계속 두드리지 않도록 404 오류를 1분간 캐시합니다.
  • any 1m: 그 밖의 상태 코드는 1분간 캐시합니다.

2.3 캐시 키 설계와 무효화 전략

캐시 키 proxy_cache_key는 어떤 요청을 ‘동일한’ 요청으로 볼지 결정합니다. 기본값은 $scheme$proxy_host$request_uri이지만 명시적으로 다음과 같이 선언하는 것을 권장합니다.

proxy_cache_key $scheme$request_method$host$request_uri;

이렇게 하면 캐시 키에 프로토콜, 요청 메서드, 호스트 이름, 전체 URI가 포함됩니다. 사이트가 GET과 POST를 모두 지원하거나 도메인이 여러 개라면 이 설정이 더 정확합니다.

캐시 무효화는 까다로운 문제입니다. 일반적인 전략은 다음과 같습니다.

  1. 시간 만료: proxy_cache_valid로 시간을 설정하고 만료 시 자동으로 무효화합니다.
  2. 능동적 우회: proxy_cache_bypass로 캐시를 우회합니다.
  3. 캐시 제거: 상용판 Nginx Plus의 proxy_cache_purge를 사용합니다.

저는 보통 두 번째 방식으로 요청 헤더를 통해 제어합니다.

# 특정 요청 헤더로 캐시 우회
proxy_cache_bypass $http_x_nocache;

# 또는 특정 매개변수로 우회
proxy_cache_bypass $arg_nocache;

캐시를 새로 고쳐야 할 때 ?nocache=1을 붙이거나 X-Nocache: 1 요청 헤더를 추가하면 됩니다.

2.4 폴백 전략: 백엔드가 중단되어도 서비스 유지하기

proxy_cache_use_stale은 매우 유용한 설정입니다. 백엔드 서비스에서 오류가 발생하거나 시간이 초과됐을 때 Nginx는 즉시 오류를 반환하는 대신 만료된 캐시 콘텐츠를 제공할 수 있습니다.

proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

작년 광군제 기간에 백엔드 서비스를 확장하다가 문제가 생겨 API 서비스에서 간헐적으로 502 오류가 발생했습니다. 다행히 캐시에 이 폴백 전략을 설정해 두어 사용자 접속에는 거의 영향이 없었습니다. 캐시가 몇 분 지난 상태였지만 콘텐츠는 정상적으로 반환됐습니다. 백엔드가 복구된 뒤에는 캐시도 자동으로 갱신됐습니다.

실측 데이터를 비교하면 다음과 같습니다.

지표캐시 없음캐시 적중폴백 모드
응답 시간150~200ms5~10ms5~10ms
백엔드 QPS1000500
사용자 경험정상정상약간 느림
95%
캐시 적중률
Source: 프로덕션 환경 실측

캐시가 적중하면 응답 시간이 200ms에서 5~10ms로 줄어 거의 20배 빨라집니다. 효과가 매우 분명합니다.

3장: 커넥션 풀 설정 — 고동시성 환경의 필수 요소

gzip과 캐시가 ‘어떻게 더 빠르게 전송할지’를 해결한다면 커넥션 풀은 ‘어떻게 더 많은 요청을 처리할지’를 해결합니다. 기본 설정에서 Nginx worker 하나의 최대 동시 연결 수는 1024개에 불과합니다. 트래픽이 늘면 곧바로 부족해집니다.

3.1 worker_connections: 상한을 정확히 계산하기

최대 동시 연결 수의 계산식은 다음과 같습니다.

최대 동시 연결 수 = worker_processes × worker_connections

서버에 8코어 CPU가 있고 worker_processes를 8로 설정하거나 auto로 자동 일치시키며, worker_connections를 4096으로 설정했다고 가정해 보겠습니다.

최대 동시 연결 수 = 8 × 4096 = 32768

수치만 보면 매우 커 보이지만, 요청 하나는 보통 두 개의 연결을 사용한다는 점을 기억해야 합니다. 하나는 클라이언트와 Nginx 사이, 다른 하나는 Nginx와 백엔드 사이의 연결입니다. 따라서 실제로 처리할 수 있는 동시 요청 수는 대략 이 수치의 절반입니다.

events 블록에 다음과 같이 설정합니다.

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

use epoll은 Linux의 기본값이라 명시할 필요는 없지만, 적어 두면 더 분명합니다. multi_accept on은 worker가 여러 새 연결을 동시에 받아들이게 해 고동시성 환경에서 연결 대기를 줄입니다.

3.2 클라이언트 keepalive: 연결을 재사용해 비용 줄이기

TCP 연결을 수립하려면 3-way handshake가 필요하며 적지 않은 비용이 듭니다. keepalive를 사용하면 클라이언트와 Nginx 사이의 연결을 재사용할 수 있어 요청마다 새 연결을 맺지 않아도 됩니다.

http {
    keepalive_timeout 65;
    keepalive_requests 1000;
}

keepalive_timeout 65는 연결을 65초 동안 유지한 뒤 종료한다는 뜻입니다. 값이 너무 크면 서버 자원을 지나치게 많이 차지하고, 너무 작으면 재사용 효과가 크지 않습니다. 60~75초가 합리적인 범위입니다.

keepalive_requests 1000은 연결 하나에서 최대 1000개의 요청을 처리한다는 뜻입니다. 값이 너무 작으면 연결이 자주 끊기고, 너무 크면 자원 누수가 생길 수 있습니다. 제가 테스트한 결과 1000은 비교적 안정적인 값입니다.

3.3 upstream keepalive: 백엔드 커넥션 풀

많은 사람이 잘 모르지만 효과가 분명한 설정입니다. Nginx와 백엔드 서비스 사이에서도 연결을 재사용해 TCP 연결을 반복해서 수립하는 비용을 줄일 수 있습니다.

upstream backend {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;

    keepalive 64;
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

keepalive 64는 유휴 연결 64개를 풀에 유지한다는 뜻입니다. 백엔드 서비스 수에 맞춰 조정하며 일반적으로 백엔드 서버 수의 4~8배로 설정합니다.

proxy_http_version 1.1proxy_set_header Connection "" 두 줄은 반드시 추가해야 합니다. HTTP/1.1은 기본적으로 keepalive를 지원하며, Connection 헤더를 비워야 연결을 재사용할 수 있습니다. 이 두 줄을 쓰지 않으면 upstream keepalive가 작동하지 않습니다.

실측 결과를 비교하면 다음과 같습니다.

설정분당 연결 수립 횟수CPU 비용권장 환경
upstream keepalive 없음6000높음저트래픽
keepalive 323000중간중간 트래픽
keepalive 641500낮음고트래픽

upstream keepalive를 적용하자 연결 수립 비용이 곧바로 50% 줄었습니다. 이 설정은 고동시성 환경에서 특히 중요합니다.

3.4 권장 매개변수 요약표

환경별 권장 설정을 표로 정리했습니다.

매개변수저트래픽(<1000 QPS)중간 트래픽(1000~5000 QPS)고트래픽(>5000 QPS)
worker_processesautoautoauto
worker_connections102420484096
keepalive_timeout606575
keepalive_requests1005001000
upstream keepalive163264

이는 출발점일 뿐이며 실제 조정은 부하 테스트 데이터를 바탕으로 해야 합니다. 저는 보통 wrk나 ab로 부하 테스트를 실행해 연결 수와 응답 시간의 변화 곡선을 확인하고 최적값을 찾습니다.

4장: 통합 설정 템플릿 — 프로덕션 환경에 바로 적용하기

앞의 세 장에서는 원리를 설명했습니다. 여기서는 통합 설정 템플릿을 바로 제공합니다. 실제 환경에 맞춰 매개변수를 조정할 수 있지만 전체 구조는 공통으로 사용할 수 있습니다.

# nginx.conf 프로덕션 환경 템플릿

user nginx;
worker_processes auto;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # gzip 압축 설정
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml
               application/xml+rss application/xhtml+xml;
    gzip_disable "msie6";

    # 캐시 경로 설정
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;

    # 클라이언트 연결 설정
    keepalive_timeout 65;
    keepalive_requests 1000;

    # 백엔드 서버 그룹
    upstream backend {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        keepalive 64;
        keepalive_timeout 60s;
        keepalive_requests 1000;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";

            # 캐시 설정
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_key $scheme$request_method$host$request_uri;
            proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

            # 디버깅용 응답 헤더
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

세 가지 환경별 설정 차이

이커머스 사이트: 홈과 상품 상세 페이지가 자주 바뀌므로 캐시 시간은 짧게 설정하며 10~15분이면 충분합니다. 데이터베이스 쿼리가 많고 백엔드 부하가 크므로 upstream keepalive는 크게 설정해야 합니다.

API 서비스: 데이터 실시간성이 중요하므로 proxy_cache_valid가 1~5분에 불과할 수 있습니다. gzip은 JSON에 효과적이므로 반드시 추가하세요.

정적 사이트: HTML, CSS, JS가 거의 바뀌지 않으므로 캐시를 1시간 또는 그 이상으로 설정할 수 있습니다. 텍스트 기반 정적 파일이 많아 gzip 압축 효과가 가장 큽니다.

5장: 자주 발생하는 문제와 해결 방법

제가 자주 겪은 문제와 해결 방법을 바로 정리해 보겠습니다.

Q: gzip 압축이 적용되지 않고 응답 헤더에 Content-Encoding: gzip이 없습니다.

다음 세 가지를 확인하세요.

  1. gzip on이 올바른 설정 계층인 http 블록에 있는지 확인합니다.
  2. gzip_types에 해당 응답의 MIME 유형이 포함되어 있는지 확인합니다.
  3. 응답 본문 크기가 gzip_min_length보다 큰지 확인합니다.

curl로 테스트합니다. curl -H "Accept-Encoding: gzip" -I http://your-site.com

Q: 캐시 적중률이 매우 낮고 X-Cache-Status는 대부분 MISS입니다.

일반적인 원인은 다음과 같습니다.

  • 캐시 키 설계가 잘못되어 각 요청이 모두 ‘서로 다른’ 요청으로 처리됩니다.
  • proxy_cache_valid가 너무 짧아 캐시를 사용하기도 전에 만료됩니다.
  • 백엔드 응답 헤더에 Cache-Control: no-cache 또는 Set-Cookie가 있습니다.

응답 헤더를 확인해 캐시를 금지하는 지시문이 없는지 확인하세요.

Q: worker_connections가 부족해 502 오류가 발생합니다.

Nginx 오류 로그를 확인하세요. worker_connections are not enough가 있다면 동시 연결 수가 상한을 넘었다는 뜻입니다.

해결 방법은 다음과 같습니다.

  1. worker_connections 값을 늘립니다.
  2. 연결 누수가 있는지 확인합니다(keepalive 설정이 적절하지 않을 수 있습니다).
  3. 서버를 추가해 부하 분산하는 방안을 검토합니다.

Q: 메모리 사용량이 너무 높고 서버에서 OOM이 자주 발생합니다.

가능한 원인은 다음과 같습니다.

  • proxy_cache_pathkeys_zone이 너무 큽니다.
  • 캐시 파일이 너무 많아 메모리 매핑 사용량이 높습니다.
  • keepalive 커넥션 풀이 너무 커서 유휴 연결이 자원을 차지합니다.

이 매개변수들을 적절히 줄이거나 서버 메모리를 늘리세요.

마치며

Nginx 성능 튜닝의 핵심 세 가지는 gzip 압축, 캐시 전략, 커넥션 풀 설정입니다. 세 모듈을 적절히 조합하면 웹사이트 로딩 속도를 두 배 높이고 동시 처리 능력을 3~4배 향상할 수 있습니다.

튜닝은 한 번으로 끝나는 작업이 아닙니다. 다음 순서로 진행하는 것을 권장합니다.

  1. 먼저 gzip 활성화: 변경 범위가 가장 작고 효과가 가장 직접적이며 10분이면 완료할 수 있습니다.
  2. 그다음 캐시 설정: 비즈니스 환경에 맞는 캐시 전략을 설계하며 하루 안에 완료할 수 있습니다.
  3. 마지막으로 커넥션 풀 조정: 부하 테스트로 검증해야 하므로 트래픽이 안정된 뒤 진행하는 것이 좋습니다.

변경할 때마다 부하 테스트로 효과를 검증하세요. wrk나 ab를 사용해 응답 시간, QPS, 오류율의 변화를 확인할 수 있습니다. 감으로 조정하지 말고 데이터에 근거해 판단해야 합니다.

마지막 점검 목록입니다.

  • gzip이 활성화되어 있고 MIME 유형 설정이 완전합니다.
  • CPU와 압축률의 균형을 위해 gzip_comp_level을 4~6으로 설정했습니다.
  • proxy_cache_path를 설정했고 캐시 용량이 적절합니다.
  • 비즈니스 환경에 맞게 proxy_cache_valid를 설정했습니다.
  • proxy_cache_use_stale 폴백 전략을 설정했습니다.
  • worker_connections를 4096 이상으로 설정했습니다.
  • keepalive_timeout을 60~75초로 설정했습니다.
  • upstream keepalive를 설정했습니다(HTTP/1.1과 Connection 헤더 포함).
  • 디버깅을 위해 응답 헤더에 X-Cache-Status가 포함되어 있습니다.

이 정도면 핵심 내용은 모두 다뤘습니다. 궁금한 점이 있으면 댓글로 남겨 주세요. 가능한 한 답변하겠습니다.

Nginx 성능 튜닝 설정 절차

gzip 압축, 캐시 전략, 커넥션 풀 최적화를 3단계로 완료하는 프로덕션 환경 설정 절차입니다.

⏱️ Estimated time: 30 min

  1. 1

    Step 1: gzip 압축 활성화

    http 블록에 설정을 추가합니다.

    • gzip on; 압축 활성화
    • gzip_vary on; Vary 응답 헤더 추가
    • gzip_comp_level 6; 압축 레벨 설정(권장 4~6)
    • gzip_min_length 1000; 1KB 미만은 압축하지 않음
    • gzip_types로 MIME 유형 지정: text/plain text/css application/json application/javascript
  2. 2

    Step 2: proxy_cache 캐시 설정

    두 단계로 설정합니다.

    1단계: 캐시 경로 정의
    • proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m

    2단계: location에서 캐시 활성화
    • proxy_cache my_cache;
    • proxy_cache_valid 200 10m; 정상 응답을 10분간 캐시
    • proxy_cache_use_stale error timeout http_502; 폴백 전략 설정
  3. 3

    Step 3: 커넥션 풀 매개변수 최적화

    핵심 설정은 세 가지입니다.

    • worker_connections 4096; events 블록에서 설정
    • keepalive_timeout 65; keepalive_requests 1000; http 블록에서 설정
    • upstream keepalive 64; proxy_http_version 1.1; proxy_set_header Connection ""; 백엔드 커넥션 풀 설정

FAQ

gzip 압축 레벨은 어느 정도가 적절한가요?
4~6을 권장합니다. 레벨 4는 압축률 72%로 CPU 사용량이 낮아 CPU 자원이 빠듯한 환경에 적합하고, 레벨 6은 압축률 75%로 대역폭을 더 절약해 대역폭이 부족한 환경에 적합합니다. 레벨 9는 CPU 사용량이 두 배가 되지만 압축률 개선은 제한적이므로 권장하지 않습니다.
proxy_cache와 fastcgi_cache 중 무엇을 선택해야 하나요?
백엔드 기술 스택에 따라 선택합니다. Node.js, Python, Go 같은 애플리케이션 서버에는 proxy_cache를 사용하고, PHP-FPM에는 fastcgi_cache를 사용합니다. 두 방식의 설정 논리는 거의 같으며 핵심 매개변수는 캐시 경로, 유효 기간, 캐시 키 설계입니다.
worker_connections는 얼마로 설정하는 것이 적절한가요?
CPU 코어 수와 예상 트래픽에 따라 정합니다. 저트래픽(&lt;1000 QPS)은 1024, 중간 트래픽(1000~5000 QPS)은 2048, 고트래픽(&gt;5000 QPS)은 4096으로 설정합니다. 실제 동시 처리 능력은 worker_processes × worker_connections ÷ 2입니다.
캐시 적중률이 낮을 때는 어떻게 진단하나요?
세 단계로 확인합니다. 1. 요청마다 값이 달라지지 않도록 캐시 키가 합리적인지 확인합니다. 2. proxy_cache_valid 기간이 너무 짧지 않은지 확인합니다. 3. 백엔드 응답 헤더에 캐시를 막는 Cache-Control: no-cache 또는 Set-Cookie가 있는지 확인합니다.
upstream keepalive에 HTTP/1.1 설정이 꼭 필요한 이유는 무엇인가요?
HTTP/1.0은 기본적으로 keepalive를 지원하지 않으므로 백엔드 연결을 재사용하려면 HTTP/1.1을 사용해야 합니다. 또한 proxy_set_header Connection ""로 Connection 헤더를 비워야 합니다. 그렇지 않으면 Nginx가 연결을 종료해 upstream keepalive가 작동하지 않습니다.
비즈니스 유형별 캐시 시간은 어떻게 설정하나요?
이커머스 사이트의 홈과 상품 상세 페이지는 자주 바뀌므로 10~15분, 실시간성이 중요한 API 서비스는 1~5분, 거의 변하지 않는 정적 사이트의 HTML/CSS/JS는 1시간 이상 캐시합니다. 핵심 원칙은 비즈니스 데이터가 자주 바뀔수록 캐시 시간을 짧게 설정하는 것입니다.

3분 읽기 · 게시일: 2026년 4월 11일 · 수정일: 2026년 9월 4일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog