/** * ModuleLoader - 지연 로딩 및 모듈 관리 * 필요한 모듈만 동적으로 로드하여 성능을 최적화합니다. */ class ModuleLoader { constructor() { console.log('🔧 ModuleLoader 초기화 시작'); // 로드된 모듈 캐시 this.loadedModules = new Map(); // 로딩 중인 모듈 Promise 캐시 (중복 로딩 방지) this.loadingPromises = new Map(); // 모듈 의존성 정의 this.moduleDependencies = { 'DocumentLoader': [], 'HighlightManager': ['DocumentLoader'], 'BookmarkManager': ['DocumentLoader'], 'LinkManager': ['DocumentLoader'], 'UIManager': [] }; // 모듈 경로 정의 this.modulePaths = { 'DocumentLoader': '/static/js/viewer/core/document-loader.js', 'HighlightManager': '/static/js/viewer/features/highlight-manager.js', 'BookmarkManager': '/static/js/viewer/features/bookmark-manager.js', 'LinkManager': '/static/js/viewer/features/link-manager.js', 'UIManager': '/static/js/viewer/features/ui-manager.js' }; // 캐시 버스팅을 위한 버전 this.version = '2025012607'; console.log('✅ ModuleLoader 초기화 완료'); } /** * 모듈 동적 로드 */ async loadModule(moduleName) { // 이미 로드된 모듈인지 확인 if (this.loadedModules.has(moduleName)) { console.log(`✅ 모듈 캐시에서 반환: ${moduleName}`); return this.loadedModules.get(moduleName); } // 이미 로딩 중인 모듈인지 확인 (중복 로딩 방지) if (this.loadingPromises.has(moduleName)) { console.log(`⏳ 모듈 로딩 대기 중: ${moduleName}`); return await this.loadingPromises.get(moduleName); } console.log(`🔄 모듈 로딩 시작: ${moduleName}`); // 로딩 Promise 생성 및 캐시 const loadingPromise = this._loadModuleScript(moduleName); this.loadingPromises.set(moduleName, loadingPromise); try { const moduleClass = await loadingPromise; // 로딩 완료 후 캐시에 저장 this.loadedModules.set(moduleName, moduleClass); this.loadingPromises.delete(moduleName); console.log(`✅ 모듈 로딩 완료: ${moduleName}`); return moduleClass; } catch (error) { console.error(`❌ 모듈 로딩 실패: ${moduleName}`, error); this.loadingPromises.delete(moduleName); throw error; } } /** * 의존성을 포함한 모듈 로드 */ async loadModuleWithDependencies(moduleName) { console.log(`🔗 의존성 포함 모듈 로딩: ${moduleName}`); // 의존성 먼저 로드 const dependencies = this.moduleDependencies[moduleName] || []; if (dependencies.length > 0) { console.log(`📦 의존성 로딩: ${dependencies.join(', ')}`); await Promise.all(dependencies.map(dep => this.loadModule(dep))); } // 메인 모듈 로드 return await this.loadModule(moduleName); } /** * 여러 모듈 병렬 로드 */ async loadModules(moduleNames) { console.log(`🚀 병렬 모듈 로딩: ${moduleNames.join(', ')}`); const loadPromises = moduleNames.map(name => this.loadModuleWithDependencies(name)); const results = await Promise.all(loadPromises); console.log(`✅ 병렬 모듈 로딩 완료: ${moduleNames.join(', ')}`); return results; } /** * 스크립트 동적 로딩 */ async _loadModuleScript(moduleName) { return new Promise((resolve, reject) => { // 이미 전역에 클래스가 있는지 확인 if (window[moduleName]) { console.log(`✅ 모듈 이미 로드됨: ${moduleName}`); resolve(window[moduleName]); return; } console.log(`📥 스크립트 로딩 시작: ${moduleName}`); const script = document.createElement('script'); script.src = `${this.modulePaths[moduleName]}?v=${this.version}`; script.async = true; script.onload = () => { console.log(`📥 스크립트 로드 완료: ${moduleName}`); // 스크립트 로드 후 잠시 대기 (클래스 등록 시간) setTimeout(() => { if (window[moduleName]) { console.log(`✅ 모듈 클래스 확인: ${moduleName}`); resolve(window[moduleName]); } else { console.error(`❌ 모듈 클래스 없음: ${moduleName}`, Object.keys(window).filter(k => k.includes('Manager') || k.includes('Loader'))); reject(new Error(`모듈 클래스를 찾을 수 없음: ${moduleName}`)); } }, 10); // 10ms 대기 }; script.onerror = (error) => { console.error(`❌ 스크립트 로딩 실패: ${moduleName}`, error); reject(new Error(`스크립트 로딩 실패: ${moduleName}`)); }; document.head.appendChild(script); }); } /** * 모듈 인스턴스 생성 (팩토리 패턴) */ async createModuleInstance(moduleName, ...args) { const ModuleClass = await this.loadModuleWithDependencies(moduleName); return new ModuleClass(...args); } /** * 모듈 프리로딩 (백그라운드에서 미리 로드) */ async preloadModules(moduleNames) { console.log(`🔮 모듈 프리로딩: ${moduleNames.join(', ')}`); // 백그라운드에서 로드 (에러 무시) const preloadPromises = moduleNames.map(async (name) => { try { await this.loadModuleWithDependencies(name); console.log(`✅ 프리로딩 완료: ${name}`); } catch (error) { console.warn(`⚠️ 프리로딩 실패: ${name}`, error); } }); // 모든 프리로딩이 완료될 때까지 기다리지 않음 Promise.all(preloadPromises); } /** * 사용하지 않는 모듈 언로드 (메모리 최적화) */ unloadModule(moduleName) { if (this.loadedModules.has(moduleName)) { this.loadedModules.delete(moduleName); console.log(`🗑️ 모듈 언로드: ${moduleName}`); } } /** * 모든 모듈 언로드 */ unloadAllModules() { this.loadedModules.clear(); this.loadingPromises.clear(); console.log('🗑️ 모든 모듈 언로드 완료'); } /** * 로드된 모듈 상태 확인 */ getLoadedModules() { return Array.from(this.loadedModules.keys()); } /** * 메모리 사용량 추정 */ getMemoryUsage() { const loadedCount = this.loadedModules.size; const loadingCount = this.loadingPromises.size; return { loadedModules: loadedCount, loadingModules: loadingCount, totalModules: Object.keys(this.modulePaths).length, memoryEstimate: `${loadedCount * 50}KB` // 대략적인 추정 }; } } // 전역 모듈 로더 인스턴스 window.moduleLoader = new ModuleLoader(); // 전역으로 내보내기 window.ModuleLoader = ModuleLoader;