Nginx 설치와 설정 - 기초부터 실전까지
“웹 서버를 설치하려는데, Apache보다 Nginx가 더 빠르다던데?” “Nginx 설정 파일이 복잡해 보이는데, 어디서부터 시작해야 할까?”
웹 개발을 하다 보면 서버를 직접 설정해야 하는 순간이 옵니다. 저도 처음에는 Nginx의 설정 파일 구조가 낯설어서 공식 문서를 여러 번 읽었습니다. 하지만 기본 구조를 이해하고 나니, 정적 파일 서빙부터 리버스 프록시까지 자유롭게 설정할 수 있게 되었습니다.
이 가이드에서는 Nginx 공식 문서를 기반으로 설치부터 기본 설정까지 단계별로 설명합니다.
목차
- Nginx란 무엇인가?
- 리버스 프록시란 무엇인가?
- 설치하기 전에
- Step 1: Nginx 설치 (Linux)
- Step 2: Nginx 시작과 제어
- Step 3: 설정 파일 구조 이해하기
- Step 4: 기본 설정 - 정적 파일 서빙
- Step 5: 리버스 프록시 설정
- 주요 지시문 (Directives) 정리
- 자주 하는 실수와 해결책
- 참고 자료
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 │
└───────────────────┘
이점:
- 단일 진입점: 하나의 도메인으로 여러 서비스 제공
- 보안 계층: 외부에서 백엔드 직접 접근 차단
- 유연성: 백엔드 서버 변경 시 클라이언트는 알 필요 없음
- 성능 최적화: 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;
}
}
작동 방식:
- 사용자가
http://myapp.com접속 - Nginx가 80번 포트에서 요청 받음
- Nginx가
localhost:3000(Node.js)로 요청 전달 - 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)$ {
# 이미지 파일
}
우선순위:
=(정확한 매칭)^~(우선 접두사)~또는~*(정규표현식)/(접두사 매칭)
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 설정, 캐싱, 보안 헤더 등 고급 기능을 학습해보세요!
댓글