이 글에서 얻는 것

  • Rate limit(할당량 제한)과 Backpressure(압력 전달/부하 제어)를 구분하고, “어디에 무엇을 걸어야 하는지” 설계할 수 있습니다.
  • 토큰 버킷/슬라이딩 윈도우 같은 알고리즘을 “정답”이 아니라 **트래픽 특성(버스트/공정성/정밀도)**에 따라 선택할 수 있습니다.
  • 리트라이/서킷 브레이커/큐/스레드풀과 결합될 때 생기는 함정(증폭, 대기열 폭발)을 피하는 기준이 생깁니다.

0) Rate limit과 Backpressure는 목적이 다르다

  • Rate limit: “누가 얼마나 요청할 수 있는지”를 제한해 공정성/남용 방지/비용 통제를 한다
  • Backpressure: “시스템이 처리할 수 있는 만큼만 받도록” 압력을 전달해 붕괴를 막는다(대기열/타임아웃 폭발 방지)

둘을 섞어 쓰면 정책이 꼬이기 쉽습니다. 예를 들어, 대기열이 길어졌을 때는 rate limit이 아니라 load shedding(부하 차단)이 더 적절할 수 있습니다.

1) 어디에 걸까: Edge(Gateway) vs App vs Downstream

1-1) Gateway(엣지) 레벨

좋은 점:

  • 공격/남용 트래픽을 “애플리케이션에 도달하기 전에” 자를 수 있음
  • 전 서비스 공통 정책(사용자별/키별/테넌트별)을 일관되게 적용하기 쉬움

주의:

  • “서비스마다 다른 처리 비용”을 반영하기 어렵습니다(요청 1건이 다 같은 비용이 아님).

1-2) 애플리케이션 레벨

좋은 점:

  • 엔드포인트/기능별로 “비용 기반 제한”이 가능(예: 파일 업로드/검색/정산)
  • 내부 리소스(DB 커넥션/스레드/큐) 상태를 근거로 load shedding 가능

주의:

  • 분산 환경에서 공유 상태(카운터/버킷)를 어떻게 유지할지 고민이 필요합니다.

1-3) 다운스트림(의존성) 보호

DB/외부 API 같은 의존성을 보호하려면:

  • 서킷 브레이커(장애 전파 차단)
  • bulkhead(풀/세마포어로 격리)
  • 제한된 리트라이(백오프, jitter)

같은 패턴이 함께 필요합니다.

2) 알고리즘 선택 감각(버스트/공정성/정확도)

  • Fixed window: 구현이 쉽지만 경계에서 버스트가 발생하기 쉬움
  • Sliding window: 더 공정하지만 구현/비용이 증가
  • Token bucket: 버스트 허용 + 평균 속도 제한(실무에서 자주 씀)
  • Leaky bucket: 일정 속도로 “배출”해 트래픽을 평탄화(서버 보호에 유리)

중요한 질문:

  • 버스트를 허용해야 하는가(로그인/결제처럼 순간 몰림이 정상인 기능)?
  • 공정성이 중요한가(테넌트 간 공정 분배)?
  • 정확도(정밀)보다 “서비스 보호”가 우선인가?

3) Backpressure를 구현하는 실무 패턴

3-1) 큐/대기열 길이 제한

대기열이 무한이면 결국 타임아웃과 메모리로 터집니다.

  • 큐 길이를 제한하고,
  • 초과하면 즉시 실패(429/503)하거나,
  • 낮은 우선순위 요청을 드롭하는(load shedding) 전략을 택합니다.

3-2) 세마포어/스레드풀로 동시 처리량 제한(bulkhead)

특정 기능이 시스템 전체를 잡아먹지 않게 격리합니다.

  • “검색”이 느려져도 “주문”은 살아 있어야 한다
  • 외부 API 호출은 별도 풀로 격리한다

3-3) 응답 설계: 429 vs 503, Retry-After

  • Rate limit은 보통 429(Too Many Requests) + Retry-After가 어울립니다.
  • 시스템이 이미 포화면 503(Service Unavailable)가 더 정직할 수 있습니다(즉시 드롭).

중요한 건 “클라이언트 재시도 정책”까지 포함해 설계하는 것입니다.

4) 가장 위험한 조합: 무제한 리트라이 + 느슨한 제한

실무에서 터지는 패턴:

  • 서버가 느려짐 → 타임아웃 증가 → 클라이언트/SDK가 재시도 → 트래픽이 더 늘어남(증폭)

방지:

  • 재시도는 꼭 필요한 경우에만(멱등 요청 중심)
  • 백오프 + jitter
  • retry budget(재시도 총량 제한)
  • 서킷 브레이커로 “이 구간은 지금 실패”를 빠르게 결정

5) 모니터링 지표(최소 세트)

  • rate limit 차단율(전체/사용자/테넌트/엔드포인트)
  • 대기열 길이/처리 대기 시간
  • p95/p99 레이턴시와 타임아웃 비율
  • 의존성(DB/외부 API) 오류율/지연, 서킷 오픈 비율

연습(추천)

  • “동일 트래픽인데 어떤 엔드포인트는 훨씬 비싸다”는 가정을 두고, 엔드포인트별 제한 정책을 설계해보기
  • 429/503을 각각 언제 반환할지 기준을 정하고, 클라이언트 재시도 규칙까지 문서로 써보기
  • 대기열 길이를 제한했을 때(즉시 드롭) vs 제한하지 않았을 때(타임아웃 증가) 사용자 경험/운영 비용이 어떻게 달라지는지 비교해보기