
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";
const apiClient = axios.create({
headers: { 'Content-Type': 'application/json; charset=UTF-8' }
});
// 동시에 여러 요청이 401 에러가 발생했을 때 관리하기 위한 변수
let isRefreshing = false;
let failedQueue = [];
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
apiClient.interceptors.request.use(
config => {
// Bearer 접두사 추가 (API 요구사항에 따라 필요한 경우)
if (store.state.authorization) {
config.headers.Authorization = store.state.authorization;
}
return config;
},
error => {
return Promise.reject(error);
}
);
async function refreshAccessToken() {
try {
// 리프레시 토큰 존재 여부 확인
if (!store.state.refreshToken) {
throw new Error('리프레시 토큰이 없습니다.');
}
console.log('토큰 재발급 시도:', new Date().toISOString());
// 서버의 구현에 맞게 요청 형식 수정
// 백엔드는 요청 바디가 아닌 쿠키에서 refreshToken을 읽으므로
// 빈 객체로 보내고 withCredentials를 true로 설정
const res = await axios.post('/refresh/tknReissue.json', {}, {
withCredentials: true,
timeout: 10000 // 10초 타임아웃 설정
});
console.log('토큰 재발급 응답:', res.status);
// 백엔드는 응답 헤더에 Authorization으로 새 토큰을 보냄
const newToken = res.headers['authorization'];
if (newToken) {
// 새 토큰을 스토어에 저장
store.commit('setAuthorization', newToken);
return newToken;
} else {
console.error('응답 헤더에 토큰이 없습니다:', res.headers);
throw new Error('토큰이 포함되어 있지 않습니다.');
}
} catch (error) {
console.error('토큰 재발급 오류:', error);
// 네트워크나 서버 오류 구분
if (!navigator.onLine) {
alert('네트워크 연결을 확인해주세요.');
} else if (error.response && (error.response.status === 401 || error.response.status === 403)) {
alert('세션이 만료되었습니다. 다시 로그인해 주세요.');
store.commit("setStoreReset");
window.location = '/login.page';
} else {
alert('토큰 재발급에 실패했습니다. 다시 로그인해 주세요.');
store.commit("setStoreReset");
window.location = '/login.page';
}
throw error;
}
}
apiClient.interceptors.response.use(
response => {
return response;
},
async error => {
const originalReq = error.config;
// 응답이 없는 경우(네트워크 오류) 처리
if (!error.response) {
console.error('네트워크 오류:', error);
return Promise.reject(error);
}
// 403 오류 처리: 접근 권한 없음
if (error.response.status === 403 && error.response.data.message === '접근 권한이 없습니다.') {
window.history.back();
return Promise.reject(error);
}
// 401 오류 처리: 토큰 만료
if (error.response.status === 401 && !originalReq._retry) {
originalReq._retry = true;
// 이미 토큰 재발급 중인 경우 대기열에 추가
if (isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
}).then(token => {
originalReq.headers['Authorization'] = token;
return apiClient(originalReq);
}).catch(err => {
return Promise.reject(err);
});
}
isRefreshing = true;
try {
// 액세스 토큰 재발급 요청
const newToken = await refreshAccessToken();
// 성공적으로 토큰을 재발급받은 경우
originalReq.headers['Authorization'] = newToken;
// 대기 중인 요청 처리
processQueue(null, newToken);
// 재요청
return apiClient(originalReq);
} catch (refreshError) {
// 대기 중인 모든 요청에 오류 전파
processQueue(refreshError, null);
return Promise.reject(refreshError);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
export default apiClient;