
새벽 3시, 머리맡의 페이저듀티(PagerDuty)가 울립니다.
비몽사몽 간에 노트북을 엽니다.
또 그 놈의 429 Too Many Requests 때문입니다.
클라이언트 개발팀이 신규 배포를 하면서 API 호출 루프를 잘못 돌렸더군요.
서버는 살려야 하니 차단(Blocking)을 걸었지만, 이미 DB CPU는 튀었고 제 수면 패턴은 박살 났습니다.
"가용성(Availability) 99.999% 지키려다 내 인생 가용성이 0%가 됐다"는 농담, 웃어넘길 일이 아닙니다.
오늘은 그 악순환을 끊어낼, 꽤 흥미로운 '내부 문건'급 이야기를 해보려 합니다.
최근 IETF에서 HTTP RateLimit 헤더를 표준화하려는 움직임이 있습니다.
Tony Finch라는 엔지니어가 쓴 분석글을 보셨나요?
핵심은 간단합니다.
"무지성으로 차단만 하지 말고, 클라이언트한테 '언제 다시 올지' 알려주자."
근데 여기서 함정이 있습니다.
대부분의 엔지니어들이 사용하는 'Quota-Reset' 방식 때문입니다.
보통 이렇게 짜죠?
"1분에 60회 허용. 00초에 카운터 리셋."
이 방식의 문제가 뭘까요?
클라이언트가 59초에 60번을 쏘고, 00초 되자마자 다시 60번을 쏏니다.
순간적으로 트래픽 폭주(Burst)가 발생합니다.
서버 입장에선 '폭주 -> 멍 때림 -> 폭주'의 반복이죠.
마치 월급 받자마자 탕진하고, 월말까지 굶는 직장인 같습니다.
이런 트래픽 패턴은 인프라 리소스 낭비의 주범입니다.

그래서 제안하는 게 선형 속도 제한(Linear Rate Limit) 알고리즘입니다.
기술적으로는 GCRA(Generic Cell Rate Algorithm)의 변형인데, 이름만 어렵지 원리는 단순합니다.
복잡한 '토큰 버킷'이나 '누수 버킷' 같은 거 필요 없습니다.
딱 하나, 'Not-Before' 타임스탬프만 기억하세요.
로직은 이렇습니다.
- 클라이언트별로
time변수(Not-Before) 하나만 저장합니다. - 요청이 올 때마다 이
time을 미래로 조금씩 밉니다. (예: 요청 1개당 0.1초) - 만약
time이 현재 시간(now)보다 미래에 있다? - 그 차이만큼 기다리라고 하고 요청을 거부(Deny)합니다.
이렇게 하면 클라이언트가 몰아서 요청을 보내려 할 때,
서버는 유효 윈도우(Window)를 줄여버립니다.
결과적으로 클라이언트가 요청 간격을 일정하게(Uniformly) 유지하도록 강제합니다.
트래픽을 예쁘게 다림질하는 Traffic Shaping 효과가 생기는 거죠.
IETF 초안에는 두 가지 헤더가 등장합니다.
1. RateLimit-Policy
서버의 정책을 선언합니다.
"우리는 60초에 100건까지만 받는다(w=60, q=100)."
2. RateLimit
현재 요청에 대한 결과입니다.
"너 지금 너무 빨라. 5초 뒤에 다시 와(t=5)."
여기서 중요한 건 RateLimit 헤더가 던져주는 숫자들입니다.
기존 방식은 "너 쿼터 다 썼어, 리셋될 때까지 기다려"였다면,
선형 알고리즘은 "너 템포가 너무 빨라, 조금만 천천히 걸어"라고 말해주는 겁니다.
이 방식의 장점은 명확합니다.
첫째, 메모리를 덜 먹습니다.
클라이언트마다 무거운 상태값(State)을 들고 있을 필요가 없습니다. 타임스탬프 정수 하나면 됩니다. Redis 비용 아끼는 소리 들리시죠?
둘째, 로직이 단순합니다.
clamp() 함수 하나로 시계가 튀는 문제(Clock Skew)나 악성 클라이언트의 공격까지 방어할 수 있습니다.
셋째, 새벽 호출이 줄어듭니다.
트래픽이 튀지 않으니 오토스케일링(Auto-Scaling)이 널뛰기할 일도 줄어듭니다.
물론, 클라이언트 개발자들은 싫어할 수도 있습니다.
"그냥 쏘게 놔두지 왜 귀찮게 하냐"고요.
그럴 땐 조용히 페이저듀티 호출 기록을 보여주세요.
"시스템이 죽으면, 당신네 API도 죽습니다."
엔지니어링의 정수는 화려한 신기술 도입이 아닙니다.
이런 투박한 알고리즘 하나를 바꿔서 시스템의 안정성을 확보하는 것.
그게 진짜 실력입니다.
오늘 당장 여러분의 API 게이트웨이를 점검해 보세요.
혹시 '무지성 카운터'로 트래픽 폭주를 방치하고 있진 않은지.
여러분의 퇴근 시간은 소중하니까요.


