Angular 비동기 요청률 제한 가이드
비동기 요청률 제한은 요청률 제한 가이드에서 설명한 시간 창 동작을 유지하면서 Promise 결과, 재시도, 오류 콜백, 진행 중인 작업에 대한 제어 기능을 추가합니다.
수락된 작업이 필요한 값을 반환하거나 거부될 수 있을 때, 또는 재시도와 중단 지원이 필요할 때 사용합니다. 즉시 수락 또는 거부 여부를 나타내는 불리언 값만 필요하다면 동기 제한기를 사용합니다.
API 선택하기
- 할당량으로 제어되는 핸들러에는
injectAsyncRateLimitedCallback을 사용합니다. - 용량 헬퍼와 선택된 상태가 필요하면
injectAsyncRateLimiter를 사용합니다.
Angular 예시
import { injectAsyncRateLimiter } from '@tanstack/angular-pacer'
export class LoadComponent {
readonly limiter = injectAsyncRateLimiter(
loadUser,
{ limit: 3, window: 10_000 },
(state) => ({
rejectionCount: state.rejectionCount,
}),
)
}
이 가이드의 이후 핵심 코드 조각은 injectAsyncRateLimiter을 사용하며 Angular 주입 컨텍스트 안에서 실행된다고 가정합니다.
수락된 호출과 거부된 호출
maybeExecute()는 다음 두 가지 일반 결과 중 하나로 이행되는 Promise를 반환합니다.
- 수락된 호출은 함수를 실행하고 그 결과로 이행됩니다.
- 시간 창에 의해 거부된 호출은 함수를 실행하지 않고
undefined로 이행됩니다.
onReject를 사용하거나 호출 전에 용량을 비교해야 하는 경우는 undefined도 유효한 함수 결과일 때입니다.
if (limiter.getRemainingInWindow() > 0) {
const result = await limiter.maybeExecute('123')
}
동기 제한기와 달리 수락된 호출은 겹칠 수 있습니다. limit는 동시에 활성화할 수 있는 실행 수가 아니라 시간 창 안에 시작할 수 있는 실행 수를 제어합니다.
limit: 3
start A ───────────────── finish A
start B ───── finish B
start C ─────────────────── finish C
call D rejected
동시 실행 제한이 필요하거나 초과 작업을 거부하는 대신 대기시켜야 한다면 비동기 큐를 사용합니다.
시간 창 동작
시간 창 타입은 동기 제한기와 동일합니다.
fixed는 첫 번째로 수락된 실행과 함께 시간 창을 시작합니다. 해당 시간 창의 모든 타임스탬프가 함께 만료됩니다.sliding은 각 타임스탬프의window기간이 지날 때까지 수락된 타임스탬프를 각각 유지합니다.
기본값은 fixed입니다. getRemainingInWindow()는 가능한 시작 횟수를, getMsUntilNextWindow()는 거부된 호출자가 용량이 생길 때까지 기다려야 하는 시간을 알려 줍니다.
수락된 실행은 시작할 때 용량을 소비합니다. 이후 비동기 함수가 실패하거나 중단되어도 횟수에 포함됩니다. 해당 실행의 재시도는 요청률 제한 타임스탬프를 추가하지 않습니다.
오류와 콜백
비동기 요청률 제한기는 각 결과에 대해 다음 콜백을 제공합니다.
onSuccess(result, args, limiter)는 수락된 실행이 성공한 후 호출됩니다.onError(error, args, limiter)는 재시도가 실패한 후 호출됩니다.onSettled(args, limiter)는 어느 실행 결과든 완료된 후 호출됩니다.onReject(args, limiter)는 시간 창에 용량이 없을 때 호출됩니다.
거부는 실행 오류가 아닙니다. onError나 onSettled를 호출하지 않습니다.
onError가 없으면 throwOnError의 기본값이 true이므로 수락된 실행이 실패하면 Promise가 거부됩니다. onError를 제공하면 이 기본값이 false로 바뀌고 Promise는 현재 lastResult로 이행됩니다. 다른 동작이 필요하면 throwOnError를 명시적으로 설정합니다.
수락된 실행 재시도하기
asyncRetryerOptions로 수락된 각 실행의 재시도를 설정합니다.
const limiter = injectAsyncRateLimiter(sendRequest, {
limit: 5,
window: 60_000,
asyncRetryerOptions: {
maxAttempts: 3,
backoff: 'exponential',
baseWait: 1000,
jitter: 0.2,
},
})
maxAttempts에는 첫 번째 시도가 포함됩니다. 따라서 하나의 수락된 요청률 제한 슬롯이 다운스트림 서비스에 여러 번 요청할 수 있습니다. 요청률 제한과 재시도를 함께 사용하기 전에 해당 서비스 자체의 제한을 고려합니다. 재시도 안전성은 비동기 재시도 가이드를 참고합니다.
활성 작업 중단하기
abort()는 모든 활성 실행을 중단합니다. 해당 타임스탬프를 제거하거나 시간 창 용량을 복원하지는 않습니다. 취소가 전파되도록 각 실행의 시그널을 기반 작업에 전달합니다.
const limiter = injectAsyncRateLimiter(
async (id: string) => {
return fetch(`/api/users/${id}`, {
signal: limiter.getAbortSignal() ?? undefined,
})
},
{ limit: 5, window: 60_000 },
)
limiter.abort()
여러 실행이 겹칠 때 인수 없이 호출한 getAbortSignal()은 가장 최근에 시작한 실행을 가리킵니다. 특정 활성 실행을 대상으로 하려면 해당 실행의 maybeExecuteCount를 전달합니다.
재설정하기
reset()은 요청률 제한 타임스탬프를 지우고 기본 상태를 복원합니다. 실행 중인 기반 작업의 중단을 보장하지 않으므로 완전히 정리해야 한다면 먼저 중단합니다.
limiter.abort()
limiter.reset()
재설정하면 용량이 즉시 복원됩니다. 설정된 제한을 우회하는 용도가 아니라 실제로 새로운 제한 기간을 시작할 때만 사용합니다.
설정
enabled, limit, window에는 값 또는 제한기 인스턴스를 받는 함수를 사용할 수 있습니다. setOptions()는 새 옵션을 현재 설정에 병합합니다.
limiter.setOptions({
enabled: (limiter) => limiter.store.state.errorCount < 3,
limit: (limiter) => (limiter.store.state.rejectionCount > 10 ? 2 : 5),
})
limit, window, windowType을 변경해도 기존 실행 기록은 지워지지 않습니다. 새 설정을 새로운 시간 창으로 시작해야 한다면 reset()을 호출합니다. 비활성화된 제한기는 함수를 실행하거나 용량을 소비하지 않으며, 호출은 undefined로 이행됩니다.
재사용 가능한 타입 검사 옵션 객체를 정의하려면 asyncRateLimiterOptions()를 사용합니다.
Angular 수명 주기
어댑터는 소유자가 제거될 때 활성 작업을 중단합니다. onUnmount를 제공하면 이 기본 정리 동작을 대체하므로 활성 작업을 중단해야 한다면 사용자 정의 콜백에서 abort()를 호출해야 합니다.
반응형 상태
어댑터는 셀렉터 인수가 반환한 상태만 구독합니다. 셀렉터가 없으면 어댑터 상태는 비어 있습니다. 일반적으로 컴포넌트나 서비스의 필드 초기화 구문과 같은 Angular 주입 컨텍스트에서 유틸리티를 생성하고 뷰에서 사용하는 필드만 선택합니다.
const limiter = injectAsyncRateLimiter(
loadUserFromApi,
{ limit: 5, window: 60_000 },
(state) => ({
isExceeded: state.isExceeded,
isExecuting: state.isExecuting,
rejectionCount: state.rejectionCount,
}),
)
console.log(
limiter.state().isExceeded,
limiter.state().isExecuting,
limiter.state().rejectionCount,
)
옵션 함수와 수명 주기 콜백은 기반 공개 유틸리티 인스턴스를 받습니다. 위 예제처럼 해당 콜백 안에서 .store.state를 읽는 방식을 지원합니다. 렌더링 코드는 여기에 나온 선택된 어댑터 상태를 읽어야 합니다.
앱에서 유지한 선택 상태를 복원하려면 initialState로 부분 스냅샷을 전달합니다. 이 스냅샷은 기본값과 병합됩니다. 지속 가능한 필드만 복원합니다. 대기 중인 타이머와 활성 실행은 복원되지 않습니다.
executionTimes: 시간 창에서 아직 사용하는 수락된 시작 시간입니다.isExceeded: 현재 제한에 도달했는지 여부입니다.isExecuting: 수락된 실행이 하나 이상 활성 상태인지 여부입니다.rejectionCount: 시간 창에 의해 거부된 호출 수입니다.lastResult: 가장 최근의 성공 결과입니다.successCount,errorCount,settleCount: 실행 결과 횟수입니다.
어댑터 시그니처는 Angular API 레퍼런스를, 전체 옵션과 상태 타입은 공개 핵심 레퍼런스를 참고합니다.