API 에러 핸들링 이야기(1)

Hi·2023년 4월 16일
9

API 에러를 어떻게 처리해야 할까?

우리는 개발을 하면서 수많은 에러를 만나게 됩니다.
컴파일 에러,타입 에러, 네트워크 에러등 에러 없이 코딩하기는 사실 불가능한 일입니다.

그중에서도 우리가 가장 많이 에러를 다루게 되는건 아마 api에러라고 생각합니다.

try catch문이나 then등을 이용해서 api 에러들을 잘 잡아내면서 개발을 해야 하는데 생각보다 적재적소에 쓰기도 어렵고 원하는대로 사용하기 어렵습니다.

기존에 있던 서비스에서 개발을 할때는 몰랐지만 새롭게 서비스를 만들다 보니 에러 핸들링이 필요했고 이러한 에러 핸들링들을 어떤 방식으로 구현했는지 정리해보려고 합니다.

우리가 가장 쉽게 할 수 있는 에러 핸들링은 무엇일까요?


const getData = async (url) => {
	try {
		const res = await fetch(url);
    	const data = await res.json();
    }catch(e){
    	throw Error(e)
    }
}

아마 이런 형태로 대부분 에러 핸들링을 해보셨을겁니다.

간단한 프로젝트를 만들거나 api 호출이 많지 않은 경우에는 이런 방식으로 해도 문제가 되지 않을 겁니다.

하지만 프로젝트가 계속해서 커지다 보면 api마다 다른 에러 처리를 해줘야 할 상황이 있을 수도 있고 컴포넌트 단위에서 처리하지 않고 전역으로 에러를 위임해줘서 처리를 하는등 다양한 상황이 발생합니다.

이럴 때 axios에서 제공하는 interceptor를 사용하면 에러 처리가 쉬워지게 됩니다.

interceptor는 api 요청 하거나 응답을 받을 때 미리 선제적인 처리를 할 수 있게 axios에서 제공하는 함수입니다.


const BASE_URL = 'www.somewhere.com/api/data';

const instance = axios.create({baseURL:`${BASE_URL}`});

instance.interceptors.request.use((req)=>{
	//요청시 필요한 작업 처리
    //ex) 토큰 불러오기, 기본 헤더 설정
})

instance.interceptors.response.use((req)=>{
	//응답 받을 때 필요한 작업 처리
    //ex) 데이터 구조로 인한 분리, 에러 처리
})

기본적인 구조는 이런 방식입니다. api요청시에는 인터셉트해서 access token이나 추가적인 헤더를 넣어 요청할 수도 있고 응답을 받을 때는 에러 핸들링을 하거나 데이터를 정제하는 과정을 거칠 수도 있습니다.

앞선 interceptor를 활용한 방식도 좋지만 진행했던 프로젝트에서는 react-query를 활용했기 때문에 queryClient에 관한 관리도 필요했고 graphqlRESTAPI를 번갈아가면서 사용했기 때문에 interceptor에서 처리하는 것보다는 추가적으로 api요청 함수를 만들어서 관리하는 것이 더 좋은 방법이라고 생각했습니다.

그래서 새롭게 에러 처리를 할 수 있는 함수를 만들었습니다


export const restApi = async <Data = unknown>({
  path,
  requestBody,
  axiosConfig = {},
  queryClient,
}: ApiProps): Promise<Data> => {
  const tokenFromCookie = //queryClient에서 토큰 가져오는 로직
  const header = {
    //헤더 설정
  };
  try{
        const res = await axiosInstance({
        url: path,
        ...axiosConfig,
        ...requestBody,
        ...header),
      });
      return res.data;
  }catch (e: unknown) {
      if () {
        //refresh 로직
        //재요청이 가능한 경우 재요청
        return restApi<Data>({
          path,
          requestBody,
          axiosConfig: {
            ...axiosConfig,
            headers: { ...header,
          },
          queryClient,
        });
      } 
      return interceptor(e);
    }
  };

코드를 조금 살펴보면 axiosInstance를 만들어서 요청을 하고 그 과정에서 에러가 생기면 catch에서 처리를 해주었습니다. catch안에서는 accessToken이 만료되어서 refresh과정을 거친후에 재요청을 해주는 과정이 담겨 있습니다.

이렇게 만든 함수에서는 queryClient를 활용해서 refresh과정을 수행하는 작업도 가능해졌습니다.

graphql를 활용하기 위한 함수도 위와 같은 형태로 만들었습니다. 하나의 함수로 통일하고 싶었지만 추가적으로 다른 처리들이 필요했기 때문에 분리해서 사용했습니다.

다음 장에서는 SSR환경에서 어떻게 에러를 처리했는지 알아보겠습니다.

profile
Hola!

0개의 댓글