새 프로젝트 배포를 맡았습니다. 개발팀이 빌드한 React 앱 정적 파일을 서버에 올려야 하고, 추후 백엔드 API 요청도 프록시해야 합니다. EC2 인스턴스에 SSH 접속하니 웹 서버가 아무것도 설치되어 있지 않습니다. 팀장은 "Nginx 써"라고 했지만, nginx.conf가 왜 저렇게 생겼는지, restart와 reload가 다른 건지, 설정 바꾸면 어디서 확인해야 하는지 — 막막합니다.
이 모듈은 그 막막함을 없애는 것이 목표입니다. Nginx를 직접 설치하고, 설정 파일 구조를 이해하고, 정적 파일을 실제로 서빙하는 것까지 해봅니다.
- 1Ubuntu와 CentOS 각각에서 Nginx를 설치할 수 있다
- 2nginx.conf의 worker_processes, events, http, server, location 블록 구조를 설명할 수 있다
- 3정적 파일 서빙 설정을 작성하고 nginx -t로 검증할 수 있다
- 4systemctl reload와 restart의 차이를 이해하고 상황에 따라 선택할 수 있다
- 5액세스 로그와 에러 로그 위치를 알고 tail -f로 실시간 확인할 수 있다
sudo apt-get update && sudo apt-get install -y nginxsudo dnf install -y nginxnginx -vsudo systemctl enable --now nginxcurl -s http://localhost | head -5Nginx 서비스 관리
systemctl로 Nginx 제어하기
설정을 변경하고 systemctl restart nginx를 실행했더니 서비스가 0.3초 끊겼다는 모니터링 알림이 왔습니다. reload를 써야 한다는 것을 나중에 알았습니다. 반대로 서버 재부팅 후 Nginx가 안 뜬다는 제보를 받았는데, enable 없이 start만 했기 때문이었습니다. start와 enable의 차이, restart와 reload의 차이를 모르면 이런 실수가 반복됩니다.
Nginx를 설치했다고 바로 동작하지 않습니다. Linux 서비스 관리자인 systemd를 통해 시작, 중지, 상태 확인을 해야 합니다. 그리고 설정을 바꿀 때마다 restart와 reload 중 어떤 것을 써야 할지 판단할 수 있어야 합니다.
# 상태 확인 (active/inactive/failed)
sudo systemctl status nginx
# 시작 / 중지 / 재시작
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx # 완전 재시작 — 순간적으로 연결 끊김
# 설정 재로드 (처리 중인 요청 유지, 권장)
sudo systemctl reload nginx
# 또는 Nginx 고유 시그널로도 가능
sudo nginx -s reload
# 부팅 시 자동 시작 등록/해제
sudo systemctl enable nginx
sudo systemctl disable nginx
restart vs reload — 언제 무엇을 쓰나:
| 상황 | 권장 명령 | 이유 |
|---|---|---|
| 일반 설정 변경 (server_name, location 등) | reload | 기존 연결 유지, 무중단 |
| Nginx 바이너리 업그레이드 후 | restart | 바이너리 교체는 reload 불가 |
| 서비스 기동이 안 될 때 초기화 | restart | 완전 재시작으로 상태 초기화 |
| SSL 인증서 갱신 반영 | reload | 연결 끊김 없이 새 인증서 적용 |
# 설정 검증 → 이상 없으면 reload (운영 서버 표준 절차)
sudo nginx -t && sudo systemctl reload nginx
nginx.conf 구조 이해
설정 파일 계층 구조
Nginx 설정이 뜻대로 적용되지 않을 때 가장 먼저 의심해야 할 것은 "올바른 블록에 설정을 넣었는가"입니다. server 블록에 있어야 할 설정이 events 블록에 들어가거나, location 설정이 http 블록 바깥에 위치하면 Nginx는 이를 인식하지 못합니다. 블록 계층을 이해하면 설정 변경이 왜 반영되지 않는지, 어느 블록을 수정해야 하는지를 파일을 열자마자 판단할 수 있습니다.

