Files
TK-FB-Project/web-ui/js/work-analysis/module-loader.js
Hyungi Ahn ed40eec261 fix: 그룹 리더 대시보드 작업 저장/삭제 오류 해결 및 작업 분석 시스템 성능 최적화
🔧 그룹 리더 대시보드 수정사항:
- API 호출 방식 수정 (modern-dashboard.js)
- 서버 API 요구사항에 맞는 데이터 구조 변경
- work_entries 배열 구조로 변경
- work_type_id → task_id 필드명 매핑
- 400 Bad Request 오류 해결

 작업 분석 시스템 성능 최적화:
- 중복 함수 제거 (isWeekend, isVacationProject 통합)
- WorkAnalysisAPI 캐싱 시스템 구현 (5분 만료)
- 네임스페이스 조직화 (utils, ui, analysis, render)
- ErrorHandler 통합 에러 처리 시스템
- 성능 모니터링 및 메모리 누수 방지
- GPU 가속 CSS 애니메이션 추가
- 디바운스/스로틀 함수 적용
- 의미 없는 통계 카드 제거

📊 작업 분석 페이지 개선:
- 프로그레스 바 애니메이션
- 토스트 알림 시스템
- 부드러운 전환 효과
- 반응형 최적화
- 메모리 사용량 모니터링
2025-11-05 10:12:52 +09:00

268 lines
8.9 KiB
JavaScript

