fix: 로그인 리다이렉트 통합 및 캐시 버스팅 정리

- config.js loginPage를 /index.html에서 /login(SSO)으로 변경
- navigation.js, load-navbar.js에 redirect 파라미터 추가
- 8개 JS 파일의 하드코딩된 '/login' → window.getLoginUrl() 전환
- 로그아웃 시 clearSSOAuth() 호출 추가 (SSO 쿠키 삭제)
- api-base.js v=2→v=3 (SW 캐시 해제 코드 통합)
- TBM 모듈 버전 쿼리스트링 통일 (tbm.html, tbm-mobile.html)
- dashboard.html SW 캐시 해제 인라인 코드 제거 (api-base.js에서 처리)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-05 09:56:08 +09:00
parent 4388628788
commit 6f1efdb03c
52 changed files with 118 additions and 99 deletions

View File

@@ -533,7 +533,8 @@ async function toggleUserStatus(userId) {
function handleLogout() {
if (confirm('로그아웃하시겠습니까?')) {
localStorage.clear();
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
}

View File

@@ -1,9 +1,9 @@
// ✅ /js/admin.js (수정됨 - 중복 로딩 제거)
async function initDashboard() {
// 로그인 토큰 확인
const token = localStorage.getItem('sso_token');
const token = localStorage.getItem('sso_token') || (window.getSSOToken ? window.getSSOToken() : null);
if (!token) {
location.href = '/login';
location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
return;
}

View File

@@ -1,6 +1,18 @@
// /js/api-base.js
// API 기본 설정 및 보안 유틸리티 (비모듈 - 빠른 로딩용)
// ==================== SW 캐시 강제 해제 (PWA 홈화면 추가 대응) ====================
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(regs) {
regs.forEach(function(reg) { reg.unregister(); });
});
}
if ('caches' in window) {
caches.keys().then(function(keys) {
keys.forEach(function(key) { caches.delete(key); });
});
}
(function() {
'use strict';

View File

@@ -184,9 +184,8 @@ form?.addEventListener('submit', async (e) => {
if (countdown < 0) {
clearInterval(countdownInterval);
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
}, 1000);

View File

@@ -15,7 +15,7 @@ export const config = {
// 페이지 경로 설정
paths: {
// 로그인 페이지 경로
loginPage: '/index.html',
loginPage: '/login',
// 메인 대시보드 경로 (모든 사용자 공통)
dashboard: '/pages/dashboard.html',
// 하위 호환성을 위한 별칭들

View File

@@ -76,7 +76,10 @@ async function loadIncompleteTbms() {
// 사용자 권한 확인 및 필터링
const user = getUser();
if (user && user.role !== 'Admin' && user.access_level !== 'system') {
const userRole = (user && user.role || '').toLowerCase();
const userAccess = (user && user.access_level || '').toLowerCase();
const isAdmin = userRole === 'admin' || userAccess === 'admin' || userAccess === 'system';
if (user && !isAdmin) {
// 일반 사용자: 자신이 생성한 세션만 표시
const userId = user.user_id;
data = data.filter(tbm => tbm.created_by === userId);

View File

@@ -148,7 +148,8 @@ function setupNavbarEvents() {
logoutButton.addEventListener('click', () => {
if (confirm('로그아웃 하시겠습니까?')) {
clearAuthData();
window.location.href = config.paths.loginPage;
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = config.paths.loginPage + '?redirect=' + encodeURIComponent('/pages/dashboard.html');
}
});
}

View File

@@ -72,9 +72,8 @@ myPasswordForm?.addEventListener('submit', async e => {
// 3초 후 로그인 페이지로 이동
setTimeout(() => {
alert('비밀번호가 변경되어 다시 로그인해주세요.');
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}, 2000);
} else {
alert('❌ 비밀번호 변경 실패: ' + (result.error || '현재 비밀번호가 올바르지 않습니다.'));

View File

@@ -152,7 +152,8 @@ function setupEventListeners() {
elements.logoutBtn.addEventListener('click', () => {
if (confirm('로그아웃하시겠습니까?')) {
localStorage.clear();
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
});
}

View File

@@ -13,8 +13,9 @@ function redirect(url) {
* 로그인 페이지로 리디렉션합니다.
*/
export function redirectToLogin() {
console.log(`🔄 로그인 페이지로 이동합니다: ${config.paths.loginPage}`);
redirect(config.paths.loginPage);
const loginUrl = config.paths.loginPage + '?redirect=' + encodeURIComponent(window.location.href);
console.log(`🔄 로그인 페이지로 이동합니다: ${loginUrl}`);
redirect(loginUrl);
}
/**

View File

@@ -77,10 +77,8 @@ function setupLogoutButton() {
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
if (confirm('로그아웃 하시겠습니까?')) {
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
localStorage.removeItem('userInfo');
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
});
}

View File

@@ -73,6 +73,7 @@
// ==================== 네비게이션 ====================
window.nextStep = function() {
console.log('[TBM Create] nextStep called, current step:', W.step, 'workTypeId:', W.workTypeId);
if (!validateStep(W.step)) return;
if (W.step < W.totalSteps) {
W.step++;
@@ -84,6 +85,7 @@
};
window.prevStep = function() {
console.log('[TBM Create] prevStep called, current step:', W.step);
if (W.step > 1) {
W.step--;
renderStep(W.step);
@@ -117,30 +119,45 @@
});
}
// 네비게이션 버튼: 단일 핸들러 (DOM 교체 없이 상태 기반 분기)
var _navAction = { prev: null, next: null };
function updateNav() {
var prevBtn = document.getElementById('prevBtn');
var nextBtn = document.getElementById('nextBtn');
if (W.step === 1) {
prevBtn.style.visibility = 'hidden';
prevBtn.onclick = null;
_navAction.prev = null;
} else {
prevBtn.style.visibility = 'visible';
prevBtn.onclick = window.prevStep;
_navAction.prev = window.prevStep;
}
if (W.step === W.totalSteps) {
nextBtn.className = 'nav-btn nav-btn-save';
nextBtn.innerHTML = '저장';
nextBtn.onclick = saveWizard;
nextBtn.textContent = '저장';
_navAction.next = saveWizard;
} else {
nextBtn.className = 'nav-btn nav-btn-next';
nextBtn.innerHTML = '다음 &#8594;';
nextBtn.onclick = window.nextStep;
_navAction.next = window.nextStep;
}
nextBtn.disabled = false;
}
// 한번만 등록하는 이벤트 리스너
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('prevBtn').addEventListener('click', function(e) {
e.preventDefault();
if (_navAction.prev) _navAction.prev();
});
document.getElementById('nextBtn').addEventListener('click', function(e) {
e.preventDefault();
if (_navAction.next) _navAction.next();
});
});
// ==================== 유효성 검사 ====================
function validateStep(step) {
@@ -365,6 +382,7 @@
};
window.selectWorkType = function(id, name) {
console.log('[TBM Create] selectWorkType:', id, name);
W.workTypeId = id;
W.workTypeName = name;
// Update pill buttons

View File

@@ -82,10 +82,8 @@ function setupLogoutButton() {
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
if (confirm('로그아웃 하시겠습니까?')) {
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
localStorage.removeItem('userInfo');
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
});
}

View File

@@ -76,10 +76,8 @@ function setupLogoutButton() {
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
if (confirm('로그아웃 하시겠습니까?')) {
localStorage.removeItem('sso_token');
localStorage.removeItem('sso_user');
localStorage.removeItem('userInfo');
window.location.href = '/login';
if (window.clearSSOAuth) window.clearSSOAuth();
window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login';
}
});
}