axios 응답 인터셉터로 리턴 값 변경 시 타입에러 발생

화영화영
3 min read

🐞 문제상황

axios에서 interceptor를 사용해서 데이터를 바로 리턴해주도록 설정했다. 그런데 axios에서 리턴 받은 데이터 객체의 속성을 바로 사용하려고 하니 타입에러가 발생했다. 그리고 인터셉터를 적용하지 않은 것처럼 data.data.속성명을 작성했더니 타입에러가 뜨지 않았다.

// utils/api.ts
const api = axios.create({
  baseURL: 'http://baseurl',
  timeout: 1000 * 30,
})

api.interceptors.response.use(
  (response) => {
    return response.data // 응답으로 받은 response의 data를 리턴값으로
  },
)

배경지식 1. interceptor

  • 인터셉터란?
    axios로 요청 또는 응답을 처리하기 전(then 또는 catch 전)에 공통적인 로직을 설정해주는 것.
    보통 요청 인터셉터에서는 공통 헤더를 추가하는 작업을,
    응답 인터셉터에서는 리턴 데이터 가공, 특정 응답상태 처리, 에러처리 공통화 등의 작업을 한다.
// 요청 인터셉터 - 헤더에 토큰 추가
api.interceptors.request.use(
  (config) => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 응답 인터셉터 - 리턴 데이터 가공 및 특정 에러상태 처리
api.interceptors.response.use(
  (response) => {
    return response.data
  },
  (error) => {
    if (error.response.status === 401) {
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)
  • 여기서 요청 인터셉터에서는 토큰을 가져와 헤더의 Authorization에 추가해서 config 객체를 수정한다(이 작업은 서버와 클라이언트 간의 인증이 필요한 요청을 처리할 수 있도록 인증정보를 포함하는 작업이다)
    - config란? Axios 요청객체. 요청을 만드는데 필요한 옵션들이 있다. method, baseURL, header, params 등

      // config(api 요청 시 필요한 옵션들이 있는 객체)
      {
        url: '/user',
        method: 'get',
        baseURL: 'https://some-domain.com/api',
        headers: {'X-Requested-With': 'XMLHttpRequest'},
        params: {
          ID: 12345
        },
      }
    

💡 해결과정

  • 코드에서 보는 것처럼 응답 인터셉터에서 return response.data를 하고 있지만 실제로 api 인스턴스의 리턴 객체는 반환을 AxiosResponse로 하기때문에 data 다음 속성을 바로 사용할 수 없었다.
    정확하게는 실행 시 에러가 나지 않았지만 코드에서는 타입에러가 발생한 것이다.
// PostRequest.tsx
const data = await api.post<LoginResponse>('/login', formValue)

if (data.init) {  // data.init에서 타입에러
  onOpen()
  return
}
  • 이 프로젝트는 타입스크립트를 사용하는데, 인터셉터는 런타임에서 동작하므로 타입스크립트에서는 인터셉터의 반환값을 알 수 없었다. 그래서 axios의 기본타입인 AxiosReponse<T>를 반환해서 타입에러가 났던 것.

  • 런타임 이후에는 인터셉터가 작동을 했으므로 실제로 코드 실행 후에는 에러가 나지 않는다

배경지식 2. 타입스크립트와 런타임

  • 타입스크립트 : 정적 타입 검사 도구로 코드 실행 전(런타임 전)에 발생하는 에러를 경고한다
    타입스크립트는 자바스크립트로 컴파일된 후 코드가 실행되는데, 이 컴파일 시간에 타입 오류를 확인한다

  • 런타임 : 코드가 동작하고 결과가 나오기까지의 시간
    js코드가 브라우저 또는 node.js 환경에서 실행될 때 발생하는 모든 작업 ex) 서버요청, 데이터처리

수정방향

  • 타입스크립트에 인터셉터로 인해 반환값이 변경된 사실을 알려줘야한다.
  1. Axios 인스턴스의 반환 타입을 명시적으로 지정한다.
import axios, { AxiosInstance } from 'axios'

interface CustomAxiosInstance extends AxiosInstance {
  post<T = any, R = T>(url: string, data?: any, config?: any): Promise<R>
}

const api: CustomAxiosInstance = axios.create({
  baseURL: 'http://192.168.3.29:7010',
  timeout: 1000 * 30,
})
  • 위의 코드처럼 타입을 지정하면 api.post<LoginResponse> 반환값은 LoginResponse로 추론되어 에러가 발생하지 않는다.
  1. 리액트쿼리 사용 : axios 인스턴스의 반환타입을 지정해주지 않아도 리액트쿼리에서는 제네릭 타입 매개변수를 받아 반환값 타입을 명시적으로 정의가 가능하다.
  const { mutate } = useMutation<LoginResponse>({
    mutationFn: () => api.post('/users/login', { id: watch('id'), pw: watch('pw') }),
  })

🎈 결과

어떤 개념을 정확히 알기위해서는 그 개념의 기초 개념을 알고 있어야한다. 매번 모든 개념을 정확히 이해하기는 어렵겠지만 내가 맞닥뜨린 문제에 대해서는 명확하게 알고 있어야한다. 이번처럼 시간이 조금 오래걸리더라도 문제에서 파생된 개념을 이해하고 넘어가도록 해야겠다.

0
Subscribe to my newsletter

Read articles from 화영 directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

화영
화영