[Web] Fetch API와 에러 핸들링

장유진·2023년 7월 18일
0

Web Development

목록 보기
3/5

Project Restoration - 댕근이다옹(DangGeuneDaong)

프로젝트에서는 데이터 패칭 라이브러리로 React-Query를 채택하고 fetch API를 사용해 요청을 보내고 있었습니다. MOCK Server로 API 요청까지 성공한 후, 실제 API로 연동을 시작하게 되었는데요. 에러 핸들링을 위해 고의로 존재하지 않는 주소로 요청을 보냈는데, onSuccess가 트리거되는 이상한 현상을 맞닥뜨리게 되었습니다.

문제가 된 코드는 다음과 같습니다.

export const addPost = async (data: PostModel) => {
    try {
      const response = await fetch('/good/offer/info', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
          'Content-Type': 'application/json',
        },
      });
    } catch (error) {
			...
    }
  };

이 문제를 해결하기 위해 처음 시도한 방법은 에러의 종류를 구분해야 할 필요성이 있었기 때문에 response.status를 이용해서 직접 에러를 발생시키는 것이었습니다. 하지만 예외 처리는 핵심 로직과 분리해 catch 블록 안에서 처리하기를 원했기 때문에 왜 이런 문제가 발생했는지 알아보고, 다른 방법을 찾기로 했습니다.

const addPost = async (data: PostModel) => {
    try {
      const response = await fetch('/good/offer/info', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: {
          'Content-Type': 'application/json',
        },
      });

      switch (response.status) {
        case 400:
         throw new Error('Error');
				...
      }
    } catch (error) {
			...
    }
  };

문제 원인

fetch() 프로미스는 네트워크 오류(보통 권한 문제 등)가 있을 때만 거부되며, 404 등의 HTTP 오류 시에는 거부되지 않습니다. 그러므로 then() 처리기는 반드시 Response.ok또는 Response.status 속성을 확인해야 합니다. - MDN

❗️문제의 원인은 fetch API로 데이터를 전송하고 있기 때문!

Fetch API는 네트워크 오류 혹은 CORS 에러가 아니라면 rejected 상태를 반환하는 대신 response.ok가 false인 resolve 상태를 반환합니다. 즉 HTTP 상태 코드가 2xx이 아니라도 catch 블록으로 넘어가지 않습니다. 만약 4xx 또는 5xx 상태 코드에 대한 HTTP 에러 핸들링이 필요하다면, 개발자가 직접 에러를 반환해주어야 합니다.

async function fetchImage() {
  try {
    const response = await fetch("flowers.jpg");
    if (!response.ok) {
      throw new Error("네트워크 응답이 OK가 아님");
    }
    const myBlob = await response.blob();
    myImage.src = URL.createObjectURL(myBlob);
  } catch (error) {
    console.error("취득에 문제가 있었습니다:", error);
  }
}

문제 해결

HTTP 코드가 2xx가 아니라면 rejected 된 상태를 반환하는 라이브러리 Axios를 도입하게 되었습니다.

❗️Axios는 브라우저 또는 Node.js에서 API 요청을 할 수 있는 HTTP 클라이언트 라이브러리로 HTTP 요청 전송, 요청 및 응답 인터셉터 처리, 헤더 설정 및 오류 처리등 다양한 기능을 지원합니다. 더 자세한 내용은 이전 포스트를 확인해주세요.

주의해야 할 점은 catch 블록 안에서 throw 객체로 에러를 다시 발생시키거나 거부된 Promise를 명시적으로 반환하지 않는다면 함수를 호출한 React Query에서 에러를 인지할 수 없기 때문에 HTTP 에러가 발생해도 onSuccess가 계속 트리거된다는 것입니다. 수정한 코드는 다음과 같습니다.

export const addPost = async (data: PostModel) => {
  try {
    const response = await instance.post(
      '/good/offer/info',
      formData,
    );
    return response;
  } catch (error: any) {
    **throw new Error(error);** 
  }
};


Reference
https://developer.mozilla.org/ko/docs/Web/API/fetch
https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Using_Fetch

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

훌륭한 글이네요. 감사합니다.

답글 달기