Nginx 설치와 설정 - 기초부터 실전까지

“웹 서버를 설치하려는데, Apache보다 Nginx가 더 빠르다던데?” “Nginx 설정 파일이 복잡해 보이는데, 어디서부터 시작해야 할까?”

웹 개발을 하다 보면 서버를 직접 설정해야 하는 순간이 옵니다. 저도 처음에는 Nginx의 설정 파일 구조가 낯설어서 공식 문서를 여러 번 읽었습니다. 하지만 기본 구조를 이해하고 나니, 정적 파일 서빙부터 리버스 프록시까지 자유롭게 설정할 수 있게 되었습니다.

이 가이드에서는 Nginx 공식 문서를 기반으로 설치부터 기본 설정까지 단계별로 설명합니다.

목차

Nginx란 무엇인가?

Nginx (발음: “엔진 엑스”)는 Igor Sysoev가 개발한 고성능 웹 서버이자 리버스 프록시 서버입니다.

왜 Nginx를 사용할까요?

Apache vs Nginx 처리 방식
┌─────────────────────────────────────────┐
│ Apache (Process-based)                  │
│                                         │
│  요청 1 → 프로세스 1                    │
│  요청 2 → 프로세스 2                    │
│  요청 3 → 프로세스 3                    │
│  ...                                    │
│  많은 메모리 사용 💾                    │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│ Nginx (Event-driven)                    │
│                                         │
│  요청 1 ┐                               │
│  요청 2 ├─→ 하나의 워커 프로세스        │
│  요청 3 ┘                               │
│  ...                                    │
│  적은 메모리, 높은 동시성 🚀            │
└─────────────────────────────────────────┘

Nginx의 주요 특징:

  • 비동기 이벤트 기반 아키텍처: 적은 리소스로 높은 동시 접속 처리
  • 리버스 프록시: 백엔드 서버를 숨기고 로드 밸런싱
  • 정적 파일 서빙 최적화: HTML, CSS, JS, 이미지 등을 빠르게 제공
  • 낮은 메모리 사용량: Apache 대비 메모리 효율적

공식 문서 출처: nginx.org

리버스 프록시란 무엇인가?

“리버스 프록시(Reverse Proxy)”라는 용어를 처음 들으면 혼란스러울 수 있습니다. “프록시는 알겠는데, 리버스가 뭐지?” 저도 처음에는 Forward Proxy와 Reverse Proxy의 차이를 이해하는 데 시간이 걸렸습니다.

Forward Proxy vs Reverse Proxy

먼저 일반적인 프록시(Forward Proxy)와 비교해보겠습니다:

Forward Proxy (클라이언트 측 프록시)
┌─────────┐      ┌─────────┐      ┌─────────┐
│ 클라이언트 │ ───→ │  Proxy  │ ───→ │  서버   │
│  (나)    │      │         │      │(Google) │
└─────────┘      └─────────┘      └─────────┘
   "난 누구?"        "내가 대신      "이 요청은
                    요청할게"      Proxy에서 왔네"

목적: 클라이언트 보호, IP 숨김, 지역 제한 우회

Reverse Proxy (서버 측 프록시)
┌─────────┐      ┌─────────┐      ┌─────────┐
│ 클라이언트 │ ───→ │  Nginx  │ ───→ │백엔드 서버│
│  (사용자) │      │(Reverse │      │(Node.js)│
│         │      │ Proxy)  │      │         │
└─────────┘      └─────────┘      └─────────┘
   "서버는          "내가           "Nginx가
   하나뿐"         여러 서버로       나한테
                   분산할게"        요청 보냄"

목적: 서버 보호, 로드 밸런싱, 캐싱, SSL 종료

핵심 차이:

  • Forward Proxy: 클라이언트를 숨깁니다 (클라이언트가 서버를 선택)
  • Reverse Proxy: 서버를 숨깁니다 (서버가 클라이언트를 선택)

리버스 프록시가 필요한 이유

1. 보안 강화

백엔드 서버를 직접 노출하지 않습니다:

