[EXT-036][실무] 시간 제한과 사용자 취소 한 신호로 묶기 > IT 기술 공유

본문 바로가기
사이트 내 전체검색

IT 기술 공유

[EXT-036][실무] 시간 제한과 사용자 취소 한 신호로 묶기

페이지 정보

profile_image
작성자 기술팀장
댓글 0건 조회 5회 작성일 26-09-09 02:37

본문

[이번 수업]
긴 작업은 사용자가 취소할 수도 있고, 정한 시간을 넘겨 자동으로 멈춰야 할 수도 있습니다. 이번에는 AbortSignal.timeout()과 AbortSignal.any()로 두 중단 조건을 한 신호로 묶고, 무엇이 먼저 멈췄는지 reason으로 구분합니다.

[선수지식]
Promise, async·await, 이벤트 리스너와 EXT-035의 AbortSignal 기본 사용법을 알면 좋습니다. 예제는 AbortSignal.any()를 제공하는 Node.js 20.3 이상에서 실행합니다.

[학습목표]
1. 사용자 취소와 시간 제한을 하나의 신호로 합칩니다.
2. AbortError와 TimeoutError를 구분합니다.
3. 중단 이유를 상위 코드로 전달합니다.

[핵심개념]
AbortController는 코드가 직접 중단을 알릴 때 사용합니다. controller.abort(reason)을 호출하면 signal이 중단 상태가 되고 reason을 보관합니다. 이유를 생략하면 AbortError인 DOMException이 사용됩니다.

AbortSignal.timeout(milliseconds)은 시간이 지나면 TimeoutError를 이유로 중단되는 신호를 만듭니다. AbortSignal.any([a, b])는 입력 중 하나라도 중단되면 함께 중단되는 새 신호를 만듭니다. 합친 신호에는 가장 먼저 중단시킨 입력의 reason이 남으며 나중에 바뀌지 않습니다. signal.throwIfAborted()는 중단되었다면 저장된 reason을 그대로 던집니다. 따라서 중단은 한 통로로 처리하고 원인은 구분할 수 있습니다.

[따라하기]
아래 내용을 cancel-race.mjs로 저장합니다.

```js
const userController = new AbortController();
const timeoutSignal = AbortSignal.timeout(80);
const combined = AbortSignal.any([
  userController.signal,
  timeoutSignal,
]);

setTimeout(() => {
  userController.abort(
    new DOMException('사용자 취소', 'AbortError'),
  );
}, 20);

await new Promise((resolve) => {
  combined.addEventListener('abort', resolve, { once: true });
});

console.log(`aborted=${combined.aborted}`);
console.log(`reason=${combined.reason.name}`);

try {
  combined.throwIfAborted();
} catch (error) {
  console.log(`caught=${error.name}`);
}
```

터미널에서 node cancel-race.mjs를 실행합니다. Windows PowerShell, macOS Terminal, Linux 셸 모두 같은 명령입니다. 예상 결과는 다음과 같습니다.

```text
aborted=true
reason=AbortError
caught=AbortError
```

20밀리초의 사용자 취소가 80밀리초 제한보다 먼저 도착했습니다. Node.js v25.8.1에서 이 결과를 확인했습니다.

[흔한 실수]
combined.aborted만 보면 재시도 여부를 잘못 판단할 수 있으므로 reason.name도 확인하세요. 이미 중단된 신호에는 나중에 등록한 abort 이벤트가 다시 발생하지 않습니다. 외부 신호를 받으면 먼저 aborted를 확인하거나 시작점에서 throwIfAborted()를 호출하세요. 리스너에는 { once: true }를 써서 불필요한 참조가 남지 않게 합니다. 작업 코드가 신호를 무시하면 실제 작업은 멈추지 않는다는 점도 주의합니다.

[보안 주의]
시간 제한은 권한 검사나 자원 제한을 대신하지 않습니다. 큰 입력이 자원을 오래 점유하지 못하도록 요청 크기와 동시 실행 수도 제한하세요. 중단 이유에 토큰, 개인정보, 전체 요청 본문을 넣거나 로그로 남기지 않습니다. 실습은 본인 소유의 로컬 환경에서만 실행합니다.

[직접 해볼 과제]
사용자 취소 타이머를 20에서 120밀리초로 바꿔 실행하세요. 80밀리초 제한이 먼저 도착해 reason과 caught가 TimeoutError가 되는지 확인합니다. 이어 두 시간을 모두 80밀리초로 맞춰 여러 번 실행하고, 같은 시각을 기대하는 설계가 불안정한 이유를 적어 보세요.

[확인문제]
1. AbortSignal.timeout()이 중단될 때 reason.name은 무엇인가요?
2. 두 입력 신호가 차례로 중단되면 합친 신호에는 어느 이유가 남나요?
3. abort 이벤트 리스너에 { once: true }를 권장하는 이유는 무엇인가요?

[다음 학습]
다음에는 합친 신호를 fetch에 전달하고 정상 응답, 사용자 취소, 시간 초과, 서버 오류를 구분해 처리합니다.

[공식 참고 자료]
- WHATWG DOM Standard — AbortSignal: https://dom.spec.whatwg.org/#interface-AbortSignal
- WHATWG DOM Standard — AbortController: https://dom.spec.whatwg.org/#interface-abortcontroller
- Node.js Global objects — AbortSignal.timeout(): https://nodejs.org/api/globals.html#static-method-abortsignaltimeoutdelay
- Node.js Global objects — AbortSignal.any(): https://nodejs.org/api/globals.html#static-method-abortsignalanysignals

댓글목록

등록된 댓글이 없습니다.

회원로그인

회원가입

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

접속자집계

오늘
3,944
어제
6,862
최대
16,772
전체
769,895
Copyright © 소유하신 도메인. All rights reserved.