처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.04.08 21

JavaScript Promise, 취소할 수 있을까? — 의외로 가능한 방법들

Hacker News 원문 보기
JavaScript Promise, 취소할 수 있을까? — 의외로 가능한 방법들

Promise는 한번 시작하면 멈출 수 없다?

자바스크립트에서 비동기 작업을 다룰 때 가장 많이 쓰는 게 Promise죠. API 호출, 파일 읽기, 타이머 등 거의 모든 비동기 작업이 Promise로 이루어져요. 그런데 한 가지 불편한 진실이 있어요. 한번 시작한 Promise는 취소할 수 없다는 거예요.

이게 왜 문제냐면, 실제 개발하다 보면 "이미 보낸 요청을 취소하고 싶다"는 상황이 정말 자주 생기거든요. 예를 들어 사용자가 검색어를 타이핑하면 자동완성 API를 호출하는데, 글자를 빠르게 입력하면 이전 요청의 결과는 더 이상 필요 없잖아요. 또는 사용자가 페이지를 떠났는데 이전 페이지의 데이터 요청이 아직 진행 중인 경우도 있고요.

이번에 소개할 글은 이 Promise 취소 문제를 깊이 있게 다루면서, 실제로 취소와 비슷한 동작을 구현하는 여러 패턴을 보여주는 내용이에요.

Promise가 취소 불가능한 이유

먼저 왜 Promise에 취소 기능이 없는지 이해할 필요가 있어요. Promise는 "미래의 어떤 값에 대한 약속"이에요. 비유하자면 식당에서 번호표를 받은 거예요. 번호표 자체를 찢는다고 해서 주방에서 요리가 중단되지는 않잖아요? Promise도 마찬가지로, 이미 실행 중인 비동기 작업 그 자체를 중단하는 메커니즘이 아니에요. 결과를 전달하는 "통로"일 뿐이죠.

실제로 TC39(자바스크립트 표준을 정하는 위원회)에서 Cancelable Promise라는 제안이 있었는데, 설계가 너무 복잡해지면서 철회됐어요. 대신 다른 방식으로 취소를 해결하는 방향으로 발전했죠.

AbortController: 공식적인 취소 방법

가장 널리 쓰이는 방법은 AbortController예요. 이건 Promise 자체를 취소하는 게 아니라, Promise가 감싸고 있는 작업(주로 fetch 요청)에게 "그만해"라는 신호를 보내는 거예요.

사용법은 이래요. const controller = new AbortController()로 컨트롤러를 만들고, fetch(url, { signal: controller.signal })처럼 signal을 전달해요. 취소하고 싶을 때 controller.abort()를 호출하면 fetch가 AbortError를 던지면서 중단되는 거죠.

React에서는 useEffect의 cleanup 함수에서 abort()를 호출하는 패턴이 일반적이에요. 컴포넌트가 언마운트되거나 의존성이 바뀌면 이전 요청을 자동으로 취소하는 거죠. 이렇게 하면 메모리 누수도 막고, 이전 요청의 응답이 뒤늦게 도착해서 잘못된 상태를 설정하는 문제(race condition)도 예방할 수 있어요.

"매달린 Promise" 패턴

이 글에서 흥미로운 패턴을 하나 소개하는데요, 바로 "매달린 Promise(hanging promise)"를 제어 흐름에 활용하는 방법이에요. 이게 뭐냐면, 의도적으로 절대 resolve되지 않는 Promise를 만들어서 실행 흐름을 멈추는 거예요.

await new Promise(() => {}) — 이 코드를 실행하면 영원히 대기 상태에 빠져요. resolve도 reject도 호출하지 않으니까요. 일반적으로는 버그로 보이지만, 특정 상황에서는 의도적으로 활용할 수 있어요. 예를 들어 워크플로우 엔진에서 특정 조건이 충족될 때까지 실행을 일시정지하는 용도로 쓸 수 있는 거죠.

다만 이 패턴은 매우 주의해서 써야 해요. 가비지 컬렉션이 제대로 안 되면 메모리 누수로 이어질 수 있고, 코드를 읽는 다른 개발자가 "이거 버그 아니야?"라고 혼란스러워할 수 있거든요. 반드시 명확한 주석과 함께 사용하고, 정말 필요한 경우에만 쓰는 게 좋아요.

Promise.race로 타임아웃 구현하기

취소와 관련된 또 하나의 유용한 패턴은 Promise.race를 활용한 타임아웃이에요. Promise.race([fetchData(), timeout(5000)]) 이런 식으로 쓰면, 데이터 요청과 5초 타이머 중 먼저 끝나는 것의 결과를 받게 돼요. 5초 안에 응답이 안 오면 타임아웃 처리를 할 수 있는 거죠. 물론 이것도 실제 네트워크 요청 자체를 취소하는 건 아니지만, 사용자 경험 측면에서 무한 대기를 방지하는 데 효과적이에요.

한국 개발자에게 주는 시사점

프론트엔드 개발을 하고 있다면 AbortController 패턴은 반드시 알아둬야 해요. 특히 React나 Vue에서 컴포넌트 생명주기와 비동기 요청을 연결할 때 필수적이거든요. 검색 자동완성, 무한 스크롤, 페이지 전환 시 데이터 패칭 같은 일상적인 기능에서 바로 쓸 수 있어요.

백엔드(Node.js)에서도 외부 API를 호출할 때 타임아웃과 취소를 적절히 구현하는 것이 서비스 안정성에 직결되는 부분이에요. 외부 서비스가 느려질 때 우리 서버까지 같이 느려지는 연쇄 지연(cascading latency) 문제를 방지하려면요.

TypeScript를 쓰고 있다면 AbortSignal의 타입도 잘 지원되니까 타입 안전하게 구현할 수 있어요.

마무리

Promise는 취소할 수 없지만, 취소가 필요한 상황은 늘 있어요. AbortController를 기본으로, 상황에 맞는 패턴을 골라 쓰는 게 핵심이에요.

여러분은 비동기 작업 취소를 어떻게 처리하고 있나요? 프로젝트에서 race condition 때문에 고생한 경험이 있다면 공유해주세요!


🔗 출처: Hacker News

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

AI 도구, 직접 활용해보세요

AI 시대, 코딩으로 수익을 만드는 방법을 배울 수 있습니다.

AI 활용 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.