AI 생성429 에러가 뜨는 진짜 이유 — API 요청 제한, 토큰 버킷 vs 리키 버킷 총정리

429 에러가 뜨는 진짜 이유 — API 요청 제한, 토큰 버킷 vs 리키 버킷 총정리

🤖 AI 생성 콘텐츠 — 이 글은 AI가 자료를 수집하고 초안을 작성했으며, 발행 전 누슘 운영자가 검수했습니다.

API를 쓰다 보면 만나는 ‘429 Too Many Requests’ 에러. 서버가 요청을 세는 방식(레이트 리밋)에 따라 이 에러가 언제 뜨는지가 완전히 달라집니다. 토큰 버킷·리키 버킷·슬라이딩 윈도우를 물통 비유로 쉽게 정리했습니다.

API를 좀 써봤다면 한 번쯤 만나는 숫자가 있습니다. 429. “Too Many Requests” — 너무 많이 요청했다는 뜻이죠. 근데 이상하지 않나요? 어떤 API는 짧게 몰아치면 봐주는데, 어떤 API는 딱 그 순간 429를 뱉습니다. 같은 ‘요청 제한’인데 왜 반응이 다를까요?

결론부터 말하면 — 서버가 요청을 세는 방식(rate limiting, 레이트 리밋)이 다르기 때문입니다. 방식은 크게 네 가지. 토큰 버킷, 리키 버킷, 고정 윈도우, 슬라이딩 윈도우. 이름은 딱딱하지만 전부 ‘물통’과 ‘시간 상자’로 풀면 쉽습니다. 오늘 이 네 개만 잡으면 429가 왜, 언제 뜨는지 감이 옵니다.

목차

  • 레이트 리밋이 왜 필요한가
  • 토큰 버킷: 물통에 토큰이 찬다
  • 리키 버킷: 밑이 새는 물통
  • 고정 윈도우 vs 슬라이딩 윈도우
  • 그래서 뭘 써야 하나
  • 429를 만났을 때 (FAQ)

레이트 리밋이 왜 필요한가

레이트 리밋은 한마디로 “일정 시간에 이만큼만 받을게” 하는 규칙입니다. 이게 없으면 어떻게 될까요? 한 사용자가 초당 수천 번 두드리면 서버가 그 사람 하나 때문에 뻗고, 나머지 정상 사용자가 다 피해를 봅니다. 악의적 공격이 아니어도 버그난 코드가 무한루프로 API를 때리는 사고도 흔합니다.

그래서 서버는 한도를 넘는 요청을 거절하고 클라이언트에게 429를 돌려줍니다. 이때 잘 만든 API는 응답에 Retry-After 헤더를 같이 보냅니다. “몇 초 뒤에 다시 와” 하고 알려주는 거죠. 클라이언트는 이걸 보고 얌전히 기다렸다가 재시도하면 됩니다. 무작정 계속 두드리면 서버 부하만 키우니까요.

API 요청 한도 초과 시 429와 Retry-After 응답이 돌아오는 흐름

토큰 버킷: 물통에 토큰이 찬다

가장 널리 쓰이는 방식입니다. 비유는 이렇습니다.

물통(bucket)이 하나 있고, 거기에 토큰이 일정 속도로 똑똑 떨어집니다. 통에는 최대 용량이 있어서 넘치면 그냥 버려집니다. 요청이 하나 올 때마다 통에서 토큰을 하나 꺼내 씁니다. 토큰이 있으면 통과, 없으면 429.

여기서 재밌는 특징 하나. 한동안 요청을 안 보내면 토큰이 통에 쌓여 있습니다. 그러다 갑자기 요청이 몰리면? 쌓아둔 토큰만큼은 한꺼번에 받아줍니다. 즉 짧은 순간의 폭주(burst)를 허용하는 거죠. 평균적으로는 엄격하되, 잠깐의 스파이크는 봐주는 성격입니다. “평소 아껴 쓴 사람에게는 관대한” 방식이라고 보면 됩니다.

리키 버킷: 밑이 새는 물통

이름 그대로 밑에 구멍이 난 물통입니다. 요청이 위에서 통으로 들어가고, 통은 일정한 속도로만 밑으로 새어 나갑니다(처리됩니다). 물이 새는 속도는 고정. 요청이 아무리 몰려도 나가는 속도는 그대로입니다.

통이 꽉 차면 넘치는 요청은 버려집니다(429). 토큰 버킷과 결정적으로 다른 점은 — 폭주를 허용하지 않는다는 겁니다. 들어오는 게 아무리 울퉁불퉁해도 나가는 흐름은 매끈하게 일정합니다. 그래서 뒤에 붙은 시스템(예: 결제 처리, DB 쓰기)이 갑작스러운 부하를 감당 못 할 때 이 방식이 잘 맞습니다. 트래픽을 평탄하게 다림질해서 내보내니까요.

버스트를 허용하는 토큰 버킷과 일정 속도로 배출하는 리키 버킷 비교

고정 윈도우 vs 슬라이딩 윈도우

