🔍 Debug Logs Added: - AuthManager.checkAuth() 상세 상태 로그 - localStorage 토큰/사용자 정보 존재 여부 확인 - currentUser 전역 변수 업데이트 로그 - DOM 요소 존재 여부 및 화면 전환 상태 확인 🎯 Issue Analysis: - AuthManager 정상 작동 확인됨 (✅ 캐시된 인증 정보 사용) - 공통 헤더 초기화 성공 - currentUser undefined 문제 → 전역 변수 동기화 이슈 - 로그인 창 보임 → DOM 조작 실패 가능성 🔧 Enhanced Debugging: - 🖥️ 화면 전환 시작/완료 로그 - loginScreen/mainScreen 요소 존재 확인 - CSS 클래스 적용 상태 확인 - 화면 전환 실패 시 에러 로그 Next Step: 콘솔 로그 확인하여 정확한 실패 지점 파악
273 lines
7.8 KiB
JavaScript
273 lines
7.8 KiB
JavaScript
/**
|
|
* 중앙화된 인증 관리자
|
|
* 페이지 간 이동 시 불필요한 API 호출을 방지하고 인증 상태를 효율적으로 관리
|
|
*/
|
|
class AuthManager {
|
|
constructor() {
|
|
this.currentUser = null;
|
|
this.isAuthenticated = false;
|
|
this.lastAuthCheck = null;
|
|
this.authCheckInterval = 5 * 60 * 1000; // 5분마다 토큰 유효성 체크
|
|
this.listeners = new Set();
|
|
|
|
// 초기화
|
|
this.init();
|
|
}
|
|
|
|
/**
|
|
* 초기화
|
|
*/
|
|
init() {
|
|
console.log('🔐 AuthManager 초기화');
|
|
|
|
// localStorage에서 사용자 정보 복원
|
|
this.restoreUserFromStorage();
|
|
|
|
// 토큰 만료 체크 타이머 설정
|
|
this.setupTokenExpiryCheck();
|
|
|
|
// 페이지 가시성 변경 시 토큰 체크
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (!document.hidden && this.shouldCheckAuth()) {
|
|
this.refreshAuth();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* localStorage에서 사용자 정보 복원
|
|
*/
|
|
restoreUserFromStorage() {
|
|
const token = localStorage.getItem('access_token');
|
|
const userStr = localStorage.getItem('currentUser');
|
|
|
|
console.log('🔍 localStorage 확인:');
|
|
console.log('- 토큰 존재:', !!token);
|
|
console.log('- 사용자 정보 존재:', !!userStr);
|
|
|
|
if (token && userStr) {
|
|
try {
|
|
this.currentUser = JSON.parse(userStr);
|
|
this.isAuthenticated = true;
|
|
this.lastAuthCheck = Date.now();
|
|
console.log('✅ 저장된 사용자 정보 복원:', this.currentUser.username);
|
|
} catch (error) {
|
|
console.error('❌ 사용자 정보 복원 실패:', error);
|
|
this.clearAuth();
|
|
}
|
|
} else {
|
|
console.log('❌ 토큰 또는 사용자 정보 없음 - 로그인 필요');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 인증이 필요한지 확인
|
|
*/
|
|
shouldCheckAuth() {
|
|
if (!this.isAuthenticated) return true;
|
|
if (!this.lastAuthCheck) return true;
|
|
|
|
const timeSinceLastCheck = Date.now() - this.lastAuthCheck;
|
|
return timeSinceLastCheck > this.authCheckInterval;
|
|
}
|
|
|
|
/**
|
|
* 인증 상태 확인 (필요시에만 API 호출)
|
|
*/
|
|
async checkAuth() {
|
|
console.log('🔍 AuthManager.checkAuth() 호출됨');
|
|
console.log('- 현재 인증 상태:', this.isAuthenticated);
|
|
console.log('- 현재 사용자:', this.currentUser?.username || 'null');
|
|
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
console.log('❌ 토큰 없음 - 인증 실패');
|
|
this.clearAuth();
|
|
return null;
|
|
}
|
|
|
|
// 최근에 체크했으면 캐시된 정보 사용
|
|
if (this.isAuthenticated && !this.shouldCheckAuth()) {
|
|
console.log('✅ 캐시된 인증 정보 사용:', this.currentUser.username);
|
|
return this.currentUser;
|
|
}
|
|
|
|
// API 호출이 필요한 경우
|
|
console.log('🔄 API 호출 필요 - refreshAuth 실행');
|
|
return await this.refreshAuth();
|
|
}
|
|
|
|
/**
|
|
* 강제로 인증 정보 새로고침 (API 호출)
|
|
*/
|
|
async refreshAuth() {
|
|
console.log('🔄 인증 정보 새로고침 (API 호출)');
|
|
|
|
try {
|
|
// API가 로드될 때까지 대기
|
|
await this.waitForAPI();
|
|
|
|
const user = await AuthAPI.getCurrentUser();
|
|
|
|
this.currentUser = user;
|
|
this.isAuthenticated = true;
|
|
this.lastAuthCheck = Date.now();
|
|
|
|
// localStorage 업데이트
|
|
localStorage.setItem('currentUser', JSON.stringify(user));
|
|
|
|
console.log('✅ 인증 정보 새로고침 완료:', user.username);
|
|
|
|
// 리스너들에게 알림
|
|
this.notifyListeners('auth-success', user);
|
|
|
|
return user;
|
|
|
|
} catch (error) {
|
|
console.error('❌ 인증 실패:', error);
|
|
this.clearAuth();
|
|
this.notifyListeners('auth-failed', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* API 로드 대기
|
|
*/
|
|
async waitForAPI() {
|
|
let attempts = 0;
|
|
const maxAttempts = 50;
|
|
|
|
while (typeof AuthAPI === 'undefined' && attempts < maxAttempts) {
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
attempts++;
|
|
}
|
|
|
|
if (typeof AuthAPI === 'undefined') {
|
|
throw new Error('AuthAPI를 로드할 수 없습니다');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 인증 정보 클리어
|
|
*/
|
|
clearAuth() {
|
|
console.log('🧹 인증 정보 클리어');
|
|
|
|
this.currentUser = null;
|
|
this.isAuthenticated = false;
|
|
this.lastAuthCheck = null;
|
|
|
|
localStorage.removeItem('access_token');
|
|
localStorage.removeItem('currentUser');
|
|
|
|
this.notifyListeners('auth-cleared');
|
|
}
|
|
|
|
/**
|
|
* 로그인 처리
|
|
*/
|
|
async login(username, password) {
|
|
console.log('🔑 로그인 시도:', username);
|
|
|
|
try {
|
|
await this.waitForAPI();
|
|
const data = await AuthAPI.login(username, password);
|
|
|
|
this.currentUser = data.user;
|
|
this.isAuthenticated = true;
|
|
this.lastAuthCheck = Date.now();
|
|
|
|
// localStorage 저장
|
|
localStorage.setItem('access_token', data.access_token);
|
|
localStorage.setItem('currentUser', JSON.stringify(data.user));
|
|
|
|
console.log('✅ 로그인 성공:', data.user.username);
|
|
|
|
this.notifyListeners('login-success', data.user);
|
|
|
|
return data;
|
|
|
|
} catch (error) {
|
|
console.error('❌ 로그인 실패:', error);
|
|
this.clearAuth();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 로그아웃 처리
|
|
*/
|
|
logout() {
|
|
console.log('🚪 로그아웃');
|
|
|
|
this.clearAuth();
|
|
this.notifyListeners('logout');
|
|
|
|
// 로그인 페이지로 이동
|
|
window.location.href = '/index.html';
|
|
}
|
|
|
|
/**
|
|
* 토큰 만료 체크 타이머 설정
|
|
*/
|
|
setupTokenExpiryCheck() {
|
|
// 30분마다 토큰 유효성 체크
|
|
setInterval(() => {
|
|
if (this.isAuthenticated) {
|
|
console.log('⏰ 정기 토큰 유효성 체크');
|
|
this.refreshAuth().catch(() => {
|
|
console.log('🔄 토큰 만료 - 로그아웃 처리');
|
|
this.logout();
|
|
});
|
|
}
|
|
}, 30 * 60 * 1000);
|
|
}
|
|
|
|
/**
|
|
* 이벤트 리스너 등록
|
|
*/
|
|
addEventListener(callback) {
|
|
this.listeners.add(callback);
|
|
}
|
|
|
|
/**
|
|
* 이벤트 리스너 제거
|
|
*/
|
|
removeEventListener(callback) {
|
|
this.listeners.delete(callback);
|
|
}
|
|
|
|
/**
|
|
* 리스너들에게 알림
|
|
*/
|
|
notifyListeners(event, data = null) {
|
|
this.listeners.forEach(callback => {
|
|
try {
|
|
callback(event, data);
|
|
} catch (error) {
|
|
console.error('리스너 콜백 오류:', error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 현재 사용자 정보 반환
|
|
*/
|
|
getCurrentUser() {
|
|
return this.currentUser;
|
|
}
|
|
|
|
/**
|
|
* 인증 상태 반환
|
|
*/
|
|
isLoggedIn() {
|
|
return this.isAuthenticated && !!this.currentUser;
|
|
}
|
|
}
|
|
|
|
// 전역 인스턴스 생성
|
|
window.authManager = new AuthManager();
|
|
|
|
console.log('🎯 AuthManager 로드 완료');
|