/**
* Work Analysis Module Loader
* 작업 분석 모듈들을 순서대로 로드하고 초기화하는 로더
*/
class WorkAnalysisModuleLoader {
constructor() {
this.modules = [
{ name: 'API Client', path: '/js/work-analysis/api-client.js', loaded: false },
{ name: 'Data Processor', path: '/js/work-analysis/data-processor.js', loaded: false },
{ name: 'State Manager', path: '/js/work-analysis/state-manager.js', loaded: false },
{ name: 'Table Renderer', path: '/js/work-analysis/table-renderer.js', loaded: false },
{ name: 'Chart Renderer', path: '/js/work-analysis/chart-renderer.js', loaded: false },
{ name: 'Main Controller', path: '/js/work-analysis/main-controller.js', loaded: false }
];
this.loadingPromise = null;
}
/**
* 모든 모듈 로드
* @returns {Promise} 로딩 완료 Promise
*/
async loadAll() {
if (this.loadingPromise) {
return this.loadingPromise;
}
this.loadingPromise = this._loadModules();
return this.loadingPromise;
}
/**
* 모듈들을 순차적으로 로드
*/
async _loadModules() {
console.log('🚀 작업 분석 모듈 로딩 시작');
try {
// 의존성 순서대로 로드
for (const module of this.modules) {
await this._loadModule(module);
}
console.log('✅ 모든 작업 분석 모듈 로딩 완료');
this._onAllModulesLoaded();
} catch (error) {
console.error('❌ 모듈 로딩 실패:', error);
this._onLoadingError(error);
throw error;
}
}
/**
* 개별 모듈 로드
* @param {Object} module - 모듈 정보
*/
async _loadModule(module) {
return new Promise((resolve, reject) => {
console.log(`📦 로딩 중: ${module.name}`);
const script = document.createElement('script');
script.src = module.path;
script.type = 'text/javascript';
script.onload = () => {
module.loaded = true;
console.log(`✅ 로딩 완료: ${module.name}`);
resolve();
};
script.onerror = (error) => {
console.error(`❌ 로딩 실패: ${module.name}`, error);
reject(new Error(`Failed to load ${module.name}: ${module.path}`));
};
document.head.appendChild(script);
});
}
/**
* 모든 모듈 로딩 완료 시 호출
*/
_onAllModulesLoaded() {
// 전역 변수 확인
const requiredGlobals = [
'WorkAnalysisAPI',
'WorkAnalysisDataProcessor',
'WorkAnalysisState',
'WorkAnalysisTableRenderer',
'WorkAnalysisChartRenderer'
];
const missingGlobals = requiredGlobals.filter(name => !window[name]);
if (missingGlobals.length > 0) {
console.warn('⚠️ 일부 전역 객체가 누락됨:', missingGlobals);
}
// 하위 호환성을 위한 전역 함수들 설정
this._setupLegacyFunctions();
// 모듈 로딩 완료 이벤트 발생
window.dispatchEvent(new CustomEvent('workAnalysisModulesLoaded', {
detail: { modules: this.modules }
}));
console.log('🎉 작업 분석 시스템 준비 완료');
}
/**
* 하위 호환성을 위한 전역 함수 설정
*/
_setupLegacyFunctions() {
// 기존 HTML에서 사용하던 함수들을 새 모듈 시스템으로 연결
const legacyFunctions = {
// 기간 확정
confirmPeriod: () => {
if (window.WorkAnalysisMainController) {
window.WorkAnalysisMainController.handlePeriodConfirm();
}
},
// 분석 모드 변경
switchAnalysisMode: (mode) => {
if (window.WorkAnalysisState) {
window.WorkAnalysisState.setAnalysisMode(mode);
}
},
// 탭 변경
switchTab: (tabId) => {
if (window.WorkAnalysisState) {
window.WorkAnalysisState.setCurrentTab(tabId);
}
},
// 개별 분석 함수들
analyzeWorkStatus: () => {
if (window.WorkAnalysisMainController) {
window.WorkAnalysisMainController.analyzeWorkStatus();
}
},
analyzeProjectDistribution: () => {
if (window.WorkAnalysisMainController) {
window.WorkAnalysisMainController.analyzeProjectDistribution();
}
},
analyzeWorkerPerformance: () => {
if (window.WorkAnalysisMainController) {
window.WorkAnalysisMainController.analyzeWorkerPerformance();
}
},
analyzeErrorAnalysis: () => {
if (window.WorkAnalysisMainController) {
window.WorkAnalysisMainController.analyzeErrorAnalysis();
}
}
};
// 전역 함수로 등록
Object.assign(window, legacyFunctions);
console.log('🔗 하위 호환성 함수 설정 완료');
}
/**
* 로딩 에러 처리
*/
_onLoadingError(error) {
// 에러 UI 표시
const container = document.querySelector('.analysis-container');
if (container) {
const errorHTML = `
<div style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
text-align: center;
color: #ef4444;
">
<div style="font-size: 4rem; margin-bottom: 1rem;">⚠️</div>
<h2 style="margin-bottom: 1rem;">모듈 로딩 실패</h2>
<p style="margin-bottom: 2rem; color: #666;">
작업 분석 시스템을 로드하는 중 오류가 발생했습니다.<br>
페이지를 새로고침하거나 관리자에게 문의하세요.
</p>
<button onclick="location.reload()" style="
padding: 0.75rem 1.5rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 0.5rem;
cursor: pointer;
font-size: 1rem;
">
페이지 새로고침
</button>
<details style="margin-top: 2rem; text-align: left; max-width: 600px;">
<summary style="cursor: pointer; color: #666;">기술적 세부사항</summary>
<pre style="
background: #f8f9fa;
padding: 1rem;
border-radius: 0.5rem;
margin-top: 1rem;
overflow-x: auto;
font-size: 0.875rem;
">${error.message}</pre>
</details>
</div>
`;
container.innerHTML = errorHTML;
}
}
/**
* 로딩 상태 확인
* @returns {Object} 로딩 상태 정보
*/
getLoadingStatus() {
const total = this.modules.length;
const loaded = this.modules.filter(m => m.loaded).length;
return {
total,
loaded,
percentage: Math.round((loaded / total) * 100),
isComplete: loaded === total,
modules: this.modules.map(m => ({
name: m.name,
loaded: m.loaded
}))
};
}
/**
* 특정 모듈 로딩 상태 확인
* @param {string} moduleName - 모듈명
* @returns {boolean} 로딩 완료 여부
*/
isModuleLoaded(moduleName) {
const module = this.modules.find(m => m.name === moduleName);
return module ? module.loaded : false;
}
}
// 전역 인스턴스 생성
window.WorkAnalysisModuleLoader = new WorkAnalysisModuleLoader();
// 자동 로딩 시작 (DOM이 준비되면)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
window.WorkAnalysisModuleLoader.loadAll();
});
} else {
// DOM이 이미 준비된 경우 즉시 로딩
window.WorkAnalysisModuleLoader.loadAll();
}
// Export는 브라우저 환경에서 제거됨