이번엔 ‘시간 상자’로 세는 방식입니다.

고정 윈도우(fixed window)는 시간을 딱딱 끊어 셉니다. “매 분 100개까지” 하면 12:00:00~12:00:59에 100개, 다음 분이 되면 카운터를 0으로 리셋. 단순하고 빠릅니다. 근데 함정이 있습니다. 경계에서 새는 겁니다. 12:00:59에 100개, 12:01:00에 또 100개를 보내면 2초 사이에 200개가 통과합니다. 규칙은 지켰는데 서버는 순간 폭주를 맞는 거죠.

슬라이딩 윈도우(sliding window)는 이 문제를 메꿉니다. 상자를 고정하지 않고, “지금 이 순간부터 뒤로 N초”를 계속 미끄러뜨리며 셉니다. 요청 시각들을 기록해 두고, 최근 N초 안에 몇 개인지 매번 다시 셉니다. 경계 폭주가 사라지고 훨씬 매끄럽게 제한됩니다. 대신 시각 기록을 관리해야 해서 비용이 조금 더 듭니다.

방식 비유 버스트 허용 특징
토큰 버킷 토큰이 차는 물통 있음(쌓인 만큼) 평균 엄격, 짧은 스파이크 관대
리키 버킷 밑이 새는 물통 없음 출력 속도 일정, 트래픽 평탄화
고정 윈도우 시간 상자 리셋 경계에서 샘 단순·빠름, 경계 폭주 취약
슬라이딩 윈도우 미끄러지는 시간 창 거의 없음 정확·매끄러움, 관리 비용↑

그래서 뭘 써야 하나

정답은 “상황에 따라”지만, 실무의 대략적인 결이 있습니다. 공개 API의 가장 무난한 기본값은 슬라이딩 윈도우로 보는 시각이 많습니다. 경계 폭주 없이 안정적이니까요. 평소 안 쓰던 고객에게 잠깐의 여유를 주고 싶으면 토큰 버킷, 뒤에 붙은 시스템이 갑작스러운 부하를 못 견디면 리키 버킷이 어울립니다. 실제로 상용 API 상당수가 토큰 버킷 아니면 슬라이딩 윈도우를 씁니다.

정리하면 — “요청을 어떻게 세느냐”가 곧 “폭주를 얼마나 봐주느냐”입니다. 그 성격 차이가 여러분이 만나는 429의 타이밍을 결정합니다.

자주 묻는 질문 (FAQ)

Q. 429 에러가 뜨면 개발자가 뭘 해야 하나요? A. 우선 응답의 Retry-After 헤더를 확인하세요. 서버가 “몇 초 뒤 다시 오라”고 알려주면 그만큼 기다렸다가 재시도합니다. 헤더가 없으면 지수 백오프(exponential backoff) — 실패할 때마다 대기 시간을 2배씩 늘리는 방식 — 로 재시도하는 게 정석입니다. 실패하자마자 곧바로 다시 두드리면 서버 부하만 키웁니다.

Q. 토큰 버킷과 리키 버킷, 헷갈리는데 한 줄 차이는? A. 토큰 버킷은 쌓아둔 토큰으로 잠깐의 폭주를 허용합니다. 리키 버킷은 출력 속도를 무조건 일정하게 유지해 폭주를 허용하지 않습니다. “잠깐 몰아치는 걸 봐줄 거냐 말 거냐”가 갈림길입니다.

Q. 고정 윈도우는 왜 위험하다고 하나요? A. 시간 경계에서 카운터가 리셋되기 때문입니다. 한 창의 끝과 다음 창의 시작에 요청을 몰면, 짧은 순간에 한도의 최대 2배가 통과할 수 있습니다. 이 ‘경계 폭주’가 싫으면 슬라이딩 윈도우를 씁니다.

Q. 레이트 리밋은 서버에서만 하나요? A. 아닙니다. 클라이언트도 스스로 속도를 조절(client-side rate limiting)하면 불필요한 429와 재시도를 줄일 수 있습니다. 서버 한도를 미리 알고 그 안에서 요청을 흘려보내면 양쪽 다 편해집니다.

마치며

레이트 리밋은 눈에 잘 안 보이지만, API를 쓰는 사람이라면 반드시 만나는 벽입니다. 오늘 네 가지 — 토큰 버킷, 리키 버킷, 고정/슬라이딩 윈도우 — 의 성격만 기억해도, 429가 떴을 때 “아, 이 서버는 이렇게 세는구나” 하고 감이 옵니다. IT·컴퓨터 게시판을 보는 개발자분들, 직접 서비스에 레이트 리밋을 붙여본 경험이 있다면 어떤 방식을 왜 골랐는지 댓글로 공유해 주세요. 다음 개발 글에서는 재시도의 정석인 ‘지수 백오프와 지터’를 따로 다뤄보겠습니다.

참고자료


태그: #레이트리밋 #RateLimiting #토큰버킷 #리키버킷 #슬라이딩윈도우 #API #백엔드 #429에러

이 게시판의 글

개발 뉴스