비동기 통신 취소하기(feat. AbortController)

상황 설명

Example 1. 메뉴바를 누르는 유저


메뉴 바 ( 홈, 상세보기, Best상품)
1) 홈 -> 메인 화면 물품 리스트 가져오기 API 호출
2) 상세보기 -> 특정 리스트 정보 가져오기 API 호출
3) Best 상품 -> Best 상품 리스트 가져오기 API 호출

<사용자 시나리오>

  1. 사용자가 API 호출을 완료해서 화면이 전환되기 전에 메뉴 탭을 번갈아 클릭한다.

  2. Front
    1) API 호출 결과 중 가장 최신의 값만 사용한다.
    2) 다른 메뉴가 클릭 되지 않도록 touch-action: none, pointer-events: none과 같은 css값을 사용한다.
    3) AbortController을 사용한다.



오늘은 마지막 방법인 AbortController를 알아보려고 한다.
이는 네트워크 요청을 취소할 수 있는 인터페이스이다.
(nodeJS에서는 사용이 불가능함 _ polyfill로 이용)

AbortController 살펴보기

abortSignal

  • 읽기 전용, 네트워크 요청 시 같이 담아 보내면 취소 하는 역할을 할 수 있음.

abort

  • 비동기 요청이 완료 되기 전 비동기 요청을 취소할 수 있는 메서드

AbortController 예시

  • 테스트를 위해 NetWork 상태를 느리게 설정하였다.
  • 버튼을 누르면 해당 API가 호출된다. 그런데, 호출이 완료되기 전에 다른 버튼을 마구 눌러보자

1) 1번 API 호출
2) 1번이 완료되기 전에 2번 클릭
3) 1번 API 호출이 취소되고 2번 API 호출
4) 2번이 완료되기 전에 다시 1번 클릭
5) 2번 API 호출이 취소되고 1번 API 호출

  • 결과: 1번, 2번, 1번 이렇게 총 3번의 API 호출이 있었지만, 최종 1번 API만 호출되어 결과가 나온다.
const Request = () => {
  let isFetch = false;
  let abortController = null;

  function init() {
    if (isFetch) abortController.abort();
    abortController = new AbortController();
    isFetch = true;
  }

  function finish() {
    abortController = null;
    isFetch = false;
  }

  return async function (url) {
    try {
      init();
      const response = await fetch(url, { signal: abortController.signal });
      const result = response.json();
      finish();
      return result;
    } catch (e) {
      const { name, status, message } = e;
      if (name === "AbortError") {
        console.log("Abort Controller의 취소");
      }
      throw { status, message };
    }
  };
};

const Fetch = Request();

const callAPI = async (url) => {
  try {
    const res = await Fetch(url);
    console.log(res);
  } catch (e) {
    console.log(e);
  }
};

const API ="https://jsonplaceholder.typicode.com/posts";

const $btnList = Array.from(document.getElementsByClassName('button'));

$btnList.forEach(($el, i) => {
  $el.addEventListener('click', async () => {
    await callAPI(`${API}/${i+1}`);
  })
})

참고) Axios

const axiosCancleToken = axios.CancelToken;
const source = axiosCancleToken.source();
....
axios.get(url, {  cancleToken: source.token });

....
// 취소 요청
source.cancle();

참고블로그

https://dev.to/bil/using-abortcontroller-with-react-hooks-and-typescript-to-cancel-window-fetch-requests-1md4

https://developer.mozilla.org/ko/docs/Web/API/AbortController