본문으로 건너뛰기

Angular 요청률 제한 가이드

요청률 제한은 시간 창 안에서 설정된 횟수만큼 실행을 허용합니다. 용량이 남아 있는 동안 호출은 즉시 실행됩니다. 제한에 도달하면 용량이 다시 생길 때까지 이후 호출이 거부됩니다.

TanStack Pacer는 주로 클라이언트 측 작업을 위한 인메모리 요청률 제한기를 제공합니다. 서버 측 JavaScript에서도 실행할 수 있지만 분산 할당량 또는 강제 적용 시스템은 아닙니다.

요청률 제한 작동 방식​

다음 예시는 창마다 세 번의 실행을 허용합니다.

Rate Limiting (limit: 3 calls per window)
Timeline: [1 second per tick]
Window 1 | Window 2
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ❌ ✅ ✅
[=== 3 allowed ===][=== blocked until reset ===][=== new window ===]

요청률 제한은 버스트를 허용합니다. 수락된 호출 사이에 일정한 간격을 두지는 않습니다.

요청률 제한 사용 시점​

다음과 같은 경우 요청률 제한을 선택합니다.

  • 클라이언트 작업이 고정 할당량을 따라야 하는 경우
  • 할당량이 소진될 때까지 호출을 즉시 실행해도 되는 경우
  • 거부된 호출을 버리거나 별도로 처리해도 되는 경우
  • 남은 용량이나 용량이 돌아올 때까지의 시간을 측정해야 하는 경우

다음과 같은 경우 다른 유틸리티를 선택합니다.

  • 실행 사이에 일정한 간격이 필요하면 스로틀을 사용합니다.
  • 마지막 호출만 중요한 경우 디바운스을 사용합니다.
  • 모든 작업을 결국 실행해야 하는 경우 큐을 사용합니다.
  • 여러 항목을 함께 실행해야 하는 경우 배칭을 사용합니다.
  • Promise 결과, 재시도 또는 중단 지원이 필요하면 비동기 요청률 제한을 사용합니다.

창 유형​

windowType 옵션은 용량이 돌아오는 시점을 제어합니다.

고정 창​

고정 창은 첫 실행이 수락될 때 시작합니다. 수락된 모든 실행은 해당 창이 끝날 때까지 횟수에 포함됩니다. 이후 용량이 한꺼번에 초기화됩니다.

const limiter = injectRateLimiter(sendEvent, {
limit: 3,
window: 1000,
windowType: 'fixed',
})

고정 창은 창이 초기화될 때 전체 할당량을 다시 사용할 수 있으므로 경계 부근에서 버스트를 허용할 수 있습니다.

이동 창​

이동 창은 수락된 실행을 개별적으로 추적합니다. 오래된 타임스탬프가 창을 벗어날 때마다 한 번의 실행 용량이 돌아옵니다.

Sliding Window (limit: 3 calls per window)
Timeline: [1 second per tick]
Calls: ⬇️ ⬇️ ⬇️ ⬇️ ⬇️
Executed: ✅ ✅ ✅ ❌ ✅
[=== full ===][oldest execution expires][=== one available ===]
const limiter = injectRateLimiter(sendEvent, {
limit: 3,
window: 1000,
windowType: 'sliding',
})

용량이 한꺼번에 돌아오지 않고 점진적으로 돌아와야 한다면 이동 창을 사용합니다.

API 선택하기​

  • 할당량으로 제어되는 핸들러에는 injectRateLimitedCallback을 사용합니다.
  • 시그널에는 injectRateLimitedSignal 또는 injectRateLimitedValue를 사용합니다.
  • 용량 헬퍼와 선택된 상태가 필요하면 injectRateLimiter를 사용합니다.

작업에는 콜백 API를, 할당량으로 제어되는 UI 업데이트에는 상태 또는 값 API를 사용합니다. 용량 헬퍼나 거부 상태가 필요하면 인스턴스 API를 사용합니다.

Angular 예시​

import {
injectRateLimitedCallback,
injectRateLimiter,
} from '@tanstack/angular-pacer'

export class SendComponent {
readonly send = injectRateLimitedCallback(sendEvent, {
limit: 3,
window: 10_000,
})
readonly limiter = injectRateLimiter(
sendEvent,
{ limit: 3, window: 10_000 },
(state) => ({
rejectionCount: state.rejectionCount,
}),
)
}

