๋ฆฌ๋ฒ์ค ํ๋ก์, ๋ก๋ ๋ฐธ๋ฐ์ฑ(Upstream), SSL/TLS(HTTPS), SPA ๋ผ์ฐํ
, Location ๋งค์นญ ์ฐ์ ์์, Rate Limiting ๋ฐ ์ค๋ฌด ํธ๋ฌ๋ธ์ํ
๋ ํผ๋ฐ์ค
## 1. CLI ํต์ฌ ์ ์ด & ์์คํ
์๋น์ค (CLI & Service Control)
```bash
# [1] ์ค์ ํ์ผ ๋ฌธ๋ฒ ๊ฒ์ฌ (์ค์ ์์ ํ ๋ฌด์กฐ๊ฑด ๋จผ์ ์คํ!)
nginx -t
# ํน์ ์ค์ ํ์ผ ๊ฒฝ๋ก๋ฅผ ์ง์ ํ์ฌ ๋ฌธ๋ฒ ๊ฒ์ฌ
nginx -t -c /etc/nginx/nginx.conf
# [2] ์๋น์ค ์ค๋จ ์๋ ๋ฌด์ค๋จ ์ค์ ๋ฆฌ๋ก๋ (Graceful Reload)
nginx -s reload
# ์ฆ์ ์ ์ง (Fast Shutdown) vs ์์ ์ ์ง (Graceful Shutdown)
nginx -s stop # ์ฆ์ ๊ฐ์ ์ข
๋ฃ
nginx -s quit # ํ์ฌ ์ฒ๋ฆฌ ์ค์ธ ์ฐ๊ฒฐ์ ๋ชจ๋ ์๋ฃํ ํ ์์ ์ข
๋ฃ
# [3] Linux systemd ์๋น์ค ๊ด๋ฆฌ ๋ช
๋ น์ด
sudo systemctl status nginx # ํ์ฌ ์คํ ์ํ ๋ฐ PID ํ์ธ
sudo systemctl start nginx # Nginx ์๋น์ค ์์
sudo systemctl stop nginx # Nginx ์๋น์ค ์ค์ง
sudo systemctl restart nginx # ํ๋ก์ธ์ค ์์ ์ฌ์์
sudo systemctl reload nginx # ์ค์ ๋ฆฌ๋ก๋ (๊ถ์ฅ)
sudo systemctl enable nginx # ๋ถํ
์ ์๋ ์์ ๋ฑ๋ก
# [4] ๋ฒ์ ๋ฐ ์ปดํ์ผ ๋ชจ๋ ์ ๋ณด ํ์ธ
nginx -v # ๊ธฐ๋ณธ ๋ฒ์ ์ถ๋ ฅ (์: nginx version: 1.24.0)
nginx -V # ์ปดํ์ผ๋ ๋ชจ๋ ๋ฐ configure ์ธ์ ์์ธ ํ์ธ
```
## 2. nginx.conf ๊ธฐ๋ณธ ๊ณจ๊ฒฉ ๊ตฌ์กฐ (Architecture & Contexts)
```nginx
# /etc/nginx/nginx.conf - ๋ฉ์ธ ์ค์ ํ์ผ ๊ณ์ธต ๊ตฌ์กฐ
user nginx; # ์์ปค ํ๋ก์ธ์ค๋ฅผ ์คํํ ์์คํ
๊ณ์
worker_processes auto; # CPU ์ฝ์ด ์์ ๋ง์ถฐ ์๋ ์์ปค ์์ฑ
pid /var/run/nginx.pid;
# 1. ์ด๋ฒคํธ ๋ธ๋ก: ๋คํธ์ํฌ ์ฐ๊ฒฐ ์ฒ๋ฆฌ ๋ฐฉ์
events {
worker_connections 1024; # ์์ปค ํ๋ก์ธ์ค๋น ๋์ ํ์ฉ ์ปค๋ฅ์
์
use epoll; # Linux ๊ณ ์ฑ๋ฅ ์ด๋ฒคํธ ํด๋ง ๋ฐฉ์
multi_accept on; # ํ ๋ฒ์ ์ฌ๋ฌ ์ฐ๊ฒฐ์ ์๋ฝ
}
# 2. HTTP ๋ธ๋ก: ์น ํธ๋ํฝ ์ ์ญ ์ค์
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ์ฑ๋ฅ ์ต์ ํ ๋๋ ํฐ๋ธ
sendfile on; # ์ปค๋ ๋ ๋ฒจ์์ ํ์ผ ์ง์ ์ ์ก (Zero-Copy)
tcp_nopush on; # ํจํท์ด ๊ฐ๋ ์ฐผ์ ๋๋ง ์ ์ก (ํค๋ ์ต์ ํ)
tcp_nodelay on; # ์์ผ ๋ฒํผ ์ง์ฐ ๋ฐฉ์ง (Keep-Alive ์ต์ ํ)
keepalive_timeout 65; # ํด๋ผ์ด์ธํธ ์ ํด ์ปค๋ฅ์
์ ์ง ์๊ฐ(์ด)
# Gzip ์์ถ ์ ์ก ์ค์
gzip on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml;
# ๊ฐ๋ณ ์ฌ์ดํธ ๊ฐ์ ํธ์คํธ ์ค์ ํ์ผ ํฌํจ
include /etc/nginx/conf.d/*.conf;
}
```
## 3. ๋ฆฌ๋ฒ์ค ํ๋ก์ ์ค์ (Reverse Proxy)
```nginx
# ๋ฐฑ์๋ WAS(Spring Boot, Node.js, Django ๋ฑ)๋ก ์์ฒญ ์ ๋ฌ
server {
listen 80;
server_name api.luckytechworld.com;
location / {
# ๋ฐฑ์๋ ์๋ฒ ์ฃผ์ (๋ด๋ถ ํฌํธ 8080)
proxy_pass http://127.0.0.1:8080;
# โ ๏ธ ํ์ ํ๋ก์ ํค๋ ์ ๋ฌ (๋ฐฑ์๋์์ ์ค์ ํด๋ผ์ด์ธํธ IP ์๋ณ)
proxy_set_header Host $host; # ์๋ณธ ์์ฒญ ํธ์คํธ
proxy_set_header X-Real-IP $remote_addr; # ์ค์ ์ ์ ํด๋ผ์ด์ธํธ IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # ๊ฑฐ์ณ์จ ํ๋ก์ IP ์ฒด์ธ
proxy_set_header X-Forwarded-Proto $scheme; # http ๋๋ https ํ๋กํ ์ฝ
# HTTP/1.1 ํ๋กํ ์ฝ ์ฌ์ฉ (Keep-Alive ์ง์)
proxy_http_version 1.1;
proxy_set_header Connection "";
# ํ๋ก์ ๋ฒํผ ๋ฐ ํ์์์
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
```
## 4. ๋ก๋ ๋ฐธ๋ฐ์ฑ & ์
์คํธ๋ฆผ (Load Balancing: upstream)
```nginx
# ๋ค์ค ๋ฐฑ์๋ ์ธ์คํด์ค๋ก ํธ๋ํฝ ๋ถ์ฐ
upstream backend_cluster {
# 1. ๋ผ์ด๋ ๋ก๋น (๊ธฐ๋ณธ๊ฐ): ๊ท ๋ฑ ๋ถ๋ฐฐ
# 2. least_conn: ํ์ฌ ์ฐ๊ฒฐ ์๊ฐ ๊ฐ์ฅ ์ ์ ์๋ฒ๋ก ๋ถ๋ฐฐ
# least_conn;
# 3. ip_hash: ํด๋ผ์ด์ธํธ IP ๊ธฐ๋ฐ ๊ณ ์ ์ธ์
(Sticky Session)
# ip_hash;
server 10.0.1.10:8080 weight=3 max_fails=3 fail_timeout=10s; # ๊ฐ์ค์น 3 (์ฐ์ ์ ๋ฌ)
server 10.0.1.11:8080 weight=1 max_fails=3 fail_timeout=10s;
server 10.0.1.12:8080 backup; # ๋ค๋ฅธ ์๋ฒ ์ฅ์ ์์๋ง ๋์ํ๋ ์๋น ์๋ฒ
server 10.0.1.13:8080 down; # ์ ๊ฒ ์ค์ผ๋ก ์ ์ธ๋ ์๋ฒ
keepalive 32; # ์
์คํธ๋ฆผ ์๋ฒ์์ ์ ํด ์ปค๋ฅ์
์ ์ง ํ
}
server {
listen 80;
server_name service.luckytechworld.com;
location / {
proxy_pass http://backend_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
## 5. ์ ์ ํ์ผ ํธ์คํ
& SPA ๋ผ์ฐํ
(Static Files & SPA)
```nginx
# Vue, React, Angular ๋ฑ SPA ๋ฐ ์ ์ ์์
์๋น
server {
listen 80;
server_name luckytechworld.com;
root /var/www/dist; # ์ ์ ํ์ผ ๋ฃจํธ ๋๋ ํ ๋ฆฌ
index index.html;
# [1] SPA ๋ธ๋ผ์ฐ์ ์๋ก๊ณ ์นจ 404 ํด๊ฒฐ (ํ์!)
# ํ์ผ -> ๋๋ ํ ๋ฆฌ -> ์์ผ๋ฉด index.html๋ก ๋ด๋ถ ๋ฆฌ๋ค์ด๋ ํธ
location / {
try_files $uri $uri/ /index.html;
}
# [2] ์ ์ ์์
(์ด๋ฏธ์ง, JS, CSS) ๋ธ๋ผ์ฐ์ ์บ์ฑ (1๋
๋ณด๊ด)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, no-transform, immutable";
access_log off; # ์์
์์ฒญ์ ์ก์ธ์ค ๋ก๊ทธ ์ ์ธ๋ก I/O ์ ์ฝ
}
# [3] ์จ๊น ํ์ผ(.git, .env ๋ฑ) ์ ๊ทผ ์ฐจ๋จ
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
```
## 6. SSL/TLS (HTTPS) ์ธ์ฆ์ & ๋ฆฌ๋ค์ด๋ ํธ
```nginx
# [1] 80๋ฒ ํฌํธ(HTTP) -> 443๋ฒ ํฌํธ(HTTPS) ์๊ตฌ ๋ฆฌ๋ค์ด๋ ํธ (301)
server {
listen 80;
listen [::]:80;
server_name luckytechworld.com www.luckytechworld.com;
return 301 https://$host$request_uri;
}
# [2] 443๋ฒ ํฌํธ (HTTPS & HTTP/2 ๋ณด์ ์๋ฒ)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name luckytechworld.com www.luckytechworld.com;
# Let's Encrypt ๋๋ ์์ฉ SSL ์ธ์ฆ์ ๊ฒฝ๋ก
ssl_certificate /etc/letsencrypt/live/luckytechworld.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/luckytechworld.com/privkey.pem;
# ์์ ํ ์ํธํ ํ๋กํ ์ฝ ๋ฐ ์ธ์
์ค์
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384";
ssl_session_cache shared:SSL:10m; # 10MB ์บ์ (์ฝ 4๋ง ์ธ์
๊ณต์ )
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS ์ค์ (๋ธ๋ผ์ฐ์ ์ 1๋
๊ฐ ๋ฌด์กฐ๊ฑด HTTPS ์ ์ ๊ฐ์ )
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://127.0.0.1:8080;
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 https;
}
}
```
## 7. Location ๋งค์นญ ๋ฌธ๋ฒ & ์ฐ์ ์์ (Location Matching)
```nginx
# [Location ๋งค์นญ ์ฐ์ ์์ ๊ท์น (์์์๋ถํฐ ์ฐ์ ํ๊ฐ๋จ)]
# 1์์: = (์ ํํ ์ผ์น - Exact Match)
location = /login {
# /login ์๋ง ์๋ฒฝํ ์ผ์น (์ฟผ๋ฆฌ์คํธ๋ง ์ ์ธ)
}
# 2์์: ^~ (์ ๊ท์ ๊ฒ์ฌ ์ค๋จ ์ ๋ฐฉ ์ผ์น)
location ^~ /images/ {
# /images/ ๋ก ์์ํ๋ ๊ฒฝ๋ก๋ ์๋ ์ ๊ท์(~)์ ๊ฑด๋๋ฐ๊ณ ์ฆ์ ์ ์ฉ
}
# 3์์: ~ (๋์๋ฌธ์ ๊ตฌ๋ถ ์ ๊ท ํํ์)
location ~ \.(php|py)$ { }
# 4์์: ~* (๋์๋ฌธ์ ๋ฌด์ ์ ๊ท ํํ์)
location ~* \.(jpg|png|gif)$ { }
# 5์์: /prefix (๊ฐ์ฅ ๊ธด ์ ๋ฐฉ ์ผ์น - Prefix Match)
location /api/v1/ { }
# ์ตํ์: / (๊ธฐ๋ณธ ํด๋ฐฑ - Default Fallback)
location / {
# ์ผ์นํ๋ ๋ค๋ฅธ ๋ฃฐ์ด ์์ ๋ ์ต์ข
์คํ
}
```
## 8. ๋ณด์ ํค๋ & IP ์ ๊ทผ ์ ์ด (Security & Access Control)
```nginx
# ์ ์ญ ๋ณด์ ์๋ต ํค๋ ์ถ๊ฐ (ํด๋ฆญ์ฌํน, XSS ๋ฐฉ์ง)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# ๊ด๋ฆฌ์ ํ์ด์ง ํน์ IP๋ง ํ์ฉ (IP Whitelist)
location /admin/ {
allow 192.168.1.100; # ํน์ ์ฌ๋ด IP ํ์ฉ
allow 10.0.0.0/24; # ์๋ธ๋ท ํ์ฉ
deny all; # ๊ทธ ์ธ ๋ชจ๋ IP ์ฐจ๋จ (403 Forbidden)
proxy_pass http://127.0.0.1:8080;
}
# ํน์ ์
์ฑ IP ์๋ ์ฐจ๋จ (Blacklist)
location / {
deny 203.0.113.50; # ์
์ฑ ๋ด IP ์ฐจ๋จ
deny 198.51.100.0/24;
# ...
}
```
## 9. ์๋ ์ ํ & DDoS ๋ฐฉ์ด (Rate Limiting)
```nginx
# http ๋ธ๋ก์ ์๋ ์ ํ ์์ญ ์ ์
http {
# ํด๋ผ์ด์ธํธ IP($binary_remote_addr) ๊ธฐ์ค์ผ๋ก 10MB ๋ฉ๋ชจ๋ฆฌ ํ ๋น, ์ด๋น 10๊ฐ ์์ฒญ ์ ํ
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# IP๋น ๋์ ํ์ฑ ์ปค๋ฅ์
์ ์ ํ ์์ญ
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 80;
# ๋ก๊ทธ์ธ ๋ฐ ํ์๊ฐ์
์๋ํฌ์ธํธ ๋ฌด์ฐจ๋ณ ๋์
๋ฐฉ์ด
location /api/v1/auth/ {
# burst=5: ์๊ฐ์ ์ผ๋ก ์ต๋ 5๊ฐ ์์ฒญ๊น์ง๋ ๋ฒํผ์ ๋๊ธฐ ํ์ฉ
# nodelay: ๋๊ธฐ ์์ด ์ฆ์ ์ฒ๋ฆฌํ๋ ํ๋ ์ด๊ณผ ์ 503 ๋ฐํ
limit_req zone=api_limit burst=5 nodelay;
limit_conn conn_limit 5; # ๋จ์ผ IP๋น ๋์ ์ปค๋ฅ์
์ต๋ 5๊ฐ
limit_req_status 429; # ๊ธฐ๋ณธ 503 ๋์ 429 Too Many Requests ๋ฐํ
proxy_pass http://127.0.0.1:8080;
}
}
}
```
## 10. ์น์์ผ ํ๋ก์ ์ค์ (WebSocket: ws / wss)
```nginx
# WebSocket์ HTTP/1.1 ์
๊ทธ๋ ์ด๋ ํค๋ ๊ตํ์ด ํ์์ ์
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name socket.luckytechworld.com;
location /ws/ {
proxy_pass http://127.0.0.1:8080;
# ์น์์ผ ์ ์ฉ ํ์ ๋๋ ํฐ๋ธ
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# ๊ธด ์ฐ๊ฒฐ ์ ์ง๋ฅผ ์ํ ์ฝ๊ธฐ ํ์์์ ์ฐ์ฅ (๊ธฐ๋ณธ 60์ด -> 1์๊ฐ)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
```
## 11. ์
๋ก๋ ์ฉ๋ & ๋ฒํผ/ํ์์์ ํ๋ (Tuning & Uploads)
```nginx
server {
listen 80;
# [1] ๋์ฉ๋ ํ์ผ ์
๋ก๋ ์ 413 Request Entity Too Large ํด๊ฒฐ
client_max_body_size 50M; # ์ต๋ ์
๋ก๋ ํ์ผ ํฌ๊ธฐ (๊ธฐ๋ณธ๊ฐ: 1M)
client_body_buffer_size 128k; # ๋ฉ๋ชจ๋ฆฌ ๋ด ์์ฒญ ๋ณธ๋ฌธ ๋ฒํผ ํฌ๊ธฐ
# [2] ๋ฐฑ์๋ ์๋ต ๋์ฉ๋ ๋ค์ด๋ก๋ ๋ฐ ๋ฒํผ ์ค์
proxy_buffering on; # ํ๋ก์ ๋ฒํผ๋ง ํ์ฑํ
proxy_buffer_size 16k; # ์ฒซ ์๋ต ํค๋ ๋ฒํผ ํฌ๊ธฐ
proxy_buffers 8 64k; # ๋ณธ๋ฌธ ๋ฒํผ ์ ๋ฐ ํฌ๊ธฐ (8 x 64k = 512k)
proxy_busy_buffers_size 128k;
# [3] ๋์ฉ๋ ๋ฐฐ์น/๋ณด๊ณ ์ API ์ง์ฐ ๋๊ธฐ (504 Gateway Timeout ๋ฐฉ์ง)
proxy_connect_timeout 120s; # ๋ฐฑ์๋ ์๋ฒ ์ฐ๊ฒฐ ํ์์์
proxy_read_timeout 300s; # ๋ฐฑ์๋ ์๋ต ์ฝ๊ธฐ ๋๊ธฐ ํ์์์ (5๋ถ)
proxy_send_timeout 300s; # ๋ฐฑ์๋๋ก ๋ฐ์ดํฐ ์ ์ก ํ์์์
}
```
## 12. ์ค๋ฌด ํธ๋ฌ๋ธ์ํ
& ๋ก๊ทธ ๋ถ์ (Troubleshooting & Logs)
```nginx
# [1] ์ปค์คํ
JSON ํฌ๋งท ์ก์ธ์ค ๋ก๊ทธ (ELK / CloudWatch ์์ง์ฉ)
log_format json_analytics escape=json
'{"time_local":"$time_local",'
'"client_ip":"$remote_addr",'
'"request_method":"$request_method",'
'"request_uri":"$request_uri",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_addr":"$upstream_addr",'
'"http_user_agent":"$http_user_agent"}';
access_log /var/log/nginx/access.log json_analytics;
error_log /var/log/nginx/error.log warn; # debug, info, notice, warn, error, crit
```
```bash
# [2] ๋ํ์ ์ธ HTTP ์๋ฌ ์์ธ ๋ฐ ํด๊ฒฐ ์ฒดํฌ๋ฆฌ์คํธ
# 502 Bad Gateway:
# - ์์ธ: ๋ฐฑ์๋ WAS๊ฐ ๊บผ์ ธ ์๊ฑฐ๋ ํฌํธ/์์ผ์ด ์ด๋ ค์์ง ์์.
# - ํ์ธ: curl http://127.0.0.1:8080 ๋๋ sudo netstat -tlpn | grep 8080
# 504 Gateway Timeout:
# - ์์ธ: ๋ฐฑ์๋ ์ฒ๋ฆฌ ์๊ฐ์ด proxy_read_timeout(๊ธฐ๋ณธ 60์ด)์ ์ด๊ณผํจ.
# - ํด๊ฒฐ: ๋ฐฑ์๋ ์ฟผ๋ฆฌ ํ๋ ๋๋ proxy_read_timeout ์ํฅ ์กฐ์ .
# 413 Request Entity Too Large:
# - ์์ธ: ํ์ผ ์
๋ก๋ ํฌ๊ธฐ๊ฐ client_max_body_size ํ๋๋ฅผ ์ด๊ณผํจ.
# - ํด๊ฒฐ: client_max_body_size 50M; ์ค์ ์ถ๊ฐ ํ reload.
# ์ค์๊ฐ ์๋ฌ ๋ก๊ทธ ๋ชจ๋ํฐ๋ง:
sudo tail -f /var/log/nginx/error.log
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์