
“왜 이렇게 바꿨는지”를 이유 중심으로, 작게-안전하게-검증 가능하게 리팩토링하는 과정을 단계별로 정리합니다.
default.conf문제의식 없이 흔히 쓰는 형태:
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
root가 location / 안에만 있어 다른 location이 생기면 일관성 깨짐(경로 드리프트)./index.html 폴백 후보 → 자원 404가 숨겨짐(콘솔 Unexpected token <).root, index는 상위로 올려 일관성 확보.index.html 폴백index.html → 재검증(no-cache)nginx -t, curl -I로 확인. 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;
}
}
root/index가 자동 적용 → “어떤 location은 다른 루트” 같은 오동작 방지.nginx -t
curl -I http://localhost:3000/
정규식 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;
}
# 없는 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
나머지 경로만 SPA로 넘깁니다.
# 페이지 경로만 폴백
location / {
try_files $uri $uri/ /index.html;
}
index.html 반환 → 클라이언트 라우터가 이어서 처리.# 실제 파일이 없는 페이지 경로 → 200, text/html
curl -I http://localhost:3000/dashboard
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 확인
etag on;
gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
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; }
}
root, index를 server 레벨로 이동하여 일관성 확보try_files $uri =404 + immutable 캐시index.html no-cache로 최신 반영 보장/ location은 페이지 경로만 폴백etag, gzip 활성화nginx -t 통과 및 curl 테스트 케이스 첨부index.html (혹은 원자적 릴리스)반패턴
location / {
try_files $uri /index.html; # ❌ 정적 자원 404도 폴백됨
}
대체
location ~* \.(?:js|css|...)\$ { try_files $uri =404; }
location / { try_files $uri $uri/ /index.html; }