⏰ AbortController - 비동기 웹 요청 중단하기

Dev.Jo·2023년 4월 23일
2
post-thumbnail

✅ 들어가며

대부분의 프론트엔드 개발에서는 서버와 통신하기 위해 비동기 웹 요청을 사용합니다. 하지만 비동기 웹 요청은 응답 시간과 결과 값을 예측하기 어려우며 사이드 이펙트를 발생시키는 제어하기 힘든 요소 중 하나입니다.

AbortController 객체를 사용하면 이러한 비동기 웹 요청을 부분적으로 제어할 수 있습니다. AbortController는 브라우저 Web API로 비동기 웹 요청을 중단할 수 있는 기능을 제공합니다.

응답 시간이 오래 걸리거나 응답이 더 이상 필요하지 않은 경우 요청을 중단할 수 있습니다. 이렇게 요청을 제어하여 불필요한 서버 요청을 줄여 성능을 개선할 수 있으며, 사용자에게 더 나은 사용자 경험을 제공할 수 있습니다.

이번 글에서는 AbortController의 개념과 활용 방법을 알아보겠습니다.

🎮 AbortController

AbortController API의 기본적인 사용방법은 다음과 같습니다.

  1. 요청을 컨트롤하는 AbortController 객체를 생성합니다.
const controller = new AbortController();

// controller 객체는 signal 프러퍼티와 abort() 메서드를 가집니다
controller.signal;
controller.abort();
  1. 중단할 fetch API에 signal 프러퍼티를 연결합니다.
fetch (URL, {
  signal: controller.signal
})
  1. abort() 메서드로 요청을 중단합니다.
controller.abort()

❗️ signal.reason

abort() 메서드에는 중단이유를 인자로 넘겨줄 수 있습니다. 여러 케이스에 따라 요청을 중단할 수 있습니다.

setTimeout(()=>{
	controller.abort("응답시간이 너무 깁니다")
},1000)

cancelButton.onclick(()=>{
	controller.abort("사용자가 취소버튼을 눌렀습니다")
})

이러한 중단이유는 abort()된 후 signal.reason 프러퍼티로 가져올 수 있습니다. 중단이유에 따라 핸들러를 작성할 수 있습니다.

controller.signal.reason 
// "사용자가 취소버튼을 눌렀습니다" 또는 "응답시간이 너무 깁니다"

❌ AbortError

abort() 메서드가 실행되면 요청은 중단되고 fetch API는 AbortError 에러를 던집니다.

const controller = new AbortController();

const fetchData = async () => {
 	const response = await fetch("https://httpbin.org/delay/10", {
      signal: controller.signal
    })
}

fetchData()
.then()
.catch(error=>{
  if(error.name === "AbortError"){
   	// abort 핸들러 
    abortHandler();
  }
})

// 1초가 지나면 응답여부에 상관없이 요청을 중단합니다. 
setTimeout(()=>{
  controller.abort();
},1000);

⭐️ 활용하기

3초가 넘어가도 응답이 오지않는다면 요청을 중단하고 사용자에게 알리는 코드를 작성해봅시다.

controller.abort() 메서드를 사용할 때 주의해야할 점은 응답 존재 여부에 상관없이 요청을 중단한다는 점입니다. 따라서 응답의 존재 여부를 확인 후 abort() 시켜야합니다.

signal.addEventListener("abort")를 사용하면 요청이 중단되었을 때 수행할 이벤트 핸들러를 정의할 수 있습니다.

만약 응답의 존재여부를 확인하지 않고 controller.abort()를 호출한다면 응답이 정상적으로 왔음에도 항상 "abort" 이벤트가 실행되기 때문에 응답의 존재여부를 확인하는 것은 중요합니다.

const controller = new AbortController();

let response;

const fetchData = async () => {
    response = await fetch("https://httpbin.org/delay/10", {
      signal: controller.signal
    });
};

fetchData()
.then()
.catch(e=>{
 	if(e.name === "AbortError"){
      // abort handler
    }
})

controller.signal.addEventListener("abort", () => {
  console.log("aborted");
});

setTimeout(() => {
  // 응답이 존재하지 않는 경우만 요청을 중단합니다.
  if (!response) {
    controller.abort("요청이 너무 느립니다");
  }
}, 3000);

⏰ AbortSignal.timeout()

AbortSignal.timeout 정적메서드를 사용하면 훨씬 짧고 간단하게 기능을 구현할 수 있습니다.

하지만 Chrome과 Safari에서 버그가 있는 기능이기 때문에 버그가 픽스되었을 때를 대비해서 실제 사용은 조심해야합니다. (관련 버그1)

AbortSignal.timeout() 정적메서드는 지정한 시간만큼 지난 후 abort()하는 AbortSignal 객체를 반환합니다. new AbortController()setTimeout()을 사용하지않고 코드를 단순화 할 수 있습니다.

const fetchData = async () => {
    response = await fetch("https://httpbin.org/delay/10", {
      // 1초뒤 자동으로 요청을 abort() 합니다
      signal: AbortSignal.timeout(1000)
    });
};

fetchData()
.then()
.catch(e=>{
  if(e.name === "AbortError"){
    console.log("abort"); 
  }
})
  

➕ 추가) 이벤트 리스너 제거하기

비동기 웹 요청뿐만 아니라 AbortController로 이벤트 리스너를 손 쉽게 제거할 수 있습니다.

일반적으로 이벤트 리스너를 제거하기 위해서는 이벤트를 등록할 때 함수의 참조값을 알고 있어야합니다. 참조값이 불일치하는 경우 이벤트 리스너를 제거할 수 없습니다.

// BAD
window.addEventLitener("click", ()=> {}); 
window.removeEventListener("click", ()=> {}); // 이벤트리스너를 제거할 수 없습니다.

// GOOD 
const clickHandler = () => {};
window.addEventLitener("click", clickHandler);
window.removeEventListener("click", clickHandler); // 동일한 함수 참조값을 가져야합니다.

AbortController과 AbortSignal을 사용하면 핸들러의 참조값을 알지못해도 이벤트 핸들러를 제거할 수 있습니다.

const controller = new AbortController();

window.addEventListener("click", () => {
  console.log("click");
},{ 
  signal: controller.signal
});

// "click" 이벤트 핸들러가 제거됩니다.
controller.abort();

axios, nodejs 지원 유무

axios는 원래 cancelToken 기반으로 요청을 취소할 수 있었습니다. 하지만 cancelToken은 중단된 TC39 제안 proposal-cancelable-promises 기반이기 때문에 현재는 deprecated되었습니다.

axios는 v.0.22.0 부터 abortController를 지원합니다. 사용하는 방법은 동일합니다.

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
})

controller.abort()

node.js에서도 v15.0.0 부터 AbortController를 지원합니다.

마치며 + 요약

이번 글에서는 AbortController를 이용해 비동기 웹 요청을 중단하는 방법에 대해 알아보았습니다.

AbortController를 사용하면, 이미 요청이 발생한 상황에서도 중간에 해당 요청을 취소할 수 있어서, 불필요한 리소스를 낭비하지 않고 빠르게 다른 작업으로 전환할 수 있습니다. 또한, 이를 이용하여 페이지 전환 등을 수행할 때, 중복된 요청을 방지할 수 있습니다

감사합니다.

profile
소프트웨어 엔지니어, 프론트엔드 개발자

4개의 댓글

comment-user-thumbnail
2023년 4월 28일

너무너무 좋은데요!!

2개의 답글

관련 채용 정보