2차 공부/TIL

24.09.05 axios instance interceptor

공대탈출 2024. 9. 5. 01:44

axios instance

axios에서 제공해주는 create메서드를 사용하면 공통으로 사용하는 axios config를 묶어서 설정해둘 수 있다.

config

config엔 이전에 BASE_URL로 빼두었던 것과 같은 반복되어 동일하게 사용되는 값을 미리 지정해 여러 군데에서 사용할 수 있다.

const BASE_URL = "http://localhost:4000";

const onSubmitHandler = async (todo) => {
    await axios.post(BASE_URL + "/todos", todo).then(({ data }) => setTodos([...todos, data]));
};

 

export const api = axios.create({
    baseURL: "http://localhost:4000",
});

지정하고 싶은 config 키/밸류값을 객체 형태로 넣어주고 해당 api를 여러 HTTP요청에서 사용하는 것이다.

const onSubmitHandler = async (todo) => {
    await api.post("/todos", todo).then(({ data }) => setTodos([...todos, data]));
};
const onDeleteHandler = async (id) => {
    await api.delete(`/todos/${id}`).then(({ data }) => setTodos(todos.filter((todo) => todo.id !== data.id)));
};
const onEditHandler = async (e, targetId, editTodo) => {
    e.preventDefault();
    await api.patch(`/todos/${targetId}`, editTodo).then(({ data }) =>
        setTodos(
            todos.map((todo) => {
                return todo.id === data.id ? data : todo;
            })
        )
    );
};

여기선 baseURL만 설정하여 쓸모없다고 생각이 들긴 하지만, onUploadProcess/onDownloadProcess를 사용해 업로드, 다운로드 시 특정 작업을 할 수 있고, 서버와의 최대 데이터통신 시간을 설정하는 timeout을 지정할 수 있다.

 


axios interceptor

axios HTTP 요청 중간에 원하는 작업을 할 수 있는 interceptor도 사용이 가능하다.

출처 - https://javascript.plainenglish.io/how-to-implement-a-request-interceptor-like-axios-896a1431304a

 

api.interceptors.request.use((config) => {
    console.log("config :>> ", config);
    return config;
});

request interceptor는 클라이언트에서 서버로 요청이 가기 전에 원하는 작업을 해준다.

api.interceptors.request.use(function (config: any) {
  const accessToken = localStorage.getItem('accessToken');
  const refreshToken = localStorage.getItem('refreshToken');
  //로컬스토리지 토큰을 헤더에 집어넣음
  if (config.headers && accessToken && refreshToken) {
    config.headers.accesstoken = `${accessToken}`;
    config.headers.refreshtoken = `${refreshToken}`;
  }

  return config;
});

이전에 작업했던 코드인데, 로컬 스토리지에서 각 토큰을 가져와 config의 헤더에 넣어 API명세서에 알맞게 백엔드 서버와 통신을 가능하게 했던 requset interceptor이다.

 

api.interceptors.response.use(
    (response) => {
        console.log("response :>> ", response);
        return response;
    },
    (error) => {
        console.log("error :>> ", error);
        return Promise.reject(error);
    }
);

response interceptor는 서버에서 데이터가 클라이언트에게 오기 전 원하는 작업을 가능하게 한다. 인자로는 두 함수를 받으며 각각 promise의 resolve, reject처럼 사용된다.

첫 함수는 HTTP상태코드가 2XX일 때 작동하고, 두번째 함수는 그 외 상태코드에 작동한다.

 

api.interceptors.response.use(
  function (config: any) {
    // access토큰 만료시 재발급받은 토큰을 다시 넣어줌
    if (config.headers && config.headers.accesstoken) {
      localStorage.setItem('accessToken', config.headers.accesstoken);
      ReactGa.event({
        category: '토큰',
        action: `accesstoken 재발급`,
      });
    }

    return config;
  },
  function (error) {
    console.log(error);
    const { status, data } = error.response;

    if (
      status === 419 ||
      data.errorMessage ===
        'RefreshToken이 조작되었습니다. 다시 로그인해주세요.'
    ) {
      localStorage.clear();
      window.location.href = '/login';
    }

    if (status === 419 || status === 400 || status === 412 || status === 404) {
      ReactGa.event({
        category: '에러발생',
        action: `에러status: ${status}, 에러메시지: ${data.errorMessage} `,
      });
    }

    // 요청 오류가 있는 경우 작업 수행
    return Promise.reject(error);
  },
);

위 코드도 이전에 작성했던 것인데, 여기서 HTTP통신이 이뤄질 때마다 accesstoken을 재발급 했던 것으로 기억한다. 백엔드에서 header에 넣어서 보내준 accesstoken을 로컬스토리지의 accesstoken에 업데이트 해준 것이다.

에러가 발생했을 때는 상태코드에 따라 작업을 달리 하였다. 사용자의 refreshtoken이 백엔드에서 검사했을 때 조작된 값이라면 상태코드 419를 보내주어 재로그인을 유도하고, 그 외 에러는 페이지 사용 분석 라이브러리에 데이터를 넘겨준 것이다.