Skip to content

axios interceptor커스텀하기

jinn2u edited this page Dec 13, 2021 · 2 revisions

axios interceptor 커스텀하기

이번에 axios interceptor를 사용하여 중복되는 header들을 하나로 합치고자 했다.

따라서 맨처음에 생각한 로직은

  1. 토큰을 페이지에서 불러온다.
  2. 토큰을 config에 넣어보낸다.
  3. 인터셉터에서 config.token을 Authorization키의 값을 넣는다.

이렇게 생각했던 이유는 인터셉터에서 토큰을 localstorage에서 가져오는것은 인터셉터의 책임이 아니라고 생각했다.

(하지만 추후에 논의한 결과 인터셉터안에서 토큰을 불러오는것이 더 효율적이라는 의견으로 통일되어 변경하였다.)

코드를 만들었던 순서

내가 설계했던 방법은 처음부터 어떻게 페이지에서 사용할 것이다. 라고 정의 후 인스턴스를 만드는 것이었다.

따라서 페이지에서는 아래와 같이 닉네임과 토큰을 파라미터로 전달해주고 싶었다.

//토큰을 localstorage에서 불러오는 로직
signUpApi(nickname, token)

따라서 나는 우선 인스턴스 만들어 주었다.

const createAuthInstance = () => {
  const instance = axios.create({ baseURL: process.env.REACT_APP_BASE_URL });
  authInterceptor(instance);
  return instance;
};

export const authInstance = createAuthInstance();

그 후 아래와 같은 함수를 만들어 주었다.

authInstance.post('/api/v1/users/signup', { nickname }, { token })

만난 에러

하지만 이렇게 했더니 아래와 같은 에러가 나왔다.

Untitled

따라서 axios에 정의되어있는 타입들을 보며 하나하나 맞춰나갔다.

시행착오

맨처음에 axiosInstance를 사용하니 그 타입을 상속받아서 재정의하면 간단히 해결될 줄 알았다.

// 정의되어있는 AxiosInstance
interface AxiosInstance extends Axios {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
}
//정의되어있는 axiosRequestConfig
export interface AxiosRequestConfig<D = any> {
  url?: string;
  method?: Method;
  baseURL?: string;
 ...
}

// 재정의하기

interface CustomAxiosRequestConfig extends AxiosRequestConfig{
	token: string
}
interface CustomAxiosInstance extends AxiosInstance{
  (config: CustomAxiosRequestConfig): AxiosPromise;
  (url: string, config?: CustomAxiosRequestConfig): AxiosPromise;
}

하지만 이렇게 함에도 불구하고 에러는 여전히 사라지지 않았다.

따라서 axiosInstance가 상속받고 있는 Axios를 살펴보았다.

export class Axios {
  constructor(config?: AxiosRequestConfig);
  defaults: AxiosDefaults;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  getUri(config?: AxiosRequestConfig): string;
  request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
  get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  delete<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  head<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  options<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
  post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  put<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
  patch<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
}

이렇게 정의가 되어있는데, 우리가 결국 사용하는 axiosInterceptor의 config는 이 Axios때문에 AxiosRequestConfig타입을 유지하고 있어 타입에러가 났다고 생각했다.

해결방법

따라서 내가 해결한 방법은

type.ts 파일안에

export interface CustomAxiosRequestConfig extends AxiosRequestConfig {
  token: string;
}
export interface CustomInstance extends AxiosInstance {
  interceptors: {
    request: AxiosInterceptorManager<CustomAxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse<any>>;
  };
  getUri(config?: CustomAxiosRequestConfig): string;
  request<T>(config: CustomAxiosRequestConfig): Promise<T>;
  get<T>(url: string, config?: CustomAxiosRequestConfig): Promise<T>;
  delete<T>(url: string, config?: CustomAxiosRequestConfig): Promise<T>;
  head<T>(url: string, config?: CustomAxiosRequestConfig): Promise<T>;
  options<T>(url: string, config?: CustomAxiosRequestConfig): Promise<T>;
  post<T>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<T>;
  put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
  patch<T>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<T>;
}

이렇게 instance가 사용할 메서드들의 configType을 재정의 해주었다.

그 후 아래와 같이 instance에 대한 타입 단언을 해주었다.

const createAuthInstance = () => {
  const instance = axios.create({ baseURL: process.env.REACT_APP_BASE_URL }) as CustomInstance;
  authInterceptor(instance);
  return instance;
};

마지막으로 인터엡터에서 instance의 타입을 지정해주면 된다!

const authInterceptor = (instance: CustomInstance) => {
  instance.interceptors.request.use((config) => {
    config.headers = {
      authorization: `Bearer ${config.token}`,
      ...config.headers,
    };
    return config;
  });
};

사실 이 프로젝트를 하면서 타입스크립트를 처음 하다 보니 이렇게 하는것이 올바른 방법인지는 잘 모르겠다,,😅😅

또한 결국 token을 가져오는 로직을 페이지에서 가져오는것이 아니라 interceptor안에서 일괄적으로 처리를 해주었기 때문에 현재는 더이상 custom interceptor가 필요한 상황은 아니다.

하지만 언제 다시 커스텀을 해야할지 모르기 때문에 기록은 해두자..!

Clone this wiki locally