React 요청률 제한 가이드
요청률 제한은 시간 창 안에서 설정된 횟수만큼 실행을 허용합니다. 용량이 남아 있는 동안 호출은 즉시 실행됩니다. 제한에 도달하면 용량이 다시 생길 때까지 이후 호출이 거부됩니다.
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 = useRateLimiter(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 = useRateLimiter(sendEvent, {
limit: 3,
window: 1000,
windowType: 'sliding',
})
용량을 한꺼번에 복원하지 않고 점진적으로 복원해야 한다면 슬라이딩 시간 창을 사용합니다.
API 선택하기
- 할당량이 제어되는 이벤트 핸들러가 필요하면
useRateLimitedCallback - React 상태가 필요하면
useRateLimitedState또는useRateLimitedValue - 용량 헬퍼와 선택한 상태가 필요하면
useRateLimiter
작업에는 콜백 API를, 할당량이 제어되는 UI 업데이트에는 상태 또는 값 API를 사용합니다. 용량 헬퍼나 거부 상태가 필요하면 인스턴스 API를 사용합니다.
React 예제
import { useRateLimiter } from '@tanstack/react-pacer'
function SendButton() {
const limiter = useRateLimiter(
sendEvent,
{ limit: 3, window: 10_000 },
(state) => ({
rejectionCount: state.rejectionCount,
}),
)
return (
<button onClick={() => limiter.maybeExecute('clicked')}>
Send ({limiter.state.rejectionCount} rejected)
</button>
)
}
이 가이드에서 이후에 다루는 핵심 코드 조각은 useRateLimiter를 사용하며 컴포넌트나 다른 훅 내부에서 실행한다고 가정합니다.
거부된 호출 처리하기
거부된 호출은 나중에도 실행되지 않습니다. 불리언 반환 값이나 onReject를 사용해 피드백을 제공하거나 다른 곳에서 재시도하거나 작업을 큐에 넣습니다.
const limiter = useRateLimiter(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 = useRateLimiter(sendEvent, {
enabled: (limiter) => limiter.store.state.executionCount < 100,
limit: (limiter) => (limiter.store.state.rejectionCount > 10 ? 2 : 5),
window: 60_000,
})
제한기를 비활성화하면 래핑된 함수가 실행되지 않습니다. 기존 실행 기록은 삭제하지 않습니다.
실행 관찰하기
onExecute는 실행된 인수와 제한기 인스턴스를 받습니다. onReject는 제한기 인스턴스를 받습니다.
const limiter = useRateLimiter(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)
},
})
React 수명 주기
동기 제한기에는 대기 중이거나 활성 상태인 작업이 없으므로 어댑터에는 기본 작업 정리가 없습니다. 컴포넌트에서 제한기와 관련된 사용자 정의 정리가 필요할 때만 onUnmount를 사용합니다.
반응형 상태
어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. 컴포넌트나 다른 훅의 최상위 수준에서 유틸리티를 만들고 뷰에서 사용하는 필드만 선택합니다.
const limiter = useRateLimiter(
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'중 하나입니다.
어댑터 시그니처는 React API 레퍼런스를, 전체 옵션 및 상태 타입은 공개 코어 레퍼런스를 참고합니다.