reduxjs/toolkit+axios에서 RTK-query로 변환하기- accessToken, RefreshToken

maliethy·2021년 12월 26일
0

react

목록 보기
1/7

'#1(이전 포스트 참고)'은 새로고침 시에도 token유지를 위해 react-cookies에 accessToken, RefreshToken를 저장하고 각 token의 만료를 axios interceptor로 확인한 버전이다. '#2'에서는 이를 RTK-query로 변환해보았다.

1. reduxjs/toolkit with axios

export const axiosApiInstance = axios.create({
  baseURL: baseURL,
  withCredentials: true,
});

export const axiosApiRefreshToken = axios.create({
  baseURL: baseURL,
  withCredentials: true,
});

axiosApiInstance.interceptors.request.use(
  (config) => {
    const accessTokenByCookies = reactCookies.load('accessToken');

    config.headers = {
      Authorization: `Bearer ${accessTokenByCookies}`,
      Accept: 'application/json',
    };
    return config;
  },
  (error) => {
    Promise.reject(error);
  },
);

axiosApiRefreshToken.interceptors.request.use(
  (config) => {
    const refreshTokenByCookies = reactCookies.load('refreshToken');

    config.headers = {
      Authorization: `Bearer ${refreshTokenByCookies}`,
      Accept: 'application/json',
    };
    return config;
  },
  (error) => {
    Promise.reject(error);
  },
);

axiosApiInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  async function (error) {
    const originalRequest = error.config;
    const refreshTokenByCookies = await Promise.resolve(reactCookies.load('refreshToken'));

    if (error.response?.status === 401 && originalRequest.url === '/Web/RefreshToken') {
      console.log('Prevent infinite loops');
      return Promise.reject(error);
    }

    if (error.response?.status === 401 && refreshTokenByCookies) {
      try {
        if (refreshTokenByCookies) {
          const response = await axiosApiRefreshToken.get('/Web/RefreshToken');
          const newAccessToken = response.data.accessToken;
          const omsIDByCookies = reactCookies.load('omsID');

          setToken(newAccessToken, refreshTokenByCookies, omsIDByCookies, response.data.accessTokenExpireTime);
          originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;

          return axios(originalRequest);
        } else {
          // console.log('refreshToken 만료1 - access갱신 후에도 에러');
          message.warn('세션이 만료되어 다시 로그인이 필요합니다.', 4);
        }
      } catch (error) {
        // console.log('refreshToken 만료2 - 다른 기기 로그인');
               message.warn('다른 기기에서 로그인되었습니다. 다시 로그인해주세요.', 4);
      }
    }
    return Promise.reject(error);
  },
);

2. migrating to RTK-query

const baseQuery = fetchBaseQuery({
  baseUrl: baseURL,
  credentials: 'include',
  prepareHeaders: (headers, { getState }) => {
    
    const accessTokenByCookies = reactCookies.load('accessToken');
    // console.log('accessTokenByCookies', accessTokenByCookies);

    if (accessTokenByCookies) {
      headers.set('Authorization', `Bearer ${accessTokenByCookies}`);
    }
    return headers;
  },
});

const baseQueryWithIntercept: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
  args,
  api,
  extraOptions,
) => {
  let result = await baseQuery(args, api, extraOptions);

  if (result.error && result.error.status === 401) {
    const refreshTokenByCookies = reactCookies.load('refreshToken');
    const refreshTokenResult: QueryReturnValue<any, FetchBaseQueryError, FetchBaseQueryMeta> = await baseQuery(
      {
        url: 'Web/RefreshToken/',
        headers: {
          Authorization: `Bearer ${refreshTokenByCookies}`,
        },
      },
      api,
      extraOptions,
    );
    console.log('refreshTokenResult', refreshTokenResult);
    if (refreshTokenResult.data) {
      const omsIDByCookies = reactCookies.load('omsID');
      setToken(
        refreshTokenResult.data?.accessToken,
        refreshTokenByCookies,
        omsIDByCookies,
        refreshTokenResult.data?.accessTokenExpireTime,
      );
      result = await baseQuery(args, api, extraOptions);
    } else {
      //refreshToken도 만료되었으므로 로그아웃하기
      message.warn('자동 로그인 기간이 만료되어 다시 로그인이 필요합니다.', 4);
    }
  }
  return result;
};

export const rtkApi = createApi({
  baseQuery: baseQueryWithIntercept,
  reducerPath: 'rtkApi',
  tagTypes: ['User'],
  endpoints: (build) => ({
   ...생략
});

export const { ...생략 } = rtkApi;

참고:
https://stackoverflow.com/questions/68581163/redux-toolkit-query-interceptor
https://cdmana.com/2021/09/20210924091527593D.html

profile
바꿀 수 있는 것에 주목하자

0개의 댓글