문제 상황
서비스를 운영하다 보면 예상치 못한 트래픽 급증이나 악의적인 공격에 직면할 수 있다.
실제로 우리 서비스도 특정 API 엔드포인트에 대량의 요청이 몰리면서 서버가 불안정해지는 경험을 했다.



사진을 보면 같은 API에 대해 초당 수십 건의 요청이 들어오고 있었다. 정상적인 사용 패턴이 아닌 건 명백했다.
이런 상황에서 가장 먼저 떠올릴 수 있는 해결책은 Cloudflare 같은 CDN/WAF 서비스를 도입하는 것이다. 하지만 지금 당장 문제를 해결
해야 하는 상황이라면? Nginx의 Rate Limiting 기능을 활용하면 빠르게 대응할 수 있다.
Rate Limiting이란?
Rate Limiting은 특정 시간 동안 허용되는 요청 수를 제한하는 기법이다. 예를 들어:
- IP당 초당 10개 요청만 허용
- 동시 접속은 IP당 최대 10개까지
- 초과하는 요청은 429 (Too Many Requests) 에러 반환
이를 통해 서버 리소스를 보호하고, 악의적인 트래픽을 차단할 수 있다.
Nginx Rate Limiting 적용하기
우리 서비스는 Nginx를 리버스 프록시로 사용하고 있어서, 애플리케이션 레벨 수정 없이 설정만으로 Rate Limiting을 적용할 수 있었다.
1. Rate Limiting Zone 정의
먼저 Nginx 설정 파일 상단에 Rate Limiting을 위한 존(zone)을 정의한다.
# API 전체에 대한 Rate Limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=50r/s;
# 동시 접속 수 제한
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
각 설정의 의미:
- $binary_remote_addr: 클라이언트 IP 주소 (바이너리 형식으로 메모리 절약)
- zone=api_limit:10m: "api_limit"이라는 이름의 존을 생성하고, 10MB 메모리 할당 (약 160,000개의 IP 추적 가능)
- rate=50r/s: 초당 50개 요청으로 제한
2. DDoS 방어 기본 설정
서버 블록에 타임아웃 관련 설정을 추가해서 느린 공격(Slow HTTP Attack)도 방어한다.
server {
listen 443 ssl;
server_name example.com
# DDoS 방어 기본 설정
client_body_timeout 10s; # 요청 본문 전송 타임아웃
client_header_timeout 10s; # 헤더 전송 타임아웃
keepalive_timeout 5s 5s; # Keep-Alive 연결 타임아웃
send_timeout 10s; # 응답 전송 타임아웃
# ... SSL 설정 등
}
3. Location별 Rate Limiting 적용
이제 실제로 각 엔드포인트에 Rate Limiting을 적용한다.
# 일반 API 엔드포인트
location /api/ {
# Rate Limiting 적용
limit_req zone=api_limit burst=100 nodelay;
limit_conn conn_limit 10;
# 429 에러 커스텀 응답
limit_req_status 429;
proxy_pass http://example;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
# 타임아웃 설정
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
주요 설정 설명:
- limit_req zone=api_limit burst=100 nodelay
- api_limit 존 사용
- burst=100: 순간적으로 최대 100개까지 버스트 허용
- nodelay: 초과 요청을 큐에 넣지 않고 즉시 처리 또는 거부
- limit_conn conn_limit 10
- IP당 동시 접속 10개로 제한
- limit_req_status 429
- 제한 초과 시 429 상태 코드 반환
4. 커스텀 에러 페이지
Rate Limit에 걸린 사용자에게 친절한 안내 메시지를 제공한다.
# 429 에러 페이지
error_page 429 /429.json;
location = /429.json {
internal;
default_type application/json;
return 429 '{"error":"Too many requests","message":"Please try again later"}';
}
전체 설정 예시
최종적으로 우리 서비스에 적용한 전체 설정은 다음과 같다.
# Rate Limiting Zone 정의
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
upstream example {
server 127.0.0.1:????;
}
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
client_max_body_size 500M;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
resolver 8.8.8.8 1.1.1.1 valid=30s ipv6=off;
# DDoS 방어 기본 설정
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;
# ... FrontEnd 설정 등
# 백엔드 API
location /api/ {
# Rate Limiting 적용
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://example;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
# 타임아웃 설정
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
# 429 에러 페이지
error_page 429 /429.json;
location = /429.json {
internal;
default_type application/json;
return 429 '{"error":"Too many requests","message":"Please try again later"}';
}
}
테스트
Apache Bench(ab)를 사용해서 실제로 Rate Limiting이 작동하는지 테스트했다.
# 45개 요청을 15개 동시 접속으로 테스트
ab -n 45 -c 15 https://example.com/api/@@@@@@
테스트 결과:
Server Software: nginx/1.24.0
Server Hostname: example.com
Server Port: 443
Document Path: /api/@@@@@@@@
Document Length: 3660 bytes
Concurrency Level: 15
Time taken for tests: 0.429 seconds
Complete requests: 45
Failed requests: 33 ← Rate Limit으로 차단된 요청
(Connect: 0, Receive: 0, Length: 33, Exceptions: 0)
Non-2xx responses: 33 ← 429 에러 응답
Total transferred: 58533 bytes
Requests per second: 104.88 [#/sec] (mean)
Time per request: 143.021 [ms] (mean)
Connection Times (ms)
min mean[+/-sd] median max
Connect: 23 46 16.9 39 76
Processing: 5 40 51.0 10 143
Waiting: 5 40 51.0 10 142
Total: 29 86 59.5 58 211
결과 분석:
- 전체 45개 요청 중 12개만 성공 (200 OK)
- 나머지 33개는 429 에러 (Too Many Requests)
- Rate Limiting이 정상적으로 작동 ✅
이는 우리가 테스트 설정 rate=5r/s, burst=10이 짧은 시간(0.429초)에 몰린 45개의 동시 요청을 적절히 제어했다는 의미다.
효과 측정
Rate Limiting 적용 전후를 비교해보면 확실한 개선을 확인할 수 있다.
Before (적용 전)
동시 대량 요청 시:
- 모든 요청이 서버까지 도달
- CPU 사용률 급증 (70~95%)
- 응답 시간 지연 (평균 300ms → 최대 2초)
- 정상 사용자도 영향 받음
After (적용 후)
동시 대량 요청 시:
- 초과 요청은 Nginx에서 즉시 차단 (429 반환)
- 서버는 허용된 요청만 처리
- CPU 사용률 안정적 유지 (30~50%)
- 정상 사용자는 영향 없음
실제 테스트 결과:
45개 동시 요청 발생 시:
- ✅ 12개 정상 처리 (200 OK)
- ❌ 33개 즉시 차단 (429 Too Many Requests)
- 처리 시간: 평균 86ms (매우 빠름)
서버 리소스 보호:
- Nginx가 앞단에서 차단하므로 백엔드는 과부하 방지
- 데이터베이스 커넥션 낭비 방지
- 메모리 사용량 안정적 유지
악의적인 트래픽이 차단되면서 서버 리소스가 정상 사용자를 위해 사용되었고, 전체적인 안정성이 크게 개선되었다.
주의사항 및 팁
1. 너무 엄격한 제한은 피하자
Rate Limiting을 너무 빡빡하게 설정하면 정상 사용자도 불편을 겪을 수 있다. 우리 서비스의 경우:
- 일반 API: 초당 50개 (burst 100)
이 정도면 정상적인 사용에는 문제가 없으면서도 공격은 효과적으로 막을 수 있었다.
2. 부하 테스트로 적정값 찾기
설정 적용 전에 부하 테스트로 적절한 값을 찾는 것이 중요하다.
# Apache Bench로 테스트
ab -n 45 -c 15 https://example.com/api/@@@@@@
# 결과 확인:
# - 정상 사용자 패턴에서 429 에러가 거의 없어야 함
# - 공격 패턴에서는 대부분 429 에러가 발생해야 함
실제 테스트 예시:
45개 요청, 15개 동시 접속:
- 성공: 12개 (27%)
- 차단: 33개 (73%)
→ Rate Limiting이 효과적으로 작동 ✅
3. 화이트리스트 적용
특정 IP (예: 사내망, 모니터링 도구)는 Rate Limiting에서 제외할 수 있다.
geo $limit {
default 1;
# 화이트리스트 IP
10.0.0.0/8 0;
192.168.0.0/16 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=api_general:10m rate=50r/s;
하지만 아직 적용은 안 했다
4. 엔드포인트별 차등 적용
모든 API에 동일한 제한을 적용할 필요는 없다. 중요도와 트래픽 패턴에 따라 차등 적용하자.
# 읽기 API - 관대하게
location /api/read {
limit_req zone=api_general burst=100 nodelay;
# ...
}
# 쓰기 API - 엄격하게
location ~ ^/api/write$ {
if ($request_method = POST) {
limit_req zone=api_heavy burst=20 nodelay;
}
# ...
}
다음 단계: Cloudflare 도입
Nginx Rate Limiting은 즉각적이고 효과적인 해결책이지만, 더 강력한 보호를 위해서는 Cloudflare 같은 전문 서비스 도입을 고려해야 한다.
Nginx vs Cloudflare
| 항목 | Nginx | Cloudflare |
| 적용 속도 | 5분 | 5분 |
| 비용 | 무료 | 무료~유료 |
| DDoS 방어 규모 | 서버 수준 | Tbps 급 |
| 관리 편의성 | 수동 설정 | 자동화된 룰 |
| 글로벌 CDN | ❌ | ✅ |
현재는 Nginx로 충분하지만, 트래픽이 더 증가하거나 대규모 DDoS 공격에 대비하려면 Cloudflare 도입을 검토할 예정이다.
마무리
Rate Limiting은 작은 설정으로 큰 효과를 볼 수 있는 기법이다.
특히 Nginx를 이미 사용하고 있다면, 설정 파일 몇 줄만 추가해도 서버를 보호할 수 있다.
물론 완벽한 해결책은 아니다. 고도화된 DDoS 공격이나 분산 공격에는 Cloudflare, AWS Shield 같은 전문 서비스가 필요하다.
하지만 지금 당장 문제를 해결해야 한다면, Nginx Rate Limiting은 가장 빠르고 효과적인 선택지다.
우리 서비스도 이 설정만으로 즉각적인 효과를 봤고, 서버 안정성이 크게 개선되었다.
여러분도 비슷한 상황이라면 한번 시도해보길 추천한다!
'IT > Cloud' 카테고리의 다른 글
| Spring Boot 서버를 AWS ECS Fargate로 배포하기 - Route 53 + ACM으로 HTTPS 적용 (3) (0) | 2026.03.28 |
|---|---|
| Spring Boot 서버를 AWS ECS Fargate로 배포하기 - ALB로 고정 도메인 연결 (2) (0) | 2026.03.28 |
| Spring Boot 서버를 AWS ECS Fargate로 배포하기 - ECR + ECS + GitHub Actions CI/CD 구축 (1) (0) | 2026.03.26 |
| 스프링부트 서버 과부화 테스트(2) (0) | 2025.09.10 |
| 스프링부트 서버 과부화 테스트 (0) | 2025.09.09 |