6-1. 대규모 트래픽 설계
서비스를 운영하다 보면 특정 순간에 엄청난 양의 요청이 몰릴 수 있음.
예를 들어 로그인 API, 게시글 조회 API, AI 모델 호출 API 등에 수천 명의 사용자가 동시에 접근하면 서버가 버티지 못할 수도 있음.
그래서 대규모 트래픽 환경에서는 서버를 보호하기 위한 다양한 기법을 사용하게 되는데, 그중 가장 기본적인 기술 중 하나가 Rate Limiting 임 !
1. Rate Limiting 이란 ?
Rate Limiting은 일정 시간 동안 허용 가능한 요청 수를 제한하는 기술임.
쉽게 말하면 사용자가 API를 너무 많이 호출하지 못하도록 제한을 거는 것이라고 생각하면 됨.
예를 들어
- 1분 동안 최대 10회 요청 허용
- 1시간 동안 최대 1000회 요청 허용
- 하루 동안 최대 10000회 요청 허용
같은 방식으로 설정할 수 있음.
OpenAI API, GitHub API, AWS API 등 대부분의 서비스들이 사용하고 있음 !
2. 왜 사용하는 걸까 ?
만약 요청 제한이 없다면 어떤 일이 발생할까 ?
한 명의 사용자가 초당 수천 번 요청을 보내도 서버는 전부 처리하려고 시도하게 됨.
결국 CPU 사용량이 폭증하고 DB 연결이 가득 차면서 정상 사용자까지 피해를 입게 됨.
그래서 Rate Limiting을 통해 서버를 보호하게 됨.
| 목적 | 설명 |
| 서버 보호 | 과도한 요청 차단 |
| 비용 절감 | 불필요한 API 호출 감소 |
| DDoS 대응 | 비정상 트래픽 완화 |
| 공정성 확보 | 특정 사용자의 독점 방지 |
| 시스템 안정성 | 전체 서비스 품질 유지 |
3. 동작 방식
사용자 요청
↓
현재 요청 횟수 확인
↓
허용 횟수 초과 ?
↙ ↘
아니오 예
↓ ↓
요청 처리 429 에러 반환
429 Too Many Requests 는 요청 횟수 제한을 초과했다는 의미의 HTTP 상태 코드임.
4. FastAPI 메모리 기반 Rate Limiting
가장 단순한 방식은 Python 메모리에 요청 기록을 저장하는 방법임.
학습용 프로젝트에서는 충분히 사용할 수 있음 !
from fastapi import FastAPI, Request, HTTPException
import time
app = FastAPI()
request_log = {}
@app.get("/")
def home(request: Request):
ip = request.client.host
now = time.time()
if ip not in request_log:
request_log[ip] = []
request_log[ip] = [
t for t in request_log[ip]
if now - t < 60
]
if len(request_log[ip]) >= 1000:
raise HTTPException(
status_code=429,
detail="Too Many Requests"
)
request_log[ip].append(now)
return {"message": "success"}
위 코드는 1분 동안 최대 1000번 요청만 허용함.
6번째 요청부터는 429 에러를 반환하게 됨.
메모리 방식의 문제점
- 서버 재시작 시 데이터 유실
- 멀티 서버 환경 불가능
- 메모리 사용량 증가
- 확장성 부족
그래서 실제 서비스에서는 잘 사용하지 않음.
5. FastAPI + Redis 기반 Rate Limiting
실무에서는 대부분 Redis를 사용함.
Redis는 메모리 기반 Key-Value 저장소이며 매우 빠르게 카운터를 관리할 수 있음.
또한 여러 대의 서버가 동일한 Redis를 사용할 수 있어서 분산 환경에서도 안정적으로 동작함.
from fastapi import FastAPI, Request, HTTPException
import redis
app = FastAPI()
r = redis.Redis(
host="localhost",
port=6379,
decode_responses=True
)
@app.get("/")
def home(request: Request):
ip = request.client.host
key = f"rate:{ip}"
count = r.incr(key)
if count == 1:
r.expire(key, 60)
if count > 1000:
raise HTTPException(
status_code=429,
detail="Too Many Requests"
)
return {"message": "success"}
Redis의 incr() 는 값을 1 증가시키는 명령어임.
expire() 를 사용하여 60초 뒤 자동으로 삭제되도록 설정할 수 있음.
즉, 1분 동안 요청 횟수를 카운트하는 구조라고 생각하면 됨 !
만약 직접 트래픽을 확인해보고 싶다면 Locust를 사용해봐도 댐. (내가해봄...)
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
일단 FastAPI 서버를 실행하고 ,
from locust import HttpUser, task, between
class RateLimitTestUser(HttpUser):
wait_time = between(0.1, 0.5)
@task
def call_root_api(self):
self.client.get("/")
Locust 코드르 짜서
locust -f locustfile.py
로 실행한다음 localhost:8089 들어가서
localhost:8000번으로 쏴보면댐 . 위 FastAPI 코드는 1분 동안 최대 1000번 요청만 수락하니 . 1분에 1001번이 넘으면 429를 반환할거임 ㅇㅈ ?

이렇게됌 ㅇㅇ...
6. 메모리 방식 vs Redis 방식
| 항목 | 메모리 | Redis |
| 속도 | 매우 빠름 | 매우 빠름 |
| 서버 재시작 | 데이터 유실 | 유지 가능 |
| 멀티 서버 | 불가능 | 가능 |
| 운영 환경 | 비추천 | 권장 |
| 확장성 | 낮음 | 높음 |
정리
Rate Limiting은 대규모 트래픽 환경에서 서버를 보호하기 위한 가장 기본적인 기술 중 하나임.
과도한 요청을 차단하여 시스템의 안정성을 유지하고, 모든 사용자에게 공정한 서비스를 제공할 수 있음.
학습 단계에서는 메모리 방식으로 충분하지만 실제 서비스에서는 Redis 기반 Rate Limiting을 사용하는 경우가 대부분이라고 생각하면 됨 !