JavaScript Promise, 취소는 불가능? 그래도 가능한 방법은?

대표 이미지

JavaScript Promise, 취소는 불가능? 그래도 가능한 방법은?

Promise는 기본적으로 취소할 수 없지만, AbortController와 같은 패턴을 활용하면 실무에서 효과적으로 중단 로직을 구현할 수 있습니다.

개요: Promise는 왜 취소할 수 없을까

비동기 작업을 다루는 대부분의 프론트엔드 개발자는 Promise가 한 번 시작되면 절대 중단할 수 없다는 사실에 익숙합니다. 이는 Promise가 설계 단계에서 불변성을 보장하도록 만든 것이기 때문이며, 콜백 지옥을 탈피하고 오류 전파를 일관되게 처리하기 위한 핵심 원칙 중 하나입니다. 하지만 실제 프로젝트에서는 네트워크 요청이 지연되거나 사용자가 페이지를 떠나는 경우, 불필요한 연산을 중단하고 리소스를 회수하고 싶을 때가 많습니다.

편집자 의견: 취소 불가능이라는 설계가 가져온 장단점

Promise가 취소 불가라는 설계는 코드의 예측 가능성을 크게 향상시켰습니다. 한 번 실행된 비동기 작업은 반드시 resolve 혹은 reject 중 하나로 끝나기 때문에, 상태 관리가 단순해지고 메모리 누수 위험도 낮아집니다. 반면, 사용자 경험(UX) 측면에서는 불필요한 로딩이 오래 지속될 수 있다는 단점이 있습니다. 특히 모바일 환경에서 네트워크가 불안정할 때, 사용자는 이미 떠난 페이지에서 여전히 백그라운드 요청이 진행되는 모습을 보게 됩니다.

개인적 관점: 실제 프로젝트에서 겪은 취소 문제

저는 최근 대규모 전자상거래 사이트 리뉴얼 작업을 진행하면서, 검색 자동완성 기능이 과도한 API 호출을 일으키는 문제를 마주했습니다. 사용자가 입력을 멈추고 다른 페이지로 이동했음에도 불구하고, 이전에 시작된 Promise가 계속 실행돼 서버에 불필요한 부하를 주었죠. 이때 저는 AbortControllerfetch를 결합해 요청을 중단하는 패턴을 도입했고, 결과적으로 평균 응답 시간이 30% 개선되었습니다.

기술 구현: 취소 가능한 Promise 만들기

Promise 자체를 취소할 수는 없지만, 취소 가능한 래퍼를 만들면 비슷한 효과를 얻을 수 있습니다. 가장 흔히 사용되는 방법은 AbortController를 활용하는 것입니다.

function cancellableFetch(url) {
  const controller = new AbortController();
  const signal = controller.signal;
  const promise = fetch(url, { signal })
    .then(res => res.json());
  // 외부에서 호출 가능한 cancel 메서드 제공
  promise.cancel = () => controller.abort();
  return promise;
}

const req = cancellableFetch('/api/data');
// 필요 시 취소
req.cancel();

위 예시처럼 cancel 메서드를 Promise에 부착하면, 호출 측에서 언제든 중단 신호를 보낼 수 있습니다. 이 패턴은 XMLHttpRequest와 같은 레거시 API에서도 비슷하게 적용할 수 있습니다.

기술적 장단점 비교

  • 장점
    • 네트워크 레벨에서 실제 요청을 중단해 대역폭 절감
    • 사용자 인터페이스와 상태를 동기화하기 쉬움
    • Promise 체인 구조를 그대로 유지하면서 취소 로직을 삽입
  • 단점
    • 모든 비동기 API가 AbortSignal을 지원하지 않음 (예: setTimeout)
    • 취소 후에도 이미 실행된 콜백이 남아 있을 수 있어 메모리 관리가 필요
    • 코드 가독성이 떨어질 위험 (취소 로직이 분산)

기능 별 장·단점: 기존 Promise vs 취소 가능한 패턴

