
429 Too Many Requests는 클라이언트가 너무 많은 요청을 보냈을 때 사용할 수 있는 HTTP 상태 코드입니다. 하지만 좋은 rate limit 설계는 단순히 요청을 막는 것에서 끝나지 않습니다.
핵심은 서버 보호, 공정한 사용량 배분, 클라이언트 재시도 경험을 함께 설계하는 것입니다. 429만 반환하고 이유와 재시도 기준을 주지 않으면 클라이언트는 더 거칠게 재시도할 수 있습니다.

429 Too Many Requests는 언제 쓰는가
RFC 6585는 429 Too Many Requests를 rate limiting에 사용할 수 있는 상태 코드로 정의합니다. 즉 서버가 과부하라서 모든 요청을 처리하지 못한다는 503과 달리, 특정 클라이언트나 기준이 너무 많은 요청을 보냈다는 의미에 가깝습니다.
- 사용자별 요청 횟수 제한
- API key별 요청량 제한
- IP별 짧은 시간 과다 요청 제한
- 비싼 엔드포인트에 대한 별도 제한
- 로그인/인증 같은 민감 경로 보호
무엇을 기준으로 제한할지 먼저 정한다
rate limit은 기준을 잘못 잡으면 억울한 차단이 생깁니다. IP 기준만 쓰면 회사나 학교처럼 여러 사용자가 같은 NAT 뒤에 있을 때 문제가 생길 수 있습니다. 사용자 기준만 쓰면 로그인 전 공격에는 약할 수 있습니다.
그래서 실무에서는 하나의 기준만 쓰기보다 경로와 위험도에 따라 여러 기준을 조합합니다.
대표 기준
- user_id: 로그인한 사용자별 제한
- api_key: 외부 연동 고객별 제한
- ip: 비로그인 요청과 공격성 트래픽 완화
- route: 비싼 API나 인증 API에 더 엄격한 제한
- tenant: 조직 단위 사용량 보호
Retry-After를 함께 설계한다
429 응답을 보낼 때 클라이언트가 언제 다시 시도하면 되는지 알려주면 재시도 폭주를 줄일 수 있습니다. HTTP Retry-After 헤더는 다음 요청 전 기다릴 시간을 표현하는 데 사용할 수 있습니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{
"error": "rate_limit_exceeded",
"message": "30초 뒤 다시 시도해 주세요."
}헤더와 본문 메시지를 함께 주면 자동 클라이언트와 사람이 보는 화면 모두에서 이해하기 쉽습니다.
token bucket은 왜 자주 쓰이는가
token bucket은 일정 속도로 토큰을 채우고, 요청마다 토큰을 하나씩 쓰는 방식입니다. 순간적인 burst를 어느 정도 허용하면서도 장기 평균 요청량을 제한할 수 있습니다.
bucket capacity = 100
refill rate = 10 tokens / second
request:
if token exists:
consume 1 token and allow
else:
return 429이 방식은 사용자가 짧은 순간에 요청을 몰아서 보내는 정상 패턴을 어느 정도 받아줄 수 있어 API rate limit에 자주 어울립니다.
sliding window는 무엇이 다른가
sliding window는 최근 일정 시간 범위 안의 요청 수를 기준으로 제한합니다. 예를 들어 최근 60초 동안 100회까지만 허용하는 식입니다. 고정 창보다 경계 시점에 요청이 몰리는 문제를 줄일 수 있습니다.
- fixed window: 구현은 쉽지만 창 경계에서 burst가 생길 수 있다
- sliding window: 최근 구간 기준이라 더 부드럽지만 저장/계산 비용이 늘 수 있다
- token bucket: burst 허용과 평균 제한을 함께 다루기 좋다
엔드포인트별 비용을 반영한다
모든 API에 같은 제한을 거는 것은 단순하지만 정확하지 않습니다. 검색, 리포트 생성, AI 호출처럼 비용이 큰 API는 더 엄격해야 할 수 있고, 가벼운 조회 API는 더 넉넉할 수 있습니다.
특히 외부 API나 LLM 호출이 뒤에 붙어 있다면 rate limit은 장애 방어뿐 아니라 비용 통제 장치이기도 합니다.
클라이언트 경험까지 설계한다
429가 자주 발생하는 API는 클라이언트가 어떻게 대응해야 하는지도 문서화해야 합니다. 단순히 재시도하라고만 쓰면 여러 클라이언트가 동시에 다시 몰릴 수 있습니다.
- Retry-After를 존중한다
- exponential backoff와 jitter를 사용한다
- 동일 요청을 무한 재시도하지 않는다
- 사용자에게 남은 대기 시간을 표시한다
- idempotent 요청과 non-idempotent 요청을 구분한다
운영 지표
rate limit은 배포 후 지표를 봐야 합니다. 너무 느슨하면 보호 효과가 없고, 너무 엄격하면 정상 사용자가 막힙니다.
- 429 발생률
- 사용자/API key별 차단 분포
- 특정 route의 차단 비율
- Retry-After 이후 성공률
- 상위 차단 원인의 정상/비정상 여부
정리
429 Too Many Requests는 API를 거칠게 막는 버튼이 아닙니다. 누구를, 어떤 기준으로, 얼마나, 언제까지 제한할지 정하는 설계입니다.
좋은 rate limit은 서버를 보호하면서도 클라이언트가 다음 행동을 알 수 있게 합니다. 장애와 재시도 흐름은 Outbox pattern 글처럼 메시지 신뢰성 문제와도 이어집니다. 상태 코드 기준은 RFC 6585의 429 정의, 재시도 안내는 Retry-After 헤더 정의를 기준으로 확인할 수 있습니다.