Files
document-server/frontend/static/js/viewer/utils/module-loader.js
hyungi cfb9485d4f 🚀 배포용: PDF 뷰어 개선 및 서적별 UI 데본씽크 스타일 적용
 주요 개선사항:
- PDF API 500 에러 수정 (한글 파일명 UTF-8 인코딩 처리)
- PDF 뷰어 기능 완전 구현 (PDF.js 통합, 네비게이션, 확대/축소)
- 서적별 문서 그룹화 UI 데본씽크 스타일로 개선
- PDF Manager 페이지 서적별 보기 기능 추가
- Alpine.js 로드 순서 최적화로 JavaScript 에러 해결

🎨 UI/UX 개선:
- 확장/축소 가능한 아코디언 스타일 서적 목록
- 간결하고 직관적인 데본씽크 스타일 인터페이스
- PDF 상태 표시 (HTML 연결, 서적 분류)
- 반응형 디자인 및 부드러운 애니메이션

🔧 기술적 개선:
- PDF.js 워커 설정 및 토큰 인증 처리
- 서적별 PDF 자동 그룹화 로직
- Alpine.js 컴포넌트 초기화 최적화
2025-09-05 07:13:49 +09:00

224 lines
7.6 KiB
JavaScript

/**
* 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;