| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import { useLoadingStore } from '@/stores/loading'
- import { useAuthStore } from '@/stores/auth'
- import axios from 'axios'
- let instance = null
- let isRefreshing = false;
- let failedQueue = [];
- function processQueue(error, token = null) {
- failedQueue.forEach(prom => {
- if (error) {
- prom.reject(error);
- } else {
- prom.resolve(token);
- }
- });
- failedQueue = [];
- }
- // interceptor가 없는 별도의 axios 인스턴스 생성
- const refreshAxios = axios.create({
- baseURL: import.meta.env.VITE_APP_API_URL, // 최종 API URL
- withCredentials: false,
- timeout: 60 * 1000,
- responseType: 'json',
- responseEncoding: 'utf8',
- xsrfHeaderName: 'X-XSRF-TOKEN',
- progress: false,
- });
- const useAxios = () => {
- /************************************************************************
- | Axios
- ************************************************************************/
- const { $log } = useNuxtApp()
- const store = useLoadingStore()
- if (!instance) {
- // 환경 변수에서 API URL과 포트를 가져옵니다
- const apiBaseUrl = import.meta.env.VITE_APP_API_URL
- const apiPort = import.meta.env.VITE_APP_API_PORT
- const fullApiUrl = `${apiBaseUrl}:${apiPort}`
- let loadingPassUrl = [
- '/p5g/fm/eventViewer'
- ]
- instance = axios.create({
- baseURL: apiBaseUrl, // 최종 API URL
- withCredentials: false,
- timeout: 60 * 1000,
- responseType: 'json',
- responseEncoding: 'utf8',
- xsrfHeaderName: 'X-XSRF-TOKEN',
- progress: false,
- });
- /**
- * 요청 인터셉터
- */
- instance.interceptors.request.use(function (config) {
- $log.debug("[REQ]" + config.url)
- let accessToken = useAuthStore().getAccessToken;
- // 개발 모드일 때는 env에서 VITE_APP_DEV_TOKEN 사용
- if (import.meta.env.MODE === 'development' && import.meta.env.VITE_APP_DEV_TOKEN) {
- accessToken = import.meta.env.VITE_APP_DEV_TOKEN;
- }
- config.headers = {
- ...config.headers, // 기존 헤더 유지
- 'Accept': 'application/json',
- 'Access-Token': accessToken ? accessToken : '', // 동적으로 토큰 세팅
- };
- // 멀티파트 요청이면 Content-Type을 자동으로 설정하지 않음
- if (config.headers['Content-Type'] !== 'multipart/form-data') {
- if (!config.headers['Content-Type']) {
- config.headers['Content-Type'] = 'application/json;charset=UTF-8';
- }
- }
- if(!loadingPassUrl.includes(config.url)) {
- store.plusCount()
- }
- return config
- },
- function (error) {
- $log.error("[REQ][ERR]" + error)
- if (!loadingPassUrl.includes(config.url)) {
- store.minusCount()
- }
- // 요청 에러에도 로딩카운트 감소
- if (error.config && !loadingPassUrl.includes(error.config.url)) {
- store.minusCount()
- }
- return Promise.reject(error)
- }
- )
- /**
- * 응답 인터셉터
- */
- instance.interceptors.response.use(
- response => {
- if(!loadingPassUrl.includes(response.config.url)) {
- store.minusCount()
- }
- return response;
- },
- async error => {
- // 응답 에러에도 로딩카운트 감소(최대한 항상 호출)
- if (error.config && !loadingPassUrl.includes(error.config.url)) {
- store.minusCount()
- }
- if (error.response && error.response.status === 401) {
- const authStore = useAuthStore();
- const originalRequest = error.config;
- // refreshToken이 있고, 재발급 시도가 아닌 경우
- if (authStore.getRefreshToken && !originalRequest._retry) {
- if (isRefreshing) {
- // 이미 재발급 중이면 큐에 쌓았다가 처리
- return new Promise(function(resolve, reject) {
- failedQueue.push({resolve, reject});
- }).then(token => {
- originalRequest.headers['Access-Token'] = `${token}`;
- return instance(originalRequest);
- }).catch(err => {
- return Promise.reject(err);
- });
- }
- originalRequest._retry = true;
- isRefreshing = true;
- store.plusCount(); // refreshToken 요청 로딩 시작
- try {
- let __REQ = {
- refreshToken: authStore.getRefreshToken
- }
- // refreshAxios로 refreshToken 요청
- const res = await refreshAxios.post('/roulette/refreshToken', __REQ);
- // 다양한 응답 구조에서 accessToken 추출
- let newAccessToken = res.data.accessToken;
- if (!newAccessToken && res.data.data && res.data.data.accessToken) {
- newAccessToken = res.data.data.accessToken;
- }
- if (!newAccessToken && res.data.token) {
- newAccessToken = res.data.token;
- }
- if (!newAccessToken) {
- if (typeof window !== 'undefined') {
- alert('세션이 만료되었습니다. 다시 로그인 해주세요.');
- window.location.href = '/';
- }
- authStore.setLogout();
- throw new Error('No accessToken in refreshToken response');
- }
- authStore.setAccessToken(newAccessToken);
- processQueue(null, newAccessToken);
- isRefreshing = false;
- originalRequest.headers['Access-Token'] = `${newAccessToken}`;
- store.minusCount();
- // instance로 원래 요청 재시도
- return instance(originalRequest);
- } catch (refreshError) {
- processQueue(refreshError, null);
- isRefreshing = false;
- store.minusCount(); // refreshToken 요청 로딩 끝
- // refreshToken 만료(401, 403)만 로그아웃, 그 외는 안내
- if (refreshError.response && (refreshError.response.status === 401 || refreshError.response.status === 403)) {
- authStore.setLogout();
- if (typeof window !== 'undefined') {
- alert('로그인 세션이 만료되었습니다. 다시 로그인 해주세요.');
- window.location.href = '/';
- }
- } else {
- if (typeof window !== 'undefined') {
- alert('일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.');
- //window.location.href = '/';
- }
- }
- return Promise.reject(refreshError);
- }
- } else {
- if(!error.response.data.messages.errorCode){
- authStore.setLogout();
- if (typeof window !== 'undefined') {
- window.location.href = '/';
- }
- }
- }
- }
- return Promise.reject(error);
- }
- );
- }
- return instance
- }
- export default useAxios
|