본문으로 건너뛰기

Solid 요청률 제한 가이드

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

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 = createRateLimiter(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 = createRateLimiter(sendEvent, {
limit: 3,
window: 1000,
windowType: 'sliding',
})

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

API 선택하기

  • 할당량으로 제어되는 값에는 createRateLimitedSignal 또는 createRateLimitedValue를 사용합니다.
  • 콜백, 용량 헬퍼, 선택된 상태에는 createRateLimiter를 사용합니다.

할당량으로 제어되는 반응형 상태에는 시그널 또는 값 API를 사용합니다. 작업, 용량 헬퍼, 거부 상태에는 createRateLimiter를 사용합니다.

Solid 예시

import { createRateLimiter } from '@tanstack/solid-pacer'

const limiter = createRateLimiter(
sendEvent,
{ limit: 3, window: 10_000 },
(state) => ({
rejectionCount: state.rejectionCount,
}),
)

const accepted = limiter.maybeExecute('clicked')
console.log(accepted, limiter.state().rejectionCount)

이 가이드의 이후 핵심 코드 조각은 createRateLimiter을 사용하며 Solid 반응형 소유자 안에서 실행된다고 가정합니다.

거부된 호출 처리하기

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

const limiter = createRateLimiter(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 = createRateLimiter(sendEvent, {
enabled: (limiter) => limiter.store.state.executionCount < 100,
limit: (limiter) => (limiter.store.state.rejectionCount > 10 ? 2 : 5),
window: 60_000,
})

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

실행 관찰하기

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

const limiter = createRateLimiter(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)
},
})

Solid 수명 주기

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

반응형 상태

어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. Solid 반응형 소유자 안에서 유틸리티를 생성하고 뷰에서 사용하는 필드만 선택합니다.

const limiter = createRateLimiter(
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' 중 하나입니다.

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