
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
import axios from 'axios';
import store from "../../views/pages/AppStore"; // Vuex 스토어 임포트
// JWT 토큰 디코더
function decodeToken(token) {
try {
const base64String = token.split('.')[1];
const base64 = base64String.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(atob(base64).split('').map(c =>
'%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
).join(''));
return JSON.parse(jsonPayload);
} catch (e) {
console.error("Invalid token", e);
return null;
}
}
// 모든 클라이언트에 적용될 요청 인터셉터
const requestInterceptor = config => {
// skipAuthRefresh는 요청 시점에 config에 직접 추가하여 토큰 재발급 로직을 건너뛰게 할 때 사용
if (store.state.authorization) {
config.headers.Authorization = store.state.authorization;
}
return config;
};
// 모든 클라이언트에 적용될 응답 인터셉터
const responseInterceptor = async error => {
const originalReq = error.config;
if (!error.response) return Promise.reject(error);
// 로그인 요청 (skipAuthRefresh 플래그 확인)
if (originalReq?.skipAuthRefresh) {
return Promise.reject(error);
}
// 권한 없음
if (error.response.status === 403 && error.response.data.message === '접근 권한이 없습니다.') {
alert('접근 권한이 없습니다.'); // 사용자에게 메시지 알림
window.history.back(); // 뒤로 가기
return Promise.reject(error);
}
// 리프레시 토큰 요청 자체는 재시도하지 않음
if (originalReq.url.includes('/refresh/tknReissue.json')) {
return Promise.reject(error);
}
// 토큰 만료 시 한 번만 재시도 (401 에러)
if (error.response.status === 401 && !originalReq._retry) {
originalReq._retry = true; // 재시도 플래그 설정
try {
// 리프레시 토큰으로 새 토큰 발급 요청
const res = await axios.post('/refresh/tknReissue.json', {});
const newToken = res.headers.authorization;
// Vuex 스토어에 새 토큰 저장
store.commit('setAuthorization', newToken);
originalReq.headers.Authorization = store.state.authorization; // 재시도 요청의 헤더 업데이트
// 유저 정보 다시 디코딩하여 스토어에 저장 (새 토큰 기준으로)
const user = decodeToken(newToken);
store.commit("setUserInfo", {
userNm: user.userNm,
loginId: user.loginId,
userId: user.userId,
roles: Array.isArray(user.roles) ? user.roles.map(r => r.authority) : [],
});
// 실패했던 원본 요청 재시도
return axios(originalReq); // axios 인스턴스에 원래 요청 그대로 전달하여 재시도
} catch (refreshError) {
// 리프레시 실패 시 (세션 만료 등) 로그인 페이지로 강제 이동
sessionStorage.setItem("redirect", window.location.pathname + window.location.search);
alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.');
store.commit("setStoreReset");
localStorage.clear();
sessionStorage.clear();
window.location.href = '/login.page';
return Promise.reject(refreshError);
}
}
// 그 외 모든 에러는 그대로 reject
return Promise.reject(error);
};
const createConfiguredClient = (contentType) => {
const client = axios.create({
baseURL: '/', // 모든 API 요청에 기본으로 붙을 URL
headers: {
'Content-Type': contentType,
},
});
// 요청 및 응답 인터셉터 적용
client.interceptors.request.use(requestInterceptor, error => Promise.reject(error));
client.interceptors.response.use(response => response, responseInterceptor);
return client;
};
// JSON 데이터 요청을 위한 클라이언트
const apiClient = createConfiguredClient('application/json; charset=UTF-8');
// 멀티파트(파일 업로드) 요청을 위한 클라이언트
const fileClient = createConfiguredClient('multipart/form-data');
// 모듈 외부로 내보내기
export { apiClient, fileClient };