Axios Interceptors를 이용하여 Loading 구현하기 with TS

이재찬·2022년 9월 2일
0
post-thumbnail

내가 Axios Interceptor를 사용하는 방법

Pinia

common.ts

import { defineStore } from "pinia";

interface State {
  loadingCnt: number;
  loading: boolean;
}

export const useCommonStore = defineStore("common", {
  state: (): State => {
    return {
      loadingCnt: 0,
      loading: false,
    };
  },
  actions: {
    startLoading() {
      this.loadingCnt += 1;
      this.loading = true;
    },
    endLoading() {
      this.loadingCnt -= 1;
    },
    cancelLoading() {
      this.loadingCnt = 0;
      this.loading = false;
    },
  },
});
  

Axios

Index.ts

import axios, { AxiosInstance } from "axios";
import { setupInterceptors } from "./interceptors";

export const createInstance = (): AxiosInstance => {
  const instance = axios.create({
    baseURL: "https://api.thecatapi.com/v1/images/search", // url
    timeout: 15000,
  });

  return setupInterceptors(instance);
};

Interceptors.ts

import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
} from "axios";

import { logOnDev } from "@/helpers/loghandler";
import { useCommonStore } from "@/store/common";

const onLoading = async (type: string) => {
  const commonStore = useCommonStore();
  const { startLoading, endLoading, cancelLoading } = commonStore;

  switch (type) {
    case "start": {
      startLoading();
      break;
    }
    case "end": {
      endLoading();
      break;
    }
    case "cancelLoading": {
      cancelLoading();
      break;
    }
    default: {
      break;
    }
  }
};

const onRequest = (config: AxiosRequestConfig): AxiosRequestConfig => {
  const { method, url } = config;
  logOnDev(`🚀 [${method?.toUpperCase()}] ${url?.toUpperCase()} | START`);
  onLoading("start");
  return config;
};

const onRequestError = (error: Error | AxiosError): Promise<AxiosError> => {
  logOnDev(`🚨 ${error.message}`);
  onLoading("cancel");
  return Promise.reject(error);
};

const onResponse = (response: AxiosResponse): AxiosResponse => {
  const { status } = response;
  const { method, url } = response.config;
  logOnDev(
    `🎉 [${method?.toUpperCase()}] ${url?.toUpperCase()} | SUCCESS ${status}`
  );
  onLoading("end");
  return response.data;
};

const onResponseError = (error: AxiosError | Error): Promise<AxiosError> => {
  if (axios.isAxiosError(error) && error.response) {
    const { statusText, status } = error.response;
    const { method, url } = error.config;
    logOnDev(
      `🚨 [${method?.toUpperCase()}] ${url?.toUpperCase()} | ${statusText} ${status}`
    );
  } else {
    logOnDev(`🚨 ${error.message}`);
  }
  onLoading("cancel");
  return Promise.reject(error);
};

export const setupInterceptors = (
  axiosInstance: AxiosInstance
): AxiosInstance => {
  axiosInstance.interceptors.request.use(onRequest, onRequestError);
  axiosInstance.interceptors.response.use(onResponse, onResponseError);
  return axiosInstance;
};

Api.ts

import { createInstance } from "@/api/index";
import axios from "axios";
  
interface CatDataType {
  id: string;
  height: number;
  url: string;
  width: number;
}
  
const getCatImg = async (): Promise<CatDataType> => {
  try {
    return await createInstance().get("/v1/images/search");
  } catch (error) {
    if (axios.isAxiosError(error)) {
      throw error;
    } else {
      throw new Error("different error than axios");
    }
  }
};

Success

Error

profile
Jr.Frontend Developer / Vue.js Developer

2개의 댓글

comment-user-thumbnail
약 23시간 전

코드 구조 잡을 때 유용했습니다 좋은 글 감사해요! 기능단위로 분리되어있어서 아주 깔끔정리 되었습니다.

답글 달기
comment-user-thumbnail
약 23시간 전

코드 구조 잡을 때 유용했습니다 좋은 글 감사해요! 기능단위로 분리되어있어서 아주 깔끔정리 되었습니다.

답글 달기