nginx.conf를 처음 열면 블록 안에 블록이 중첩되어 있어 어디를 수정해야 할지 막막합니다. server 블록을 건드려야 하는데 http 블록 안에서 찾지 못하거나, location 설정을 엉뚱한 레벨에 추가해 설정이 적용되지 않는 경우가 잦습니다. 어느 블록에서 어떤 설정을 하는지 구조를 알면 원하는 위치를 바로 찾을 수 있습니다.
Nginx 설정이 처음 보면 낯선 것은 블록이 중첩되는 구조 때문입니다. 이 구조는 의도적인 설계입니다 — 전역 설정, 연결 설정, HTTP 처리, 가상 호스트, URL 패턴이 각자의 레이어에 있어 관심사가 분리됩니다.
기본 설정 파일 위치:
- Ubuntu/Debian:
/etc/nginx/nginx.conf - CentOS/RHEL:
/etc/nginx/nginx.conf - 사이트별 설정:
/etc/nginx/conf.d/*.conf(권장) - Ubuntu 전통 방식:
/etc/nginx/sites-available/+sites-enabled/
# /etc/nginx/nginx.conf (기본 구조)
# ── 전역 블록 ──────────────────────────────────────
# Nginx 프로세스 전체에 적용되는 설정
user nginx;
worker_processes auto; # CPU 코어 수만큼 worker 생성 (auto 권장)
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# ── events 블록 ────────────────────────────────────
# 연결(connection) 처리 방식 설정
events {
worker_connections 1024; # worker당 최대 동시 연결 수
# multi_accept on; # 한 번에 여러 연결 수락 (고트래픽 시 활성화)
}
# ── http 블록 ──────────────────────────────────────
# HTTP 처리 전반 설정 (server 블록의 부모)
http {
include /etc/nginx/mime.types; # 파일 확장자 → Content-Type 매핑
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log main;
sendfile on; # 커널 레벨 파일 전송 (정적 파일 성능 향상)
keepalive_timeout 65; # 클라이언트 연결 유지 시간(초)
# ── server 블록 (가상 호스트) ──────────────────
# 한 서버에서 여러 도메인/포트를 서비스할 때 블록을 추가
server {
listen 80 default_server;
server_name _; # _ 는 모든 Host 헤더에 매칭 (catch-all)
# ── location 블록 (URL 패턴 매칭) ──────────
location / {
root /usr/share/nginx/html;
index index.html;
}
}
# 추가 설정 파일 포함 (conf.d/*.conf)
include /etc/nginx/conf.d/*.conf;
}
블록별 역할 요약:
| 블록 | 위치 | 주요 설정 |
|---|---|---|
| 전역 | 최상단 | worker_processes, error_log, pid |
| events | 전역 하위 | worker_connections (동시 연결 한도) |
| http | 전역 하위 | mime.types, 로그 형식, keepalive |
| server | http 하위 | 포트, 도메인, root 경로 |
| location | server 하위 | URL 패턴별 처리 방식 |

location 블록 — URL 패턴 매칭
/api/ 경로만 Tomcat으로 보내도록 설정했는데 정적 파일도 Tomcat으로 넘어가고 있었습니다. location 매칭 규칙의 우선순위를 몰라서 / 블록이 모든 요청을 먹고 있었기 때문입니다. 설정을 바꿔도 의도대로 동작하지 않는 상황이 되면, 어떤 블록이 실제로 요청을 처리하는지 파악하는 것이 디버깅의 시작입니다. 매칭 규칙을 정확히 알아야 원하는 라우팅을 만들 수 있습니다.
location 블록은 URL 패턴에 따라 요청을 다르게 처리합니다. 실무에서 가장 자주 수정하는 부분입니다. 어떤 요청이 어느 location에 매칭되는지 이해하지 못하면, 설정을 바꿔도 의도대로 동작하지 않습니다.
server {
listen 80;
server_name example.com;
root /var/www/html;
# ① 일반 prefix 매칭 (가장 길게 일치하는 것 선택)
location / {
try_files $uri $uri/ /index.html; # SPA 라우팅에 필수
}
# ② 더 구체적인 prefix — /api/로 시작하는 요청
location /api/ {
proxy_pass http://localhost:3000; # 백엔드 서버로 프록시
}
# ③ 정확히 일치 (= 기호)
location = /favicon.ico {
log_not_found off; # 404 로그 억제
access_log off;
}
# ④ 정규식 매칭 (~: 대소문자 구분, ~*: 구분 안 함)
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d; # 브라우저 캐시 30일
add_header Cache-Control "public, immutable";
}
}
매칭 우선순위 (헷갈리는 부분):
=정확히 일치 (최우선)^~prefix 매칭 (정규식보다 우선)~,~*정규식 (선언 순서대로)- 일반 prefix 매칭 (가장 길게 일치하는 것)
정적 파일 서빙
/etc/nginx/conf.d/myapp.conf 파일을 아래 내용으로 작성합니다. nginx.conf 에 include /etc/nginx/conf.d/*.conf; 가 있어서 이 파일이 자동으로 포함됩니다. 사이트별로 설정을 분리하면 관리가 훨씬 쉽습니다.
server {
listen 80 default_server;
server_name _;
root /var/www/myapp;
index index.html;
# SPA (React/Vue 등) — 모든 경로를 index.html로 fallback
location / {
try_files $uri $uri/ /index.html;
}
# 정적 자산 캐시
location ~* \.(js|css|png|jpg|gif|ico|woff2)$ {
expires 7d;
add_header Cache-Control "public";
}
# 로그 위치
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
}
sudo vim /etc/nginx/conf.d/myapp.conf/etc/nginx/conf.d/myapp.conf파일이 생성됐는가 (ls /etc/nginx/conf.d/)nginx -t실행 시 오류 없이syntax is ok가 나왔는가server_name,root,location /블록이 모두 포함됐는가
Nginx worker 프로세스는 nginx 유저로 실행됩니다. root 디렉터리 파일의 소유자가 nginx여야 읽기 권한 문제가 발생하지 않습니다. Ubuntu는 www-data 유저를 쓰기도 합니다(cat /etc/nginx/nginx.conf | grep user로 확인).
sudo mkdir -p /var/www/myapp && echo '<h1>Hello Nginx</h1>' | sudo tee /var/www/myapp/index.html && sudo chown -R nginx:nginx /var/www/myapp/var/www/myapp/index.html파일이 존재하고 HTML 내용이 들어있는가ls -la /var/www/myapp/에서 소유자가nginx(또는www-data)로 나왔는가cat /etc/nginx/nginx.conf | grep user출력과 소유자가 일치하는가
nginx -t가 syntax is ok / test is successful을 반환하면 설정에 문제가 없습니다. 이상이 없을 때만 reload가 실행됩니다(&& 덕분에). 실제 운영 서버에서는 이 두 명령을 항상 세트로 씁니다.
sudo nginx -t && sudo systemctl reload nginx- nginx -t 출력에 'syntax is ok'와 'test is successful' 두 줄이 모두 나왔는가
- curl http://localhost 응답에 '<h1>Hello Nginx</h1>'가 포함됐는가
- curl -I http://localhost 로 HTTP/1.1 200 OK 헤더가 반환됐는가
- curl http://localhost/static/app.js 같은 없는 경로 요청 시 index.html로 fallback됐는가 (SPA 확인)
- sudo systemctl status nginx — active (running) 상태인가
로그 확인
액세스 로그와 에러 로그
Nginx가 이상하게 동작할 때 가장 먼저 열어야 하는 것이 로그입니다. 설정 문제인지, 요청 자체에 문제가 있는지를 로그로 구분합니다. 로그 경로를 모르면 장애 대응이 한참 늦어집니다.
기본 로그 위치:
| 파일 | 내용 |
|---|---|
/var/log/nginx/access.log | 모든 HTTP 요청 기록 (IP, URI, 상태코드, 응답크기) |
/var/log/nginx/error.log | Nginx 에러 및 경고 (설정 오류, 권한 문제, upstream 실패 등) |
# 실시간 액세스 로그 모니터링
sudo tail -f /var/log/nginx/access.log
# 출력 예시:
# 192.168.1.10 - - [30/May/2026:09:23:14 +0000] "GET / HTTP/1.1" 200 345 "-" "curl/7.81.0"
# 192.168.1.10 - - [30/May/2026:09:23:20 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "-" "Mozilla/5.0"
# 에러 로그 확인 (warn 이상)
sudo tail -f /var/log/nginx/error.log
# 특정 상태코드만 필터링 (500 에러 찾기)
sudo grep ' 500 ' /var/log/nginx/access.log | tail -20
# 특정 IP의 요청만 확인
sudo grep '192.168.1.100' /var/log/nginx/access.log | tail -10
# 최근 1분간 요청 수 집계
sudo awk '{print $4}' /var/log/nginx/access.log | grep "$(date +'%d/%b/%Y:%H:%M')" | wc -l
# 404 많이 나는 URI 찾기
sudo awk '$9==404{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
에러 로그 레벨 (error_log 지시어에서 설정):
debug— 매우 상세 (개발 환경만, 운영 금지)info— 정보성warn— 경고 (기본값)error— 에러crit— 크리티컬
백그라운드로 로그를 팔로우하면서 요청을 보내면 로그가 실시간으로 찍히는 것을 볼 수 있습니다. 200(성공), 404(없는 경로), try_files fallback 동작을 한 번에 확인합니다.
sudo tail -f /var/log/nginx/access.log &
sleep 1
curl -s http://localhost/
curl -s http://localhost/nonexistent
curl -s http://localhost/api/test
wait- curl http://localhost/ → access.log에 200 응답 기록이 찍혔는가
- curl http://localhost/nonexistent → index.html fallback으로 200이 나왔는가 (try_files 동작)
- 에러 로그에 'permission denied' 같은 에러가 없는가
- 로그 형식에서 IP, 날짜, 메서드, URI, 상태코드, 바이트 수 구분이 가능한가
트러블슈팅
원인: 80번 포트를 이미 다른 프로세스(Apache, 기존 Nginx, Node.js 등)가 점유하고 있습니다. 서버에 Apache가 설치되어 있었거나, Nginx를 중복 실행했을 때 발생합니다.
# 80번 포트를 점유한 프로세스 확인
sudo ss -tlnp | grep ':80'
# 또는
sudo lsof -i:80
# 출력 예시:
# LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=1234,fd=4))
# Apache가 충돌하는 경우
sudo systemctl stop apache2
sudo systemctl disable apache2 # 부팅 시 자동 시작도 해제
# 다른 Nginx 인스턴스인 경우
sudo nginx -s quit
sudo systemctl start nginx
# 임시 프로세스인 경우 (PID 확인 후)
sudo kill -TERM <PID>
원인: Nginx가 root 경로의 파일을 읽을 권한이 없습니다. 세 가지 경우가 가장 흔합니다: ① 파일/디렉터리 소유자 문제, ② SELinux 컨텍스트 문제(CentOS/RHEL), ③ 디렉터리에 execute 권한 없음.
# 에러 로그에서 원인 확인 (가장 먼저)
sudo tail -20 /var/log/nginx/error.log
# 예시: [error] 1234#0: *1 open() "/var/www/myapp/index.html" failed
# (13: Permission denied), ...
# ① 파일 소유자 및 권한 확인
ls -la /var/www/myapp/
# Nginx 유저 확인
grep '^user' /etc/nginx/nginx.conf
# 소유권 수정 (Ubuntu는 www-data, CentOS는 nginx)
sudo chown -R nginx:nginx /var/www/myapp
sudo chmod -R 755 /var/www/myapp
# ② SELinux 문제 (CentOS/RHEL에서 자주 발생)
# 임시로 SELinux 컨텍스트 확인
ls -lZ /var/www/myapp/
# 웹 서빙 컨텍스트로 변경
sudo chcon -Rt httpd_sys_content_t /var/www/myapp/
# ③ 부모 디렉터리 execute 권한 확인
# /var/www 또는 /var 에 x 권한이 없으면 403
sudo chmod +x /var/www
원인: 설정 파일에 오타가 있습니다. Nginx는 알 수 없는 지시어를 만나면 즉시 에러를 냅니다. 에러 메시지에 파일 경로와 줄 번호가 명시되어 있어 찾기 쉽습니다.
# 에러 메시지 예시:
# nginx: [emerg] unknown directive "server_namee" in /etc/nginx/conf.d/myapp.conf:3
# nginx: configuration file /etc/nginx/nginx.conf test failed
# 해당 파일의 해당 줄 확인
sed -n '1,10p' /etc/nginx/conf.d/myapp.conf
# 수정 후 재검증
sudo nginx -t
# 설정 파일 전체 덤프 (include 포함 확인)
sudo nginx -T 2>/dev/null | head -50
실제 업무에서 이 지식이 쓰이는 상황:
팀에 새로 합류한 주니어 개발자가 가장 먼저 맞닥뜨리는 Nginx 상황은 크게 세 가지입니다.
1. 프론트엔드 배포: React 빌드 결과물(npm run build 후 /dist)을 서버에 올릴 때, Nginx root 경로와 try_files 설정을 잡아주는 것이 역할입니다. SPA 라우팅을 위해 try_files 없이 배포하면 페이지 새로고침 시 404가 납니다.
2. 설정 변경 요청 처리:
# 실무에서 반복되는 패턴:
# 1. 설정 수정
sudo vim /etc/nginx/conf.d/myapp.conf
# 2. 검증
sudo nginx -t
# 3. 이상 없으면 무중단 재로드
sudo systemctl reload nginx
# 4. 로그로 확인
sudo tail -f /var/log/nginx/access.log
3. 장애 대응 첫 30초:
# Nginx 기동 안 됨 → 원인 파악 루틴
sudo systemctl status nginx # 상태와 최근 로그
sudo journalctl -u nginx -n 30 --no-pager # 상세 에러
sudo nginx -t # 설정 파일 검증
sudo ss -tlnp | grep ':80' # 포트 충돌 확인
sudo tail -20 /var/log/nginx/error.log # 에러 로그 직접 확인
Nginx 설정을 직접 바꿀 줄 알고, nginx -t로 검증하고, reload로 무중단 반영하는 것만 해도 팀의 배포 속도가 눈에 띄게 빨라집니다. 다음 모듈에서는 이 Nginx 앞에 도메인을 연결하고 HTTPS를 적용하는 방법을 다룹니다.