레이트 리미팅은 클라이언트가 일정 시간 동안 보낼 수 있는 요청 수에 상한을 두는 기법입니다. 목적은 남용·폭주·비용 폭발을 막고, 공정하게 자원을 배분하며, 장애 확산을 억제하는 것입니다. 잘 설계된 레이트 리밋은 단순히 '막는' 것이 아니라, 얼마나 남았고 언제 초기화되는지를 클라이언트에게 알려줘 클라이언트가 스스로 속도를 조절하게 돕는 협조적 계약입니다.
429와 Retry-After
한도를 초과한 요청에 서버는 429 Too Many Requests로 응답합니다. 이때 핵심은 Retry-After 헤더로 '언제 다시 시도하라'를 알려주는 것입니다. Retry-After는 초 단위 정수(예: Retry-After: 30)나 HTTP 날짜 형식으로 표현할 수 있습니다. 클라이언트는 이 값을 존중해 그 시간만큼 기다린 뒤 재시도해야 하며, 무시하고 즉시 재요청하면 한도가 더 늦게 풀리거나 차단이 길어질 수 있습니다.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
{"error":"rate_limited","message":"Too many requests, retry in 30s"}
RateLimit-* 헤더
매 응답(성공·실패 모두)에 남은 한도를 실어 보내면 클라이언트가 429에 도달하기 전에 미리 감속할 수 있습니다. 표준화가 진행 중인 헤더는 다음과 같습니다.
RateLimit-Limit: 해당 윈도우에서 허용되는 총 요청 수.RateLimit-Remaining: 현재 윈도우에 남은 요청 수.RateLimit-Reset: 한도가 초기화되기까지 남은 초(또는 초기화 시각).- (관례)
X-RateLimit-*접두 버전도 널리 쓰이며, 새 표준(IETF draft)은 접두 없는RateLimit-*및 구조화된RateLimit헤더로 수렴하는 중입니다.
알고리즘: 무엇을 세는가
레이트 리밋의 정확도와 부드러움은 내부 알고리즘이 결정합니다. 고정 윈도우(fixed window)는 '매 분 100회'처럼 시계 경계로 카운터를 리셋하는 가장 단순한 방식인데, 경계 직전과 직후에 요청이 몰리면 짧은 순간 한도의 두 배가 통과하는 '경계 버스트' 문제가 있습니다. 슬라이딩 윈도우(sliding window)는 현재 시점 기준 과거 N초를 계속 미끄러지듯 계산해 경계 버스트를 없애지만 계산·저장 비용이 큽니다. 토큰 버킷(token bucket)은 버킷에 일정 속도로 토큰을 채우고 요청마다 하나씩 소비하는 방식으로, 평소엔 버스트를 허용하면서도 평균 속도를 제한해 실무에서 가장 널리 쓰입니다. 비슷한 누수 버킷(leaky bucket)은 출력 속도를 일정하게 만들어 트래픽을 평탄화합니다.
클라이언트 백오프와 지터
429나 5xx를 받은 클라이언트는 지수 백오프(exponential backoff)로 재시도 간격을 점점 늘려야 합니다(1초 → 2초 → 4초 → 8초 …). 그런데 여러 클라이언트가 동시에 한도에 걸려 똑같은 백오프로 재시도하면, 동일한 순간에 다시 몰려 '천둥 소리 무리(thundering herd)' 문제가 생깁니다. 해결책은 대기 시간에 무작위성을 더하는 지터(jitter)입니다. 각자 조금씩 다른 시각에 재시도하게 되어 부하가 시간축으로 분산됩니다.
# 지수 백오프 + 풀 지터 (의사코드)
base = 1초, cap = 30초
delay = min(cap, base * 2^attempt)
sleep = random(0, delay) # 0 ~ delay 사이 무작위
# Retry-After가 있으면 그 값을 우선 존중
사용자별 vs IP별 한도
무엇을 기준으로 셀지도 중요한 설계 결정입니다. IP별 한도는 인증 전 트래픽(로그인·회원가입·공개 엔드포인트)에 유용하지만, NAT·회사망·모바일 캐리어 뒤의 수많은 사용자가 하나의 IP를 공유하면 무고한 사용자가 함께 막히고, 반대로 공격자가 IP를 바꿔가며 우회할 수도 있습니다. 사용자별(또는 API 키별) 한도는 인증된 요청에 공정하지만 인증 전에는 쓸 수 없습니다. 현실적인 설계는 이 둘을 계층으로 겹치는 것입니다 — 인증 전은 IP+엔드포인트 기준으로 느슨하게, 인증 후는 사용자/키 기준으로 엄격하게. 여기에 비용이 큰 엔드포인트에는 별도의 더 낮은 한도를 두면 자원을 세밀하게 보호할 수 있습니다.