infra
Platform

모듈 맵

[Infra Ops] Nginx 설치, 기본 설정, 정적 파일 서빙 실무

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 12 / 52

[Infra Ops] Nginx 설치, 기본 설정, 정적 파일 서빙 실무

Nginx 설치부터 nginx.conf 구조 이해, 정적 파일 서빙, 서비스 관리, 로그 확인까지 — 웹 서버를 직접 띄우고 운영하는 실무 흐름을 익힙니다

🚨INCIDENT ALERT
HIGH

새 프로젝트 배포를 맡았습니다. 개발팀이 빌드한 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로 실시간 확인할 수 있다
실습 환경 준비
Ubuntu — Nginx 설치
sudo apt-get update && sudo apt-get install -y nginx
CentOS/RHEL — Nginx 설치
sudo dnf install -y nginx
설치 확인 및 버전 출력
nginx -v
Nginx 서비스 시작 및 부팅 자동 시작 등록
sudo systemctl enable --now nginx
브라우저 없이 로컬 동작 확인
curl -s http://localhost | head -5

Nginx 서비스 관리

💡개념

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 설정 파일 블록 구조

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/
Nginx
# /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
serverhttp 하위포트, 도메인, root 경로
locationserver 하위URL 패턴별 처리 방식

Nginx 요청 처리 흐름 — location 매칭 순서

💡개념

location 블록 — URL 패턴 매칭

/api/ 경로만 Tomcat으로 보내도록 설정했는데 정적 파일도 Tomcat으로 넘어가고 있었습니다. location 매칭 규칙의 우선순위를 몰라서 / 블록이 모든 요청을 먹고 있었기 때문입니다. 설정을 바꿔도 의도대로 동작하지 않는 상황이 되면, 어떤 블록이 실제로 요청을 처리하는지 파악하는 것이 디버깅의 시작입니다. 매칭 규칙을 정확히 알아야 원하는 라우팅을 만들 수 있습니다.

location 블록은 URL 패턴에 따라 요청을 다르게 처리합니다. 실무에서 가장 자주 수정하는 부분입니다. 어떤 요청이 어느 location에 매칭되는지 이해하지 못하면, 설정을 바꿔도 의도대로 동작하지 않습니다.

Nginx
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";
    }
}

매칭 우선순위 (헷갈리는 부분):

  1. = 정확히 일치 (최우선)
  2. ^~ prefix 매칭 (정규식보다 우선)
  3. ~, ~* 정규식 (선언 순서대로)
  4. 일반 prefix 매칭 (가장 길게 일치하는 것)

정적 파일 서빙

1정적 파일 서빙 설정 작성

/etc/nginx/conf.d/myapp.conf 파일을 아래 내용으로 작성합니다. nginx.conf 에 include /etc/nginx/conf.d/*.conf; 가 있어서 이 파일이 자동으로 포함됩니다. 사이트별로 설정을 분리하면 관리가 훨씬 쉽습니다.

Nginx
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 / 블록이 모두 포함됐는가
2서빙할 디렉터리와 테스트 파일 생성

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 출력과 소유자가 일치하는가
3설정 검증 후 재로드

nginx -tsyntax is ok / test is successful을 반환하면 설정에 문제가 없습니다. 이상이 없을 때만 reload가 실행됩니다(&& 덕분에). 실제 운영 서버에서는 이 두 명령을 항상 세트로 씁니다.

sudo nginx -t && sudo systemctl reload nginx
🔍실행 후 확인할 것
  • nginx -t 출력에 'syntax is ok'와 'test is successful' 두 줄이 모두 나왔는가
  • curl http://localhost 응답에 '&lt;h1&gt;Hello Nginx&lt;/h1&gt;'가 포함됐는가
  • 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.logNginx 에러 및 경고 (설정 오류, 권한 문제, 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 — 크리티컬
4로그 실시간 확인 및 요청 테스트

백그라운드로 로그를 팔로우하면서 요청을 보내면 로그가 실시간으로 찍히는 것을 볼 수 있습니다. 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를 적용하는 방법을 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

nginx -t 명령어의 목적으로 올바른 것은?

Q2

nginx.conf에서 server 블록 안에 listen 80; 대신 listen 80 default_server; 를 쓰는 이유는?

Q3

systemctl reload nginx와 systemctl restart nginx의 차이로 올바른 것은?

Q4

location 블록에서 / 와 /api/ 두 개가 있을 때, /api/users 요청은 어디서 처리되는가?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 60
[Infra Ops] Let's Encrypt 인증서 발급과 Nginx SSL 설정
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점