Skip to main content

Command Palette

Search for a command to run...

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

Updated
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') }),
  })

🎈 결과

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

More from this blog

Tab Navigator에서 params에 따라 렌더링하기

React Navigation은 react native에서 화면이동(routing) 시 사용되는 라이브러리로, 페이지가 쌓이는 구조(stack 구조)를 제공한다. Stack 뿐만 아니라 Tab(하단탭), Drawer(옆에서 열리는 전체메뉴) 등의 화면 이동 방식(네비게이터)도 가지고 있다. 네비게이터들은 서로 중첩이 가능하다. 우리 프로젝트에서는 Stack과 Tab 네비게이터를 사용했고, Stack 내 하나의 화면에 Tab을 중첩하여 사용했다....

Apr 18, 20253 min read

npm run ios 에러 해결기

이전부터 리액트 네이티브 프로젝트에서 ios 시뮬레이터를 실행시키면 절대 한번에 실행되는 법이 없었다. 그래서 그때도 어떻게 에러를 해결했는지 기록해놨었는데, 그걸 참고해도 실행이 안되더라는😂 그래서 또 다시 작성한다. npm run ios로 스트레스 받지 않는 그날을 위해 1. pod install 깃에서 프로젝트를 받고 npm run ios를 하기 전 pod install(프로젝트에 필요한 라이브러리 설치)을 했다. 하지만 언제나 여기부터...

Feb 23, 20254 min read

react-hook-form을 알아보자

React-hook-form에 대해서는 input 입력 시 렌더링을 최소화하는 라이브러리 정도로만 알고 있었다. 현 회사에 입사해서 계속 리액트 훅 폼을 사용하고 있는데, 기계적으로 붙여넣기(..)만 하다가 제대로 공부해야겠다는 생각이 들어 작성하는 리액트 훅 폼 정리 글이다. 1. 제어 컴포넌트 vs 비제어 컴포넌트 제어/비제어 컴포넌트는 리액트가 실시간으로 state를 제어할 수 있는지 없는지에 따라 나눠진다. 제어 컴포넌트를 사용하게 되면...

Feb 4, 20255 min read

개발 블로그

6 posts