Nginx `default.conf` 리팩토링 관점에서 본 SPA 배포 설정 가이드

okorion·2025년 9월 24일

🛠 Fix & Build

목록 보기
1/12
post-thumbnail

“왜 이렇게 바꿨는지”를 이유 중심으로, 작게-안전하게-검증 가능하게 리팩토링하는 과정을 단계별로 정리합니다.


0) 시작점: 기존 default.conf

문제의식 없이 흔히 쓰는 형태:

server {
  listen 3000;

  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;
  }
}

이 구성의 리스크

  • rootlocation / 안에만 있어 다른 location이 생기면 일관성 깨짐(경로 드리프트).
  • 정적 자원도 무조건 /index.html 폴백 후보 → 자원 404가 숨겨짐(콘솔 Unexpected token <).
  • 캐싱 정책(HTML vs 정적 자원)이 구분돼 있지 않음 → 최신 반영/성능 모두 애매.

1) 리팩토링 원칙

  1. server 레벨 기본값 정리: root, index는 상위로 올려 일관성 확보.
  2. 경로 책임 분리:
    • 정적 자원: “있으면 제공, 없으면 404”
    • 페이지 라우팅: 그 외 URL만 index.html 폴백
  3. 캐시 정책 분리:
    • index.html → 재검증(no-cache)
    • 해시 자원(JS/CSS/폰트/이미지) → 강 캐시(immutable)
  4. 작게 변경 → 즉시 검증: 각 단계마다 nginx -t, curl -I로 확인.

2) 1차 리팩토링: 기본값 끌어올리기

변경 전 → 변경 후 (핵심만)

 server {
   listen 3000;
+  server_name _;
+  root /usr/share/nginx/html;
+  index index.html;

   location / {
-    root /usr/share/nginx/html;
-    index index.html index.htm;
     try_files $uri $uri/ /index.html;
   }
 }

왜?

  • 중복/드리프트 제거: 경로 변경 시 한 곳만 고치면 됨.
  • 새 location 추가해도 기본 root/index가 자동 적용 → “어떤 location은 다른 루트” 같은 오동작 방지.

검증

nginx -t
curl -I http://localhost:3000/

3) 2차 리팩토링: 정적 자원 전용 location 분리

정규식 location을 먼저 두면 자원을 이 블록에서 끝냅니다.

# 정적 자원: 없으면 404, 강 캐시
location ~* \.(?:js|mjs|css|map|json|png|jpe?g|gif|svg|ico|webp|avif|woff2?|ttf|eot)$ {
  try_files $uri =404;  # 절대 SPA로 넘기지 않음
  expires 1y;
  add_header Cache-Control "public, max-age=31536000, immutable";
  access_log off;
}

왜?

  • 자원 404를 404로 보이게 하여 문제를 숨기지 않음(배포 누락, CDN 캐시 순서 이슈 탐지).
  • 해시 파일명 기반 프로덕션에서는 강 캐시가 안전하고 성능에 바로 기여.

검증

# 없는 js 요청 → 404여야 정상
curl -I http://localhost:3000/assets/not-exist.js
# 있는 js → 200, Cache-Control: immutable
curl -I http://localhost:3000/assets/app.abc123.js

4) 3차 리팩토링: 페이지 라우팅 폴백 최소화

나머지 경로만 SPA로 넘깁니다.

# 페이지 경로만 폴백
location / {
  try_files $uri $uri/ /index.html;
}

왜?

  • 정적 자원은 이미 위에서 처리.
  • 여기선 파일/디렉터리로 존재하지 않는 경우에만 index.html 반환 → 클라이언트 라우터가 이어서 처리.

검증

# 실제 파일이 없는 페이지 경로 → 200, text/html
curl -I http://localhost:3000/dashboard

5) 4차 리팩토링: index.html 캐시 정책 분리

# index.html은 항상 재검증(no-cache)
location = /index.html {
  try_files $uri =404;
  add_header Cache-Control "no-cache, must-revalidate" always;
  add_header Pragma "no-cache" always;
  add_header Expires "0" always;
}

왜?

  • 배포 직후 일반 새로고침만 해도 최신 해시 자산을 쓰게 하려면,
    index.html항상 최신이어야 함(캐시 재검증).
  • =404로 파일 누락 시 바로 실패 → 배포/이미지 빌드 오류를 즉시 인지.

검증

curl -I http://localhost:3000/index.html
# Cache-Control: no-cache, must-revalidate 확인

6) 5차 리팩토링: 전송 최적화(안전 옵션)

etag on;

gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;

왜?

  • ETag: 조건부 요청(304)로 재전송 최소화.
  • gzip: 텍스트 리소스 압축 → 전송량 감소.
  • 이미지·동영상은 자체 압축 포맷 → gzip 대상에서 제외(기본 동작 무해).

부작용?

  • 일반 정적 사이트/SPA에선 사실상 없음. (CPU 극단적 제약 환경 아니면 OK)

7) 최종 리팩토링 결과

server {
  listen 3000;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;

  # 1) 정적 자원: 없으면 404, 강 캐시
  location ~* \.(?:js|mjs|css|map|json|png|jpe?g|gif|svg|ico|webp|avif|woff2?|ttf|eot)$ {
    try_files $uri =404;
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
    access_log off;
  }

  # 2) index.html: 항상 재검증(최신 반영)
  location = /index.html {
    try_files $uri =404;
    add_header Cache-Control "no-cache, must-revalidate" always;
    add_header Pragma "no-cache" always;
    add_header Expires "0" always;
  }

  # 3) 페이지 라우팅 폴백
  location / {
    try_files $uri $uri/ /index.html;
  }

  # 4) 전송 최적화
  etag on;
  gzip on;
  gzip_min_length 1024;
  gzip_types text/plain text/css application/javascript application/json image/svg+xml;

  # (옵션) 헬스체크
  location = /healthz { return 200; }
}

8) 리팩토링 체크리스트 (PR 설명 템플릿)

  • root, indexserver 레벨로 이동하여 일관성 확보
  • 정적 자원 전용 location 추가: try_files $uri =404 + immutable 캐시
  • index.html no-cache로 최신 반영 보장
  • / location은 페이지 경로만 폴백
  • etag, gzip 활성화
  • nginx -t 통과 및 curl 테스트 케이스 첨부
  • 없는 자원 404
  • 페이지 경로 200(text/html)
  • index.html no-cache 헤더
  • 정적 자원 immutable 헤더

9) 배포·운영 팁 (실패 줄이는 습관)

  • 배포 순서: 정적 자산(CDN) → index.html (혹은 원자적 릴리스)
  • 모니터링: 자원 404 비율 알람, HTML/자산 헤더 스캔
  • CDN: 오리진 헤더 존중, HTML만 짧게/재검증, 자산은 길게/immutable
  • 서비스 워커 사용 시: SW가 캐시를 가로채므로 별도 무효화/버전 전략 필수

10) 흔한 반패턴 → 대체 코드

반패턴

location / {
  try_files $uri /index.html;  # ❌ 정적 자원 404도 폴백됨
}

대체

location ~* \.(?:js|css|...)\$ { try_files $uri =404; }
location / { try_files $uri $uri/ /index.html; }
profile
okorion's Tech Study Blog.

0개의 댓글