import { createStore } from "vuex"; import createPersistedState from "vuex-persistedstate"; import { logOutProc } from "../../resources/api/logOut" let globalStore = null; // 전역 스토어 설정 함수 export function setGlobalStore(store) { globalStore = store; } // 전역 스토어 호출 함수 export function getGlobalStore() { return globalStore; } export default function createAppStore(ctx, strgMode, lgnMode) { const store = createStore({ plugins: [createPersistedState({ storage: strgMode === 'S' ? window.sessionStorage : window.localStorage, paths: ['loginMode', 'authorization', 'mbrId', 'mbrNm', 'roles', 'contextPath', 'pageAuth'] })], state: { authorization: null, loginMode: lgnMode || 'J', userType: "portal", menu: null, path: null, roles: [{authority: "ROLE_NONE"}], pageAuth: null, contextPath: ctx || null, }, getters: { getAuthorization: function () {}, getMbrNm: function () {}, getRoles: function () {}, getLoginMode: state => state.loginMode, // 로그인 상태 확인 isLoggedIn: (state) => { if (state.loginMode === 'J') { return !!state.authorization && !!state.mbrNm; } else if (state.loginMode === 'S') { return !!state.mbrNm; } return false; }, }, mutations: { setAuthorization(state, newValue) { state.authorization = newValue; }, setMbrNm(state, newValue) { state.mbrNm = newValue; }, setMbrId(state, newValue) { state.mbrId = newValue; }, setRoles(state, newValue) { state.roles = newValue; }, setUserType(state, newValue) { state.userType = newValue; }, setMenu(state, newValue) { state.menu = newValue; }, setPath(state, newValue) { state.path = newValue; }, setStoreReset(state) { state.authorization = null; state.loginMode = 'J'; state.mbrNm = null; state.mbrId = null; state.roles = [{authority: "ROLE_NONE"}]; state.menu = null; state.pageAuth = null; state.contextPath = null; }, setPageAuth(state, newValue) { state.pageAuth = newValue; }, setMenuList(state, menuList) { state.menuList = menuList; // 메뉴트리 펼치기 const flattenMenus = (menus) => { const result = []; for (const menu of menus) { result.push(menu); if (menu.childList?.length) { result.push(...flattenMenus(menu.childList)); } } return result; } const flattenedMenuList = flattenMenus(menuList); state.flatMenuList = flattenedMenuList; }, setContextPath(state, ctx) { state.contextPath = ctx; }, setLoginMode(state, value) { state.loginMode = value; }, // 로그인 정보 일괄 설정 setLoginInfo(state, { mbrNm, mbrId, roles, authorization, loginMode }) { state.mbrNm = mbrNm; state.mbrId = mbrId; state.roles = roles || [{authority: "ROLE_NONE"}]; if (loginMode) { state.loginMode = loginMode; } // JWT 모드일 때만 authorization 저장 if (state.loginMode === 'J' && authorization) { state.authorization = authorization; } else if (state.loginMode === 'S') { state.authorization = null; // 세션 모드에서는 토큰 불필요 } }, }, actions: { async logout({ commit, state, dispatch }) { const ctx = state.contextPath; const admPath = state.path?.includes("/adm"); const loginMode = state.loginMode || localStorage.getItem('loginMode') || 'J'; try { // 1. 서버 로그아웃 API 호출 (모든 모드에서 호출) const res = await logOutProc(); // 2. 클라이언트 완전 정리 await dispatch('performCompleteCleanup', { loginMode, ctx, admPath }); } catch (error) { // 오류 발생해도 클라이언트 정리는 수행 await dispatch('performCompleteCleanup', { loginMode, ctx, admPath }); } }, // 완전한 클라이언트 정리 수행 async performCompleteCleanup({ commit, dispatch }, { loginMode, ctx, admPath }) { try { // 1. 상태 초기화 commit("setStoreReset"); // 2. 모든 저장소 완전 정리 dispatch('clearAllStorages'); // 3. 모든 쿠키 제거 dispatch('clearAllCookies'); // 4. 세션 무효화 (세션 모드인 경우) if (loginMode === 'S') { dispatch('invalidateSession'); } // 5. 브라우저 캐시 정리 dispatch('clearBrowserCache'); // 6. 페이지 리다이렉트 const loginUrl = admPath ? (ctx || '') + "/cmslogin.page" : (ctx || '') + "/login.page"; // 히스토리 정리 후 이동 window.history.replaceState(null, '', loginUrl); window.location.href = loginUrl; } catch (cleanupError) { // 최종 수단: 강제 새로고침 window.location.reload(); } }, // 모든 저장소 정리 clearAllStorages() { // localStorage 완전 정리 const localStorageKeys = Object.keys(localStorage); localStorageKeys.forEach(key => { localStorage.removeItem(key); }); localStorage.clear(); // sessionStorage 완전 정리 const sessionStorageKeys = Object.keys(sessionStorage); sessionStorageKeys.forEach(key => { sessionStorage.removeItem(key); }); sessionStorage.clear(); }, // 모든 쿠키 제거 clearAllCookies() { const cookiesToDelete = [ // 일반 인증 쿠키 'refresh', 'Authorization', 'access_token', 'JSESSIONID', 'SESSION', // OAuth 관련 쿠키 'oauth_access_token', 'oauth_refresh_token', 'oauth_state', 'OAUTH2_AUTHORIZATION_REQUEST', 'oauth2_auth_request', // 카카오 관련 'kakao_login', '_kadu', '_kadub', '_kalt', // 네이버 관련 'NID_AUT', 'NID_SES', 'NID_JKL', // 구글 관련 'SACSID', 'APISID', 'SSID', '1P_JAR', // 기타 가능한 쿠키 'remember-me', 'user-session', 'auth-token' ]; const domains = [ '', '.' + window.location.hostname, window.location.hostname, '.localhost', 'localhost' ]; const paths = ['/', '/oauth2', '/login', '/auth']; cookiesToDelete.forEach(cookieName => { paths.forEach(path => { domains.forEach(domain => { // 기본 쿠키 삭제 document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path};`; // 도메인별 쿠키 삭제 if (domain) { document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; domain=${domain};`; } }); }); }); }, // 세션 무효화 invalidateSession() { // 세션 무효화를 위한 더미 요청 (필요한 경우) fetch('/mbr/sessionInvalidate', { method: 'POST', credentials: 'include' }).catch(() => {}); // 실패해도 무시 }, // 브라우저 캐시 정리 clearBrowserCache() { // 캐시 API 지원 확인 및 정리 if ('caches' in window) { caches.keys().then(cacheNames => { cacheNames.forEach(cacheName => { caches.delete(cacheName); }); }).catch(() => {}); } // IndexedDB 정리 (가능한 경우) if ('indexedDB' in window) { // 일반적인 DB 이름들 정리 시도 const commonDBNames = ['auth_db', 'user_db', 'session_db']; commonDBNames.forEach(dbName => { try { indexedDB.deleteDatabase(dbName); } catch (e) {} }); } }, setUserType({ commit }, userType) { commit("setUserType", userType); }, setMenu({ commit }, menu) { commit("setMenu", menu); }, setPath({ commit }, path) { commit("setPath", path); }, setPageAuth({ commit }, pageAuth) { commit("setPageAuth", pageAuth); }, setStoreReset({commit}) { commit("setStoreReset"); }, }, }); setGlobalStore(store); return store; }