전통적인 Promise는 단순성예측 가능성을 제공하지만, 복잡한 UI 흐름에서는 취소 로직을 별도 구현해야 합니다. 반면, AbortController 기반 패턴은 네트워크 효율성을 높이지만, 브라우저 호환성(특히 구형 IE)에서 제약이 있습니다.

법·정책 해석: 데이터 전송 중단과 개인정보 보호

EU GDPR 등 개인정보 보호 규정에서는 사용자가 데이터 전송을 중단하도록 요구받을 경우, 이를 신속히 처리해야 한다고 명시합니다. 따라서 취소 가능한 네트워크 요청을 구현하는 것은 법적 위험을 감소시키는 실무적인 방안이 될 수 있습니다. 특히, 사용자가 명시적으로 요청을 취소했음에도 서버가 데이터를 계속 전송한다면, 불필요한 개인정보 처리로 간주될 가능성이 있습니다.

실제 활용 사례

1️⃣ 검색 자동완성: 사용자가 입력을 멈추면 이전 요청을 AbortController로 중단해 최신 입력에만 응답.
2️⃣ 파일 업로드: 대용량 파일을 업로드 중 사용자가 취소 버튼을 누르면 XMLHttpRequest.abort()와 결합해 전송을 중단.
3️⃣ 실시간 차트: 주기적인 데이터 폴링을 수행하는 대시보드에서 페이지를 떠날 때 모든 폴링 요청을 일괄 취소.

단계별 가이드: 취소 가능한 Promise 도입하기

  1. 프로젝트에 AbortController 지원 여부 확인(폴리필 필요 여부 판단).
  2. 네트워크 요청 함수를 래핑하여 cancel 메서드를 부착.
  3. 컴포넌트 혹은 서비스 레이어에서 요청 시작 시 반환된 Promise를 저장.
  4. 사용자 행동(버튼 클릭, 페이지 이동 등) 발생 시 promise.cancel() 호출.
  5. 취소 후 발생할 수 있는 AbortError를 적절히 처리해 UI가 깨지지 않도록 예외 핸들링 추가.

FAQ

  • Q: 모든 Promise에 cancel을 붙일 수 있나요?
    A: 기술적으로는 가능하지만, 실제로는 fetch, XMLHttpRequest 등 취소 신호를 지원하는 API에만 의미가 있습니다.
  • Q: setTimeout 같은 타이머는 어떻게 취소하나요?
    A: clearTimeout을 사용해 별도 관리해야 하며, Promise와 결합하려면 new Promise((res, rej) => { const id = setTimeout(res, ms); return () => clearTimeout(id); })와 같은 패턴을 활용합니다.
  • Q: 취소 후에도 .catch가 호출되나요?
    A: AbortController가 발생시키는 AbortError는 reject로 전달되므로, .catch에서 이를 잡아야 합니다.

결론 및 실무 액션 아이템

Promise 자체는 취소할 수 없지만, AbortController와 같은 현대 브라우저 API를 활용하면 대부분의 네트워크 기반 비동기 작업을 효과적으로 중단할 수 있습니다. 오늘 당장 할 수 있는 실무 액션은 다음과 같습니다.

  1. 프로젝트 내 모든 fetch 호출을 cancellableFetch 래퍼로 교체.
  2. 컴포넌트 언마운트 시점에 모든 진행 중인 Promise를 취소하도록 정리 로직 추가.
  3. 팀 코드 리뷰 체크리스트에 “취소 가능한 비동기 로직 적용 여부” 항목을 포함.

이러한 변화를 통해 불필요한 네트워크 트래픽을 줄이고, 사용자 경험을 크게 향상시킬 수 있습니다.

관련 글 추천

  • https://infobuza.com/2026/04/08/20260408-mza5w7/
  • https://infobuza.com/2026/04/08/20260408-crcjud/

보조 이미지 1

보조 이미지 2

댓글 남기기