AbortSignal.timeout()과 AbortSignal.any()는 요청 취소를 훨씬 간결하게 만들어주는 정적 메서드입니다. 앞의 것은 '몇 밀리초 지나면 자동 취소되는 신호'를 한 줄로 만들고, 뒤의 것은 '여러 취소 조건 중 하나라도 걸리면 취소되는 신호'를 합쳐줍니다. AbortController를 직접 만들고 setTimeout으로 배선하던 코드가 사라지죠.
fetch에 타임아웃을 거는 그 흔한 작업, 예전엔 은근히 성가셨습니다. 컨트롤러 만들고, 타이머 걸고, 응답 오면 타이머 지우고... 이걸 매번 반복했거든요.
예전 방식 — 손이 많이 갔다
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 5000);
try {
const res = await fetch(url, { signal: ctrl.signal });
return await res.json();
} finally {
clearTimeout(timer); // 이거 자주 까먹음
}
clearTimeout을 빼먹으면 타이머가 남아 떠돕니다. 잔실수가 나기 딱 좋은 구조였어요.
AbortSignal.timeout() — 한 줄로
const res = await fetch(url, {
signal: AbortSignal.timeout(5000),
});
끝입니다. 컨트롤러도, 타이머 정리도 필요 없습니다. 5초가 지나면 신호가 자동으로 발동하고, 요청이 먼저 끝나면 신호는 그냥 버려집니다. 타임아웃으로 취소되면 예외의 name이 TimeoutError라, 사용자가 누른 취소(AbortError)와 구분할 수 있는 것도 좋습니다.
try {
await fetch(url, { signal: AbortSignal.timeout(3000) });
} catch (e) {
if (e.name === 'TimeoutError') console.log('시간 초과');
else if (e.name === 'AbortError') console.log('사용자 취소');
}
AbortSignal.any() — 조건 합치기
현실에선 취소 이유가 하나가 아닙니다. '사용자가 취소했거나' 또는 '타임아웃이 났거나' 둘 중 뭐든 걸리면 멈춰야 하죠. AbortSignal.any()는 여러 신호를 묶어, 그중 하나라도 발동하면 취소되는 새 신호를 만듭니다.
const userCancel = new AbortController();
const res = await fetch(url, {
signal: AbortSignal.any([
userCancel.signal, // 사용자가 취소 버튼
AbortSignal.timeout(8000), // 또는 8초 초과
]),
});
이 패턴이 특히 빛나는 순간이 있습니다. 컴포넌트가 언마운트될 때의 취소 신호와 요청별 타임아웃을 한 번에 엮을 때죠. 각각 관리하던 걸 하나로 합치니 코드가 훨씬 읽기 쉬워집니다.
정리 비교
| 상황 | 쓸 것 |
|---|---|
| 단순 타임아웃 | AbortSignal.timeout(ms) |
| 사용자가 직접 취소 | new AbortController() |
| 여러 취소 조건 결합 | AbortSignal.any([...]) |
한 가지 주의
AbortSignal.any()로 만든 신호는 소스 신호들이 살아 있는 동안만 의미가 있습니다. 그리고 취소 사유(signal.reason)는 가장 먼저 발동한 소스의 사유를 물려받습니다. 그래서 타임아웃이 먼저 걸렸는지 사용자 취소가 먼저였는지 reason이나 name으로 판별할 수 있습니다.
자주 묻는 질문
fetch 말고 다른 데도 쓰나요?
네. AbortSignal을 받는 API라면 어디든 됩니다. addEventListener의 signal 옵션으로 리스너 자동 해제, ReadableStream, 여러 서드파티 라이브러리가 이 신호를 지원합니다. 취소라는 개념이 필요한 곳의 공통 규약이라고 보면 됩니다.
브라우저·Node 지원은요?
timeout()은 비교적 일찍, any()는 2024년경 자리 잡아 2026년 현재 모던 브라우저와 최신 Node.js에서 폭넓게 씁니다. 아주 오래된 환경만 아니라면 대체로 안전합니다.
타임아웃 취소와 사용자 취소를 어떻게 구분하나요?
잡힌 예외의 name을 보세요. 타임아웃이면 TimeoutError, controller.abort() 호출이면 기본적으로 AbortError입니다. abort()에 직접 사유 객체를 넘겨 커스텀 구분을 만들 수도 있습니다.

댓글 0