DOM) 실행 중인 Http Request 취소하기 (AbortController)

김명성·2022년 6월 27일
0

Http request 요청이 진행중일 때 페이지가 전환되면 오류가 발생할 수 있다.예를 들면, 이벤트가 발생했는데, response보다 빠르게 페이지가 전환되어 더이상 화면에 없는 component의 요소를 업데이트 해야 하는 상황이 발생할 수 있다.

이런 경우에는 실제로 진행 중인 HTTP request를 취소할 수 있어야 하는데 최신 브라우저에서 제공하는 AbortController라는 생성자 함수가 존재한다.

import {useState,useEffect,useCallback,useRef} from 'react'

export const useHttp = () => {
  const [isLoading,setIsLoading] = useState(false);
  const [error,setError] = useState();
  

 //AbortController
  // 1. 현재 진행중인 HttpRequest를 담을 수 있는 배열을 생성하고 useRef에 담아준다.
  // 부수작업이고, UI를 업데이트 하기 위한 작업이 아니기에 useState보다는 useRef가 적합하다.
  // useRef를 통해 화면에는 보이지 않게 useHttp hook을 사용하는 compoenent가 랜더링 될 때마다 다시 실행된다.
  const activeHttpRequests = useRef([]);

  const sendRequest = useCallback( async(url, method= 'GET',body,headers={}) => {
    setIsLoading(true)
    // 2. Request를 보내기 이전에 AbortController 생성자 함수 생성
    // AbortController는 현재 활성화 된 HTTP requests를 보여준다.
    const httpAbortController = new AbortController();
    // 3. sendRequest가 실행될 때마다  activeHttpRequests에 AbortController를 push한다.
    activeHttpRequests.current.push(httpAbortController);

    try{
      const response = await fetch(url,{
        method,
        body,
        headers,
        // 4.생성된 AbortController를 가리킴으로서 해당 http Request를 취소할 수 있다.
        signal:httpAbortController.signal
      });
      const responseData = await response.json()
      
      // 5.해당 Http request에서 응답을 받게 되면 AbortController 배열에서 해당 AbortController를 삭제한다
      if(!response.ok){
        setError(responseData.message)
      }
      setIsLoading(false)
      return responseData;
    }catch(err){
      setError(err.message)
      setIsLoading(false)
      throw err;
    }
   

  },[])
  const clearError = () => {
    setError(null)
  }

  useEffect(()=>{
    // 6. clean up function을 통해 다음 sendRequest가 실행 되기 전에,
    // 정확히는 useHttp hook이 unmount 될 때 ref에 저장된 AbortController를 삭제한다.
    return () => 
    {
      activeHttpRequests.current.forEach(abortController => abortController.abort())
    };
  },[])

  return {
    error,
    isLoading,
    sendRequest,
    clearError
  }
}

1.abort()
abort()가 호출되면, fetch() promise는 AbortError으로 명명된 DOMException과 함께 reject된다.
2. AbortController.signal
AbortController 인터페이스의 signal은 읽기 전용 프로퍼티로서
DOM 요청과 통신하거나 취소하는데 사용하는 AbortSignal 객체 인터페이스를 반환한다.

0개의 댓글