처리율 제한은 왜 필요한가?
누가 당신의 서버리스 서버에 초당 1000개의 요청을 보낸다면? 요청 단위로 요금 나올텐데? 심지어 ai api 요청을 무료로 풀었었다면?(ai api provider 단에서 막힐 수도 있음 이쪽도 429 error 너무 잘낸다…) 그냥 돈 빠져나갑니다~
보통 DoS 공격을 막고, 리소스 사용량을 조절하기 위해 사용한다.
제한 장치의 위치
가장 쉽게 ‘클라이언트 - 서버’ 라고 생각하자. 그러면 선택지는 2개 뿐이다. 클라이언트에 두거나 아니면 서버에서 처리하거나.
하지만 선택지는 하나일 것이다 결국 서버.
클라이언트는 위변조가 너무 쉬워서 신뢰하기 어렵다. 그러니 서버에서 처리하는 게 대부분이다.
미들웨어
서버라고 해도 그냥 서버에서 처리한다가 아니라 api gateway라고 불리는 미들웨어에서 처리한다.
요즘은 호스팅 서비스에서 지원하는 경우도 많긴하지만…
상용 API Gateway를 쓰는 것이 아마 더 나은 선택일 것이다. 왜냐면 인력리소스나 여러 관점에서 직접 구현하는 것보다 이득일 가능성이 높기 때문이다.
알고리즘
알고리즘은 단순한 편이다. 하지만 이것이 분산처리 환경에서 까다로울 뿐…
토큰 버킷
일정 시간마다 버킷에 토큰을 채우고 해당 토큰이 남아 있으면 allow 아니면 429 too much 에러 처리
인자는 다음과 같다.
- bucket size: 버킷에 담을 수 있는 최대 토큰 수
- refill rate: 초당 토큰 공급률
두 인자를 적절히 설정하는 것이 관건..

누출 버킷
토큰 버킷과 비슷하지만 다른점은 고정된 속도로 토큰이 빠져나간다는 점이다. 버킷에 들어온 작업은 큐에 들어가게 되고 큐에 있는 작업은 고정된 속도로 처리된다. 만약 큐에 버킷이 꽉 차면 429 error 처리.
인자:
- bucket size: 큐 사이즈
- overflow rate: 지정된 시간에 몇개 작업 처리할지
단점:
- 단시간에 많이 몰려 큐가 꽉차면 최신 요청들은 byebye

고정 윈도 카운터
특정 시간(duration)을 윈도우로 해서 지정하고 해당 윈도우 동안 request counting 해. request 횟수가 rate limit count를 넘지 않으면 통과 아니면 429 error 발생

단점:
- rate count가 5라 가정했을 때, 직전 윈도우의 마지막 20초에 3개의 요청 그리고 현재 윈도우의 30초 동안 5개의 요청이 들어온다면 50초 동안 총 8개의 요청을 받은게 되어 edge of window에서 요청량이 spike됨

이동 윈도 로깅
고정 윈도 카운터의 단점 보완.
현재 들어온 로그의 타임스탬프를 기준으로 특정 시간내에 몇개 요청 왔는 지 파악. rate limit 을 넘지 않았다면 통과, 아니라면 disable.
새로운 요청이 왔는데 기록하는 시간 이전에 것이 남아 있다? 측정에 필요 없는 것은 로그에서 삭제함.

단점:
- 로깅으로 인한 메모리 사용량 높음.
이동 윈도 카운터
고정 윈도 카운터와 이동 윈도 로깅을 합친 것.
계산법: 현재 윈도우의 요청 수 + 이전 윈도우의 요청 수 * (이동 윈도우와 직전 윈도우가 겹치는 비율)

반올림 시 7이므로 책 예제에서는 불통과(그림에는 5라고 나오는 데 통과되었다고 적힌거 보면 오타인듯)
장점:
- 몰리는 시간에도 제한 가능
- 로깅보다 메모리 사용량 적음
단점:
- 직전 윈도우가 고르게 분포되어 있다 가정하에 한 것이라 다소 느슨함 cloudflare는 40억개 요청중 의도와 맞지 않은게 0.003% 정도라고 한다.1
계략적 알고리즘
rate limit 을 확인 하는 알고리즘은 비교적 쉬운편이다. 하지만 우리는 어떤 기준으로 rate limit 을 정할 것이고 rate limit 에 대한 정보는 어디에 저장할 것인가?
간단하게 token bucket 의 경우는? 만약 postgres와 같은 sql database에 저장하게 된다면 별로 좋지 않은 선택일 것이다. rate limit 은 자주 발생하기에 disk에 저장하는 것은 속도에서 좋은 선택이 아니다.
redis와 같은 캐시를 아마도 이용할 것이다. 명령어
- INCR : 카운터 증가
- EXPIRE : 카운트에 타임아웃 설정. 만료 시 삭제
만약 rate limit을 초과하는 request가 들어온다 그러면 바로 429 Error 처리하고 끝낼 것인가?
쇼핑 결제와 같은 상황에서는 에러를 보내기 보다는 메시지큐에 대기하여 순차적으로 처리하는 것이 좋은 방법일 수 있다.
분산 처리 환경
서버가 한개라면 쉽지만 여러개의 서버와 여러개의 rate limit middleware가 있다면 얘기는 달라진다.
보통 두가지 이슈를 맞이한다.
- Race Condition
- Synchronization(동기화)
Race Condition
빠른 요청으로 인해 값이 수정되지 않고 값을 가져와 문제가 생긴다

