/** * 페이지 관리자 * 모듈화된 페이지들의 생명주기를 관리하고 부드러운 전환을 제공 */ class PageManager { constructor() { this.currentPage = null; this.loadedModules = new Map(); this.pageHistory = []; } /** * 페이지 초기화 * @param {string} pageId - 페이지 식별자 * @param {Object} options - 초기화 옵션 */ async initializePage(pageId, options = {}) { try { // 로딩 표시 this.showPageLoader(); // 사용자 인증 확인 const user = await this.checkAuthentication(); if (!user) return; // 공통 헤더 초기화 await this.initializeCommonHeader(user, pageId); // 페이지별 권한 체크 if (!this.checkPagePermission(pageId, user)) { this.redirectToAccessiblePage(); return; } // 페이지 모듈 로드 및 초기화 await this.loadPageModule(pageId, options); // 페이지 히스토리 업데이트 this.updatePageHistory(pageId); // 로딩 숨기기 this.hidePageLoader(); } catch (error) { console.error('페이지 초기화 실패:', error); this.showErrorPage(error); } } /** * 사용자 인증 확인 */ async checkAuthentication() { const token = localStorage.getItem('access_token'); if (!token) { window.location.href = '/index.html'; return null; } try { // API가 로드될 때까지 대기 await this.waitForAPI(); const user = await AuthAPI.getCurrentUser(); localStorage.setItem('currentUser', JSON.stringify(user)); return user; } catch (error) { console.error('인증 실패:', error); localStorage.removeItem('access_token'); localStorage.removeItem('currentUser'); window.location.href = '/index.html'; return null; } } /** * API 로드 대기 */ async waitForAPI() { let attempts = 0; const maxAttempts = 50; while (!window.AuthAPI && attempts < maxAttempts) { await new Promise(resolve => setTimeout(resolve, 100)); attempts++; } if (!window.AuthAPI) { throw new Error('API를 로드할 수 없습니다.'); } } /** * 공통 헤더 초기화 */ async initializeCommonHeader(user, pageId) { // 권한 시스템 초기화 if (window.pagePermissionManager) { window.pagePermissionManager.setUser(user); } // 공통 헤더 초기화 if (window.commonHeader) { await window.commonHeader.init(user, pageId); } } /** * 페이지 권한 체크 */ checkPagePermission(pageId, user) { // admin은 모든 페이지 접근 가능 if (user.role === 'admin') { return true; } // 권한 시스템이 로드되지 않았으면 기본 페이지만 허용 if (!window.canAccessPage) { return ['issues_create', 'issues_view'].includes(pageId); } return window.canAccessPage(pageId); } /** * 접근 가능한 페이지로 리다이렉트 */ redirectToAccessiblePage() { alert('이 페이지에 접근할 권한이 없습니다.'); // 기본적으로 접근 가능한 페이지로 이동 if (window.canAccessPage && window.canAccessPage('issues_view')) { window.location.href = '/issue-view.html'; } else { window.location.href = '/index.html'; } } /** * 페이지 모듈 로드 */ async loadPageModule(pageId, options) { // 이미 로드된 모듈이 있으면 재사용 if (this.loadedModules.has(pageId)) { const module = this.loadedModules.get(pageId); if (module.reinitialize) { await module.reinitialize(options); } return; } // 페이지별 모듈 로드 const module = await this.createPageModule(pageId, options); if (module) { this.loadedModules.set(pageId, module); this.currentPage = pageId; } } /** * 페이지 모듈 생성 */ async createPageModule(pageId, options) { switch (pageId) { case 'issues_create': return new IssuesCreateModule(options); case 'issues_view': return new IssuesViewModule(options); case 'issues_manage': return new IssuesManageModule(options); case 'projects_manage': return new ProjectsManageModule(options); case 'daily_work': return new DailyWorkModule(options); case 'reports': return new ReportsModule(options); case 'users_manage': return new UsersManageModule(options); default: console.warn(`알 수 없는 페이지 ID: ${pageId}`); return null; } } /** * 페이지 히스토리 업데이트 */ updatePageHistory(pageId) { this.pageHistory.push({ pageId, timestamp: new Date(), url: window.location.href }); // 히스토리 크기 제한 (최대 10개) if (this.pageHistory.length > 10) { this.pageHistory.shift(); } } /** * 페이지 로더 표시 */ showPageLoader() { const existingLoader = document.getElementById('page-loader'); if (existingLoader) return; const loader = document.createElement('div'); loader.id = 'page-loader'; loader.className = 'fixed inset-0 bg-white bg-opacity-90 flex items-center justify-center z-50'; loader.innerHTML = `

페이지를 로드하는 중...

잠시만 기다려주세요

`; document.body.appendChild(loader); } /** * 페이지 로더 숨기기 */ hidePageLoader() { const loader = document.getElementById('page-loader'); if (loader) { loader.remove(); } } /** * 에러 페이지 표시 */ showErrorPage(error) { this.hidePageLoader(); const errorContainer = document.createElement('div'); errorContainer.className = 'fixed inset-0 bg-gray-50 flex items-center justify-center z-50'; errorContainer.innerHTML = `

페이지 로드 실패

${error.message || '알 수 없는 오류가 발생했습니다.'}

`; document.body.appendChild(errorContainer); } /** * 페이지 정리 */ cleanup() { if (this.currentPage && this.loadedModules.has(this.currentPage)) { const module = this.loadedModules.get(this.currentPage); if (module.cleanup) { module.cleanup(); } } } } /** * 기본 페이지 모듈 클래스 * 모든 페이지 모듈이 상속받아야 하는 기본 클래스 */ class BasePageModule { constructor(options = {}) { this.options = options; this.initialized = false; this.eventListeners = []; } /** * 모듈 초기화 (하위 클래스에서 구현) */ async initialize() { throw new Error('initialize 메서드를 구현해야 합니다.'); } /** * 모듈 재초기화 */ async reinitialize(options = {}) { this.cleanup(); this.options = { ...this.options, ...options }; await this.initialize(); } /** * 이벤트 리스너 등록 (자동 정리를 위해) */ addEventListener(element, event, handler) { element.addEventListener(event, handler); this.eventListeners.push({ element, event, handler }); } /** * 모듈 정리 */ cleanup() { // 등록된 이벤트 리스너 제거 this.eventListeners.forEach(({ element, event, handler }) => { element.removeEventListener(event, handler); }); this.eventListeners = []; this.initialized = false; } /** * 로딩 표시 */ showLoading(container, message = '로딩 중...') { if (typeof container === 'string') { container = document.getElementById(container); } if (container) { container.innerHTML = `

${message}

`; } } /** * 에러 표시 */ showError(container, message = '오류가 발생했습니다.') { if (typeof container === 'string') { container = document.getElementById(container); } if (container) { container.innerHTML = `

${message}

`; } } } // 전역 인스턴스 window.pageManager = new PageManager(); window.BasePageModule = BasePageModule;