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