티스토리 뷰

 

 

참여하고 있는 프로젝트에서 UI 구현을 마치고 API 연동을 하고 있는 중이다. 에러 처리를 위해 Axios Interceptor를 사용했는데, 이 과정에서 AxiosError를 처리했던 과정을 기록해보려 한다.

 

 

초기 코드


초기에 작성한 에러 처리 코드는 다음과 같았다.

if (error) {
    const errorMessage = error instanceof Error ? error.message : '알 수 없는 에러가 발생했습니다.';
    return <div>{errorMessage}</div>;
}

 

이 코드에 대해 리뷰를 받으면서, 공통 모듈로 처리할 수 있는지에 대한 피드백을 받았다. 처음에는 이 부분을 별도의 공통 컴포넌트로 분리하라는 의미인지, 이해가 잘 되지 않아 혼란스러웠다.

 

리뷰어분은 Axios를 직접 사용해본 경험은 없으시고 ky 라이브러리를 사용해보셔서, 유사한 기능이 Axios에 있는지 여쭤보시기 위해 관련 링크를 공유해 주셨다. 확인해보니 Axios Interceptor와 유사한 기능이라는 것을 알게되었다.

 

사실 나는 그동안 Axios Interceptor를 주로 액세스 토큰을 이용해 리프레시 토큰을 갱신하는 용도로만 사용해왔다. 에러 처리에도 활용할 수 있다는 점을 미처 생각하지 못했던 것이다.

 

그래서 구현하면서 새로운 시각을 얻게 됐고, 상당히 유용하다고 느꼈다👍 또, 구현 과정에서 Axios의 에러 핸들링에 대해 이해할 수 있는 좋은 기회가 되었다고 생각한다.

 

 

 

AxiosError 타입 지정


 

처음에 매개변수 error를 AxiosError 타입으로 지정했다.

AXIOS_INSTANCE.interceptors.response.use(
  (response) => response,
  (error: AxiosError) => {
    error.message = getErrorMessage(error);
    return Promise.reject(error);
  },
);

 

 

그런데 모든 에러가 Axios와 관련된 에러가 아닐 수 있다는 생각이 들었다. 이유는 네트워크 연결 문제나, CORS 오류 등 다양한 종류의 에러가 들어올 수 있기 때문이다.

 

검색해보니 Axios에서 제공하는 isAxiosError() 함수를 사용하여 error 객체가 AxiosError인지 확인할 수 있다는 것을 알게 되었다. 그래서 isAxiosError를 사용해서 아래와 같이 코드를 짰다.

if (axios.isAxiosError(error)) {
  error.message = getErrorMessage(error);
} else if (error instanceof Error) {
  error.message = '알 수 없는 오류가 발생했습니다.';
}

 

 

하지만 error.response.data에 접근할 때, error.response.data' is of type 'unknown'. 타입 에러가 발생했다.

const getErrorMessage = (error: AxiosError) => {
  if (error.response) {
    // 서버가 2xx 범위를 벗어나는 상태 코드로 응답한 경우
    return `요청이 실패했습니다. 상태 코드: ${error.response.status}. 오류 메시지: ${error.response.data.message || '알 수 없는 오류가 발생했습니다.'}`;
  }

  if (error.request) {
    // 요청은 전송되었으나 응답을 받지 못한 경우
    return '서버로부터 응답을 받지 못했습니다.';
  }

  // 서버 요청 중에 오류가 발생한 경우
  return '요청 설정 중 오류가 발생했습니다.';
};

 

'error.response.data' is of type 'unknown'

 

 

이 에러가 생긴 이유는 error.response.data의 타입이 unknown이기 때문에 생긴 오류이다.

이유는 unknown 타입이 가진 몇 가지 특징 때문이다.

 

 

Unknown 타입 특징


unknown 타입은 타입스크립트에서 가장 상위의 타입인데, any 타입과 같이 어떤 타입의 값이든 다 저장 할 수 있다.

 

그러나 unknown 타입의 값은 어떤 타입의 변수에도 저장할 수 없다.

let num: number = 10;
(...)

let unknownVar: unknown;
unknownVar = "";
unknownVar = 1;
unknownVar = () => {};

num = unknownVar; // 오류 !

 

 

unknown 타입은 어떤 연산도 할 수 없고, 어떤 메서드도 사용할 수 없다.

let unknownVar: unknown;
(...)

unknownVar * 2 // 오류!
아래는 예전에 any 타입과 unknown 타입의 차이점을 정리하면서 작성했던 글이다.
둘의 차이가 궁금하다면 읽어보길!

any 타입과 unknown 타입의 차이점

 

 

제네릭 타입 사용으로 문제 해결하기


isAxiosError() 함수의 타입을 확인해보니 아래와 같이 제네릭 타입을 사용하고 있었다.

export function isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>;

 

 

T가 어디로 전달되는지 확인하기 위해 AxiosError와, AxiosResponse 인터페이스를 살펴봤는데 data를 T로 받고 있었다.

interface AxiosError<T = any, D = any> extends Error {
  config: AxiosRequestConfig<D>;
  code?: string;
  request?: any;
  response?: AxiosResponse<T, D>;
  isAxiosError: boolean;
  toJSON: () => object;
}
export interface AxiosResponse<T = any, D = any> {
  data: T;
  status: number;
  statusText: string;
  headers: RawAxiosResponseHeaders | AxiosResponseHeaders;
  config: InternalAxiosRequestConfig<D>;
  request?: any;

 

 

그래서 아래와 같이 응답 데이터 타입을 제네릭 T로 넘겨주었더니 문제를 해결할 수 있었다.

const getErrorMessage = (error: AxiosError<ResponseDataType>) => {
.
.
.

 

 

또 인터셉터에서 에러를 처리하기 때문에 각 컴포넌트에서의 에러 처리 코드도 간단해졌다.

// Before
if (error) {
    const errorMessage = error instanceof Error ? error.message : '알 수 없는 에러가 발생했습니다.';
    return <div>{errorMessage}</div>;
  }
  
// After
if (error) return <div>{error.message}</div>;

 

Axios Interceptor을 토큰 갱신을 위해서만 사용해오다가 에러 처리로써 사용해보니 굉장히 유용하다고 생각한다.

 

에러 처리를 중앙으로 집중할 수 있어서 일관되게 처리할 수 있다는 점, 각 컴포넌트마다 반복적으로 에러 처리 로직을 작성하지 않아도 되는 부분이 큰 장점인 것 같다.

 

앞으로도 라이브러리나 도구 사용에 있어서 다양한 방법을 생각해보아야 겠다.

 


 

 

참고

https://github.com/axios/axios/tree/main#typescript

https://axios-http.com/kr/docs/interceptors

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함