❌ 리버스 프록시 없이
클라이언트 → http://192.168.1.10:3000 (백엔드 직접 노출)
              ↑ 서버 IP와 포트가 그대로 노출됨

✅ 리버스 프록시 사용
클라이언트 → https://api.example.com (Nginx)
              ↓
              http://192.168.1.10:3000 (내부 네트워크, 숨김)

2. 로드 밸런싱

여러 백엔드 서버에 부하를 분산합니다:

                    ┌─→ 서버 1 (localhost:3001)
클라이언트 → Nginx ─┼─→ 서버 2 (localhost:3002)
                    └─→ 서버 3 (localhost:3003)

# 한 서버가 다운되어도 다른 서버가 처리

3. SSL/TLS 종료 (SSL Termination)

HTTPS 처리를 Nginx에서 담당:

클라이언트 ←─HTTPS─→ Nginx ←─HTTP─→ 백엔드
           (암호화)         (평문, 내부망)

장점:
- 백엔드 서버는 SSL 처리 불필요
- 인증서를 한 곳에서만 관리
- 성능 향상 (암호화/복호화 오버헤드 감소)

4. 정적/동적 콘텐츠 분리

효율적인 콘텐츠 제공:

server {
    # 정적 파일 (이미지, CSS, JS)
    location /static/ {
        root /var/www/static;
        expires 30d;  # 브라우저 캐싱
    }

    # 동적 API 요청
    location /api/ {
        proxy_pass http://backend:3000;
    }
}

5. 캐싱

자주 요청되는 콘텐츠를 캐시:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;

server {
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 10m;  # 10분간 캐시
        proxy_pass http://backend;
    }
}

효과:

  • 백엔드 서버 부하 감소
  • 응답 속도 향상
  • 대역폭 절약

실전 시나리오: 왜 Nginx를 앞에 두는가?

현대 웹 아키텍처의 전형적인 구성:

인터넷
  ↓
방화벽
  ↓
┌─────────────────────────────────┐
│    DMZ (외부 접근 가능 영역)     │
│                                 │
│  ┌──────────────────────┐       │
│  │   Nginx (포트 80/443) │       │
│  │   - 정적 파일 서빙     │       │
│  │   - SSL 종료          │       │
│  │   - Rate limiting     │       │
│  └──────────┬───────────┘       │
│             │                   │
└─────────────┼───────────────────┘
              │
    ┌─────────┼─────────┐
    │   내부 네트워크    │
    │                   │
    │  백엔드 서버들     │
    │  - Node.js (3000) │
    │  - Python (8000)  │
    │  - Java (8080)    │
    │                   │
    │  데이터베이스      │
    │  - PostgreSQL     │
    │  - Redis          │
    └───────────────────┘

이점:

  1. 단일 진입점: 하나의 도메인으로 여러 서비스 제공
  2. 보안 계층: 외부에서 백엔드 직접 접근 차단
  3. 유연성: 백엔드 서버 변경 시 클라이언트는 알 필요 없음
  4. 성능 최적화: Nginx의 빠른 정적 파일 서빙

실제 예제: 간단한 리버스 프록시