보통은 Lock을 사용하는 데 Lock의 경우 성능이 급격하게 줄어든다고 한다(추후의 테스트하고 포스팅하겠습니다)
하지만 우리는 Redis를 위에서 쓴다 가졍했으니 Redis로 이를 해결할 수 있다.
방법 1. Lua script
Redis에서 Lua script를 사용하면 원자성을 보장해준다.
그래서 Redis에서 Lua script를 통해 다른 작업들을 blocking하고 실행하므로 Lua script로 해결가능하다
아래 redis 공식 문서가 있으니 확인 가능
방법 2. Redis Sorted Set(ZSET)
Redis 자료구조에서 Sorted Set(ZSET)을 사용하면 또 동시성 문제를(Race condition)을 해결할 수 있다.
그런데 위 블로그 글에서는 Lua script를 사용할 수 있는 사람이 없었어서 Redis의 MULTI 명령어를 활용해 아래와 같은 방식으로 해결했다.
- 이전 윈도우 작업 로그 제거 - clean up
- 집합 요소 가져오기
- 현재 작업을 집합에 추가 -
ZADD - 작업 완료 후, 요소 개수 계산 만약 초과시 작업 허용 x -
ZCARD
위 방식의 단점은 저자가 언급했듯이 작업이 실패해도 작업이 하나 더 추가되는 것임.
Synchronization(동기화)
분산 처리 환경에서는 하나의 처리율 제한 장치만으로 부족할 수 있다. 그렇기에 여러 처리율 제한 장치가 있을 수 있는데, 이때 A 클라이언트가 C 처리율 제한 장치를 거쳤었고, B 클라이언트가 D 처리율 제한 장치를 거쳤었다 가정하자.
그런데 다음 요청에서부터는 A 클라이언트가 D 처리율 제한 장치를 거쳤고, B 클라이언트가 C 처리율 제한 장치를 거친다면 어떡할 까?
웹 통신은 stateless 이기 때문에 클라이언트의 상태를 저장할 수 없다. 그래서 하나의 방법으로 sticky session으로 어떤 클라이언트가 어떤 처리율 제한 장치를 거쳤는지 저장할 수 있다. 하지만 이것은 유연하지 못하다
이유는 아래와 같이 생각된다
- 클라이언트 - 서버가 종속적이므로 장애 발생시 대처 어려움
- 오버헤드 증가
- 서버 확장 시 지속적인 스티키 세션 관리, 지원 필요
그래서 필자는 여러 처리율 제한 장치가 하나의 redis 인스턴스를 공유하는 방식으로 구현하는 것을 권한다.
성능 최적화
사용자가 서버에 있는 지역에만 사는가? 한국의 경우 한국 내에 어디에 서버가 있든 큰 상관이 없을 수 있다. 하지만 미국과 같이 넓은 지역에 혹은 글로벌 서비스의 경우 이용자들이 여러 지역에 분산되어 있는 경우 각 지역에(region) 서버를 분산처리 해야할 것이다.
이렇게 각 region에 분산을 하면 데이터 동기화를 위해 최종 일관성 모델(eventual consistency model)을 사용할 수 있다.
하지만 cloudflare는 다르게 접근하는 것 같다1
Anycast 방식을 통해 항상 가까운 서버에만 접속하게 해 동기화를 하지 않고 각 PoP(Point of Presence)에서 독립적으로 처리한다.
어차피 사용자들은 대부분 지역을 이동하지 않기 때문에 가까운 서버에만 접속할 것이고 결국엔 Global 한 동기화가 필요없다라는 의미인 것 같다.
그리고 DoS 공격 정도면 이미 뭐 지역내 PoP에서 rate limit 걸릴테니…
모니터링
아래 두가지 포인트에서 모니터링한다
- 처리율 제한 알고리즘이 효과적인가?
- 처리율 제한 규칙이 효과적인가?
Etc
soft vs hard limit
- soft: 잠깐정도는 제한 허용
- hard: 제한 초과시 바로 거부
계층별 처리율 제한
위의 내용에서 OSI 7계층 중 Application Layer에서만 정리했으나 iptables와 같은 방화벽을 통해 Network Layer에서 처리율 제한을 걸 수 있다.
에러 처리
단순히 429 error를 반환하는 것 외에 다른 방법이 있을까?
- 클라이언트에서 retry를 설정해서 exponetional 하게 backoff 하게 유도
- 캐시(CDN ?)
- 에러 처리 잘하기…?
뭐 여러가지 방법이 있어보인다.
Rate Limit 비교 코드
2026-02-07 업데이트
참고자료
Footnotes
February 6, 2026
