AbortSignal 결합: 취소 이유, 활성 시간, 정리의 경계
수동 취소와 시간 제한을 결합할 때 입력 순서, 이유 값, 응답 본문과 리스너 수명을 구분하는 방법.
발행·오류 제보: infostoa · 편집 기준
이 글의 개별 작성자와 전문 검토자 정보는 제공되지 않습니다.
AbortSignal은 이를 받는 API에 취소 요청을 전달하고 결합한다. 컨트롤러는 수동 중단을, timeout()은 시간 제한을, any()는 여러 신호의 결합을 제공한다. 실제 활용에는 작업의 지원 범위와 완료·정리 처리를 함께 살펴야 한다.
실행 기록이 아닌 개념도다.
작업의 소유자를 정한다
취소를 요청할 코드가 컨트롤러를 가지고 작업에는 signal을 전달한다. MDN은 이 통신 경계와 fetch, 응답 본문 소비, 스트림의 중단 범위를 설명한다. 컨트롤러와 참여 작업
가상의 검색 패널에 취소 버튼이 있다고 하자. 현재 검색에 속한 요청, 버튼 처리자의 소유자, 패널에서 완료가 뜻하는 상태를 먼저 적어 본다. 한 검색의 응답을 다른 검색의 결과처럼 표시하지 않도록 화면 갱신 조건도 정한다. 신호를 전달했다는 사실만으로 이 화면 정책이 완성되지는 않는다. 사용자가 패널을 닫았을 때 누가 결과 표시를 멈추는지도 같은 검토에 포함한다.
이유의 형태와 사용자 의도를 구분한다
reason은 Error 객체에 한정되지 않는 자바스크립트 값이다. 중단 전에는 undefined이며, 이유를 생략하거나 abort()에 undefined를 전달하면 기본 AbortError를 사용한다. 속성을 읽기 전에 값의 형태를 확인한다. 이유 값 DOM의 기본값 규칙
DOM이 정하는 취소 가능한 Promise 기반 웹 플랫폼 API의 통합 요건에는 이유 값으로 거부하고 이미 중단된 신호에는 즉시 거부하는 것이 포함된다. 모든 Promise에 취소 기능이 생긴다는 뜻은 아니다. API 통합 요건
검색 패널에서 버튼으로 취소했다는 사실을 알아야 한다면 그 동작을 별도로 기록하는 설계를 고려한다. 예외 이름 하나를 사용자 의도의 기록으로 대신하지 않는다. 사용자 중단, 제한 도달, 다른 실패를 어떤 문구로 보여 줄지 정하고, 잡힌 모든 값을 한 분류에 넣지 않는다. 이 구분은 실제 구현에서 확인할 제품 요구 사항이다.
입력 순서와 시간의 경계를 읽는다
any()는 이미 중단된 입력을 순회 순서대로 살펴 첫 해당 입력의 이유를 사용한다. 그런 입력이 없으면 처음 중단하는 입력의 이유를 받으며, 그 뒤의 중단은 이 이유를 바꾸지 않는다. 결합 자체가 다른 입력을 중단시키지는 않는다. 입력 순서와 이유 전달
timeout()은 활성 시간을 사용한다. 일시 중지된 워커나 뒤로·앞으로 캐시에 들어간 문서에서는 멈출 수 있으며 모든 백그라운드 탭에 대한 설명은 아니다. 활성 시간의 경계
가상 검토에서는 탐색을 마친 뒤 패널로 돌아왔을 때 오래된 결과가 여전히 필요한지 묻는다. 제한 숫자만으로 이 질문에 답할 수 없다. 이미 중단된 입력을 어떤 순서로 모았는지도 검토한다. 입력 이름을 사용자 신호라고 붙여도 앞선 중단 입력보다 자동으로 우선하지 않는다. 코드 검토에는 입력을 만드는 지점과 결합하는 지점을 함께 남기는 편이 이해하기 쉽다.
전문가 관점: 본문과 정리까지 본다
Jake Archibald의 2017년 Chrome 설명은 컨트롤러와 신호의 분리를 강조하고 fetch 취소가 응답 본문 읽기에도 적용됨을 보여 준다. 당시 브라우저 설명은 현재 호환성 표가 아니다. 날짜가 있는 전문가 설명
MDN의 정리 예시는 response.text() 완료까지 애플리케이션 리스너를 유지한 뒤 finally에서 제거한다. once: true는 이벤트가 발생해야 제거하므로 정상·실패 완료 경로도 필요하다. 이 리스너 제거는 시간 제한 신호를 취소하거나 fetch 내부 리스너를 관리하는 작업이 아니다. 애플리케이션 리스너 수명 시간 제한 신호의 수명
예시를 시작, 본문 처리, 종료 단계의 점검표로 사용한다. 패널을 닫은 뒤 화면 갱신을 멈추는 주체와 리스너 제거 위치를 확인한다. 대상 브라우저가 각 메서드를 지원하는지도 실제 환경에서 확인해야 한다. 2026-10-12 Chrome/macOS localhost에서 이 독립 HTML의 로컬 wait() 데모를 실행했다. KO/EN/JA 버튼의 UTF-8 표시, 수동 취소 AbortError: signal is aborted without reason, 무클릭 타임아웃 TimeoutError: signal timed out을 확인했다. 이 확인은 fetch/네트워크 취소, BFCache 또는 다른 브라우저를 검증하지 않는다.
실행 가능한 최소 DOM 예제
아래 HTML을 UTF-8 파일로 저장해 브라우저에서 열면 버튼 취소와 3초 제한을 한 신호로 합치는 동작을 직접 확인할 수 있다. wait()는 신호를 존중하도록 만든 예시 작업이다. 실제 API에서는 fetch(url, { signal })처럼 해당 API에 신호를 전달한다.
<!doctype html>
<html lang="ko-KR">
<head>
<meta charset="utf-8">
<title>AbortSignal 취소 데모</title>
</head>
<body>
<button id="cancel">취소</button>
<pre id="result">8초 작업 대기 중</pre>
<script>
const result = document.querySelector("#result");
const controller = new AbortController();
const signal = AbortSignal.any([
controller.signal,
AbortSignal.timeout(3000),
]);
document.querySelector("#cancel").addEventListener("click", () => controller.abort());
function wait(ms, signal) {
return new Promise((resolve, reject) => {
if (signal.aborted) return reject(signal.reason);
const timer = setTimeout(() => { cleanup(); resolve("완료"); }, ms);
const onAbort = () => { clearTimeout(timer); cleanup(); reject(signal.reason); };
const cleanup = () => signal.removeEventListener("abort", onAbort);
signal.addEventListener("abort", onAbort, { once: true });
});
}
wait(8000, signal)
.then(message => { result.textContent = message; })
.catch(error => { result.textContent = `${error.name}: ${error.message}`; });
</script>
</body>
</html>
예상 결과: 3초 안에 버튼을 누르면 기본 취소 이유인 AbortError가, 누르지 않으면 약 3초 뒤 TimeoutError가 표시된다. 작업 시간은 8초라 정상 완료보다 두 취소 경로가 먼저 끝난다. 이 예제는 신호·이벤트 처리를 보여 주는 로컬 데모이며 네트워크 요청은 보내지 않는다. AbortSignal.any()와 timeout()을 지원하지 않는 브라우저에서는 먼저 해당 메서드 지원 여부를 확인한다.