# Node.js 앱을 Nginx 뒤에 숨기기
server {
    listen 80;
    server_name myapp.com;

    # 모든 요청을 Node.js로 전달
    location / {
        proxy_pass http://localhost:3000;

        # 원본 클라이언트 정보 전달
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

작동 방식:

  1. 사용자가 http://myapp.com 접속
  2. Nginx가 80번 포트에서 요청 받음
  3. Nginx가 localhost:3000(Node.js)로 요청 전달
  4. Node.js 응답 → Nginx → 사용자

사용자는 Node.js가 3000번 포트에서 실행되는지 전혀 모릅니다!

요약

리버스 프록시는 “서버 앞의 문지기”입니다:

  • 외부 요청을 받아서 내부 서버로 전달
  • 보안, 성능, 관리 편의성 제공
  • 클라이언트는 실제 백엔드 구조를 알 수 없음

Nginx가 리버스 프록시로 인기있는 이유:

  • ✅ 높은 성능과 낮은 리소스 사용
  • ✅ 간단한 설정
  • ✅ 로드 밸런싱, 캐싱, SSL 기능 내장
  • ✅ 무료 오픈소스

이제 Nginx 설치부터 시작해보겠습니다!

설치하기 전에

필요한 것

  • Linux 서버 (Ubuntu, Debian, CentOS, RHEL 등)
  • 루트 권한 또는 sudo 권한
  • 인터넷 연결

버전 선택: Stable vs Mainline

Nginx는 두 가지 버전을 제공합니다:

  • Stable (안정 버전): 프로덕션 환경 권장
  • Mainline (최신 버전): 최신 기능과 버그 수정 포함

권장: 프로덕션에서는 Stable 버전을 사용하세요.

Step 1: Nginx 설치 (Linux)

공식 저장소를 통한 설치 방법을 운영체제별로 설명합니다.

Ubuntu 설치

1-1. 사전 요구사항 설치

sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

1-2. GPG 서명 키 추가

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

1-3. GPG 키 지문 검증 (보안)

gpg --dry-run --quiet --no-keyring --import --import-options import-show \
    /usr/share/keyrings/nginx-archive-keyring.gpg

출력에서 다음 지문을 확인하세요:

573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62

1-4. Nginx 저장소 추가 (Stable)

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

1-5. 저장소 우선순위 설정

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

1-6. Nginx 설치

sudo apt update
sudo apt install nginx

1-7. 설치 확인

nginx -v
# 출력: nginx version: nginx/1.24.0

Debian 설치

Debian은 Ubuntu와 거의 동일하지만, 사전 요구사항에서 차이가 있습니다:

# 1. 사전 요구사항
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring

# 2-5단계는 Ubuntu와 동일, 6단계에서 URL만 변경:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian $(lsb_release -cs) nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

CentOS / RHEL 설치

1-1. yum-utils 설치

sudo yum install yum-utils

1-2. Nginx 저장소 추가

/etc/yum.repos.d/nginx.repo 파일 생성:

sudo tee /etc/yum.repos.d/nginx.repo > /dev/null <<EOF
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

1-3. Nginx 설치

sudo yum install nginx

1-4. (선택) Mainline 버전 사용하려면

sudo yum-config-manager --enable nginx-mainline
sudo yum install nginx

공식 문서 출처: Installing nginx - Linux packages

Step 2: Nginx 시작과 제어

서비스 시작

# systemd 사용 (대부분의 최신 Linux)
sudo systemctl start nginx

# 부팅 시 자동 시작 활성화
sudo systemctl enable nginx

# 상태 확인
sudo systemctl status nginx

Nginx 제어 명령어

Nginx는 시그널을 통해 제어할 수 있습니다:

# 설정 파일 테스트 (변경 전 반드시 확인!)
sudo nginx -t

# 우아한 종료 (현재 요청 완료 후 종료)
sudo nginx -s quit

# 빠른 종료
sudo nginx -s stop

# 설정 재로드 (무중단 재시작)
sudo nginx -s reload

# 로그 파일 재오픈
sudo nginx -s reopen

설정 변경 후 재로드 워크플로우

# 1. 설정 파일 수정
sudo vim /etc/nginx/nginx.conf

# 2. 문법 검사 (필수!)
sudo nginx -t

# 3. 문제 없으면 재로드
sudo nginx -s reload

⚠️ 주의: nginx -s reload 전에 반드시 nginx -t로 설정 파일 검증을 하세요. 잘못된 설정으로 재로드하면 서비스가 중단될 수 있습니다!

공식 문서 출처: Beginner’s Guide - Starting, Stopping, and Reloading Configuration

Step 3: 설정 파일 구조 이해하기

설정 파일 위치

# 메인 설정 파일
/etc/nginx/nginx.conf

# 사이트별 설정 (Ubuntu/Debian)
/etc/nginx/sites-available/
/etc/nginx/sites-enabled/

# 사이트별 설정 (CentOS/RHEL)
/etc/nginx/conf.d/

# 로그 파일
/var/log/nginx/access.log
/var/log/nginx/error.log

nginx.conf 기본 구조

# 최상위: main 컨텍스트
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# events 블록: 연결 처리 설정
events {
    worker_connections 1024;
}

# http 블록: HTTP 서버 설정
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;

    # server 블록: 가상 호스트 설정
    server {
        listen 80;
        server_name example.com;

        # location 블록: URI별 설정
        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }
}

컨텍스트 계층 구조

main 컨텍스트
├── events { }
└── http {
    ├── server {
    │   ├── location / { }
    │   ├── location /images/ { }
    │   └── location /api/ { }
    │   }
    └── server {
        └── location / { }
        }
    }

핵심 규칙:

  • 단순 지시문: 이름과 파라미터 후 세미콜론(;)
  • 블록 지시문: 중괄호({})로 감싼 구조
  • 상속: 하위 컨텍스트는 상위 설정을 상속

Step 4: 기본 설정 - 정적 파일 서빙

정적 HTML 사이트를 서빙하는 가장 간단한 설정입니다.

4-1. 웹 루트 디렉토리 생성

# 디렉토리 생성
sudo mkdir -p /var/www/mysite

# 테스트 HTML 파일 생성
sudo tee /var/www/mysite/index.html > /dev/null <<EOF
<!DOCTYPE html>
<html>
<head>
    <title>My Nginx Site</title>
</head>
<body>
    <h1>Welcome to Nginx!</h1>
    <p>정적 파일 서빙이 성공적으로 작동합니다.</p>
</body>
</html>
EOF

# 이미지 디렉토리 생성
sudo mkdir -p /var/www/mysite/images

4-2. 서버 블록 설정

/etc/nginx/conf.d/mysite.conf 파일 생성:

server {
    # 포트 80에서 수신
    listen 80;

    # 서버 이름 (도메인 또는 IP)
    server_name localhost;

    # 접근 로그
    access_log /var/log/nginx/mysite.access.log;
    error_log /var/log/nginx/mysite.error.log;

    # 루트 location
    location / {
        root /var/www/mysite;
        index index.html index.htm;
    }

    # 이미지 디렉토리
    location /images/ {
        root /var/www/mysite;
        # /images/logo.png 요청 → /var/www/mysite/images/logo.png
    }
}

4-3. 설정 적용

# 설정 테스트
sudo nginx -t

# ✅ 성공 메시지:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# 재로드
sudo nginx -s reload

4-4. 테스트

# 로컬에서 테스트
curl http://localhost

# 브라우저에서 접속
# http://your-server-ip

공식 문서 출처: Serving Static Content

Step 5: 리버스 프록시 설정

백엔드 애플리케이션 앞에 Nginx를 리버스 프록시로 사용하는 설정입니다.

사용 사례

클라이언트 → Nginx (포트 80) → Node.js 앱 (포트 3000)

5-1. 리버스 프록시 설정

/etc/nginx/conf.d/app-proxy.conf:

server {
    listen 80;
    server_name app.example.com;

    location / {
        # 백엔드 서버로 프록시
        proxy_pass http://localhost:3000;

        # 프록시 헤더 설정
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

5-2. API와 정적 파일 분리

실전에서는 API와 정적 파일을 함께 서빙하는 경우가 많습니다:

server {
    listen 80;
    server_name myapp.com;

    # 정적 파일 (React, Vue 등 빌드 결과물)
    location / {
        root /var/www/myapp/dist;
        try_files $uri $uri/ /index.html;
    }

    # API 요청은 백엔드로 프록시
    location /api/ {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

try_files 지시문 설명:

try_files $uri $uri/ /index.html;
# 1. $uri: 요청한 파일이 존재하면 반환
# 2. $uri/: 디렉토리면 index 파일 찾기
# 3. /index.html: 없으면 SPA의 index.html 반환

5-3. 로드 밸런싱

여러 백엔드 서버에 부하를 분산:

# upstream 블록 (http 컨텍스트 내부)
upstream backend {
    # 기본: 라운드 로빈
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    listen 80;

    location / {
        proxy_pass http://backend;
    }
}

로드 밸런싱 방법:

# 1. 라운드 로빈 (기본)
upstream backend {
    server server1.com;
    server server2.com;
}

# 2. 가중치 기반
upstream backend {
    server server1.com weight=3;  # 더 자주 선택됨
    server server2.com weight=1;
}

# 3. IP 해시 (같은 클라이언트는 같은 서버로)
upstream backend {
    ip_hash;
    server server1.com;
    server server2.com;
}

공식 문서 출처: Setting Up a Simple Proxy Server

주요 지시문 (Directives) 정리

server

가상 호스트를 정의합니다.

server {
    listen 80;
    server_name example.com www.example.com;
}

listen

서버가 수신할 포트와 주소를 지정합니다.

listen 80;                    # IPv4, 포트 80
listen [::]:80;               # IPv6, 포트 80
listen 443 ssl;               # HTTPS
listen 80 default_server;     # 기본 서버

server_name

가상 호스트 이름을 설정합니다.

# 정확한 이름
server_name example.com;

# 와일드카드
server_name *.example.com;
server_name example.*;

# 정규표현식
server_name ~^www\d+\.example\.com$;

location

URI 패턴에 따른 설정을 정의합니다.

# 접두사 매칭
location /images/ {
    root /data;
}

# 정확한 매칭
location = / {
    # 정확히 "/" 요청만
}

# 정규표현식 (대소문자 구분)
location ~ \.php$ {
    # .php로 끝나는 모든 요청
}

# 정규표현식 (대소문자 무시)
location ~* \.(jpg|jpeg|png|gif)$ {
    # 이미지 파일
}

우선순위:

  1. = (정확한 매칭)
  2. ^~ (우선 접두사)
  3. ~ 또는 ~* (정규표현식)
  4. / (접두사 매칭)

root vs alias

# root: 경로를 URI에 추가
location /images/ {
    root /data;
    # /images/cat.jpg → /data/images/cat.jpg
}

# alias: 경로를 URI로 대체
location /images/ {
    alias /data/photos/;
    # /images/cat.jpg → /data/photos/cat.jpg
}

index

디렉토리 요청 시 기본 파일을 지정합니다.

index index.html index.htm index.php;
# 순서대로 파일 존재 확인

try_files

파일 존재를 확인하고 첫 번째로 찾은 파일을 사용합니다.

location / {
    try_files $uri $uri/ =404;
    # 1. 파일 확인
    # 2. 디렉토리 확인
    # 3. 404 에러 반환
}

공식 문서 출처: ngx_http_core_module

자주 하는 실수와 해결책

1. root와 alias 혼동

# ❌ 잘못된 예
location /static/ {
    root /var/www/files/;
    # /static/style.css → /var/www/files/static/style.css ⚠️
}

# ✅ 올바른 예
location /static/ {
    alias /var/www/files/;
    # /static/style.css → /var/www/files/style.css ✅
}

2. proxy_pass 슬래시 주의

# 슬래시 없음
location /api/ {
    proxy_pass http://backend;
    # /api/users → http://backend/api/users
}

# 슬래시 있음
location /api/ {
    proxy_pass http://backend/;
    # /api/users → http://backend/users (api 제거됨!)
}

3. try_files 마지막은 코드 또는 URI

# ❌ 잘못된 예
try_files $uri $uri/;
# 마지막 옵션이 없어서 에러 발생

# ✅ 올바른 예
try_files $uri $uri/ =404;
try_files $uri $uri/ /index.html;

4. server_name이 없으면 첫 번째 서버가 기본값

# 명시적으로 기본 서버 지정
server {
    listen 80 default_server;
    server_name _;
    return 444;  # 알 수 없는 호스트 요청 거부
}

5. 설정 테스트 없이 재로드

# ❌ 위험한 방법
sudo systemctl restart nginx
# 설정 오류 시 서비스 중단!

# ✅ 안전한 방법
sudo nginx -t && sudo nginx -s reload
# 설정 검증 후 무중단 재로드

참고 자료

공식 문서

추가 학습 자료


다음 단계: 이제 Nginx의 기본을 익혔으니, HTTPS 설정, 캐싱, 보안 헤더 등 고급 기능을 학습해보세요!

댓글