이 가이드의 이후 핵심 코드 조각은 injectRateLimiter을 사용하며 Angular 주입 컨텍스트 안에서 실행된다고 가정합니다.

거부된 호출 처리하기​

거부된 호출은 나중에 실행되지 않습니다. 불리언 반환값이나 onReject를 사용해 피드백을 제공하거나 다른 곳에서 재시도하거나 작업을 큐에 넣습니다.

const limiter = injectRateLimiter(sendEvent, {
limit: 2,
window: 1000,
onReject: (limiter) => {
console.log('Rejected calls:', limiter.store.state.rejectionCount)
},
})

거부된 작업을 결국 실행해야 한다면 일반적으로 큐어가 더 적합합니다.

용량 확인하기​

인스턴스 API는 두 가지 계산 헬퍼를 제공합니다.

limiter.getRemainingInWindow() // Accepted executions still available.
limiter.getMsUntilNextWindow() // Time until at least one execution is available.

두 헬퍼 모두 현재 limit, window, windowType과 실행 기록을 사용합니다.

제한기 재설정 및 구성하기​

reset()은 실행 타임스탬프, 카운터, 정리 타이머를 지웁니다. 다음 호출은 전체 용량으로 시작합니다.

limiter.reset()

설정을 업데이트하려면 setOptions()를 사용합니다.

limiter.setOptions({
limit: 10,
window: 30_000,
})

옵션을 변경해도 기존 실행 기록은 지워지지 않습니다. 새 구성이 새로운 창으로 시작해야 한다면 reset()을 호출합니다.

enabled, limit, window 옵션에는 제한기 인스턴스를 받는 함수를 사용할 수도 있습니다.

const limiter = injectRateLimiter(sendEvent, {
enabled: (limiter) => limiter.store.state.executionCount < 100,
limit: (limiter) => (limiter.store.state.rejectionCount > 10 ? 2 : 5),
window: 60_000,
})

제한기를 비활성화하면 래핑된 함수가 실행되지 않습니다. 기존 실행 기록은 삭제되지 않습니다.

실행 관찰하기​

onExecute는 실행된 인수와 제한기 인스턴스를 받습니다. onReject는 제한기 인스턴스를 받습니다.

const limiter = injectRateLimiter(sendEvent, {
limit: 5,
window: 1000,
onExecute: (args, limiter) => {
console.log('Sent:', args)
console.log('Remaining:', limiter.getRemainingInWindow())
},
onReject: (limiter) => {
console.log('Rejected:', limiter.store.state.rejectionCount)
},
})

Angular 수명 주기​

동기 제한기에는 대기 중이거나 활성 상태인 작업이 없으므로 어댑터에 기본 작업 정리 동작이 없습니다. 컴포넌트에서 제한기와 관련된 사용자 지정 정리가 필요할 때만 onUnmount를 사용합니다.

반응형 상태​

어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. 일반적으로 컴포넌트나 서비스의 필드 초기화 구문과 같은 Angular 주입 컨텍스트에서 유틸리티를 생성하고 뷰에서 사용하는 필드만 선택합니다.

const limiter = injectRateLimiter(
sendEvent,
{ limit: 5, window: 60_000 },
(state) => ({
isExceeded: state.isExceeded,
rejectionCount: state.rejectionCount,
}),
)

console.log(limiter.state().isExceeded, limiter.state().rejectionCount)

옵션 함수와 수명 주기 콜백은 기반 공개 유틸리티 인스턴스를 받습니다. 위 예제처럼 해당 콜백 안에서 .store.state를 읽는 방식을 지원합니다. 렌더링 코드는 여기에 나온 선택된 어댑터 상태를 읽어야 합니다.

앱에서 유지한 선택 상태를 복원하려면 initialState로 부분 스냅샷을 전달합니다. 이 스냅샷은 기본값과 병합됩니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머는 복원되지 않습니다.

일반적으로 유용한 상태에는 다음 항목이 포함됩니다.

  • executionCount: 완료된 수락 실행의 총횟수입니다.
  • executionTimes: 현재 창 계산에 사용하는 타임스탬프입니다.
  • isExceeded: 현재 제한에 도달했는지 여부입니다.
  • rejectionCount: 창이 가득 차서 거부된 호출 수입니다.
  • status: 'disabled', 'exceeded', 'idle' 중 하나입니다.

어댑터 시그니처는 Angular API 레퍼런스를, 전체 옵션과 상태 타입은 공개 핵심 레퍼런스를 참고합니다.