"use client"; import { ComponentDefinition } from "@/types/component"; import { ComponentStandard } from "@/hooks/admin/useComponents"; /** * 컴포넌트 시스템 성능 최적화 도구 */ export interface PerformanceMetrics { totalComponents: number; registrationTime: number; searchTime: number; renderTime: number; memoryUsage: number; cacheHitRate: number; } export interface OptimizationOptions { enableBatchRegistration: boolean; enableLazyLoading: boolean; enableMemoryOptimization: boolean; enableIndexing: boolean; batchSize: number; cacheSize: number; debounceTime: number; } export const DEFAULT_OPTIMIZATION_OPTIONS: OptimizationOptions = { enableBatchRegistration: true, enableLazyLoading: true, enableMemoryOptimization: true, enableIndexing: true, batchSize: 50, cacheSize: 1000, debounceTime: 300, }; /** * 성능 최적화 클래스 */ export class PerformanceOptimizer { private static options: OptimizationOptions = DEFAULT_OPTIMIZATION_OPTIONS; private static metrics: PerformanceMetrics = { totalComponents: 0, registrationTime: 0, searchTime: 0, renderTime: 0, memoryUsage: 0, cacheHitRate: 0, }; // 캐시와 인덱스 private static searchCache = new Map(); private static categoryIndex = new Map>(); private static webTypeIndex = new Map>(); private static nameIndex = new Map>(); // 성능 측정 private static performanceMarks = new Map(); private static cacheHits = 0; private static cacheRequests = 0; /** * 성능 최적화 초기화 */ static initialize(options: Partial = {}): void { this.options = { ...DEFAULT_OPTIMIZATION_OPTIONS, ...options }; console.log("⚡ 성능 최적화 시스템 초기화:", this.options); // 메모리 사용량 모니터링 (개발 모드에서만) if (process.env.NODE_ENV === "development") { this.startMemoryMonitoring(); } // 브라우저 개발자 도구 등록 if (typeof window !== "undefined") { (window as any).__PERFORMANCE_OPTIMIZER__ = this; } } /** * 배치 컴포넌트 등록 (성능 최적화) */ static async batchRegisterComponents( components: ComponentDefinition[], onProgress?: (completed: number, total: number) => void, ): Promise { if (!this.options.enableBatchRegistration) { // 배치 비활성화된 경우 순차 등록 for (let i = 0; i < components.length; i++) { await this.registerComponent(components[i]); onProgress?.(i + 1, components.length); } return; } const startTime = performance.now(); const { batchSize } = this.options; console.log(`📦 배치 등록 시작: ${components.length}개 컴포넌트 (배치 크기: ${batchSize})`); for (let i = 0; i < components.length; i += batchSize) { const batch = components.slice(i, i + batchSize); // 배치 처리 (비동기) await Promise.all(batch.map((component) => this.registerComponent(component))); // 진행률 콜백 onProgress?.(Math.min(i + batchSize, components.length), components.length); // UI 블로킹 방지를 위한 마이크로태스크 양보 await new Promise((resolve) => setTimeout(resolve, 0)); } const endTime = performance.now(); this.metrics.registrationTime = endTime - startTime; this.metrics.totalComponents += components.length; console.log(`✅ 배치 등록 완료: ${components.length}개 (${Math.round(this.metrics.registrationTime)}ms)`); } /** * 개별 컴포넌트 등록 (인덱싱 포함) */ private static async registerComponent(component: ComponentDefinition): Promise { // 인덱싱 활성화된 경우 if (this.options.enableIndexing) { this.updateIndices(component); } // 실제 등록 로직은 ComponentRegistry에서 처리 // 여기서는 성능 측정만 수행 } /** * 인덱스 업데이트 */ private static updateIndices(component: ComponentDefinition): void { // 카테고리 인덱스 if (!this.categoryIndex.has(component.category)) { this.categoryIndex.set(component.category, new Set()); } this.categoryIndex.get(component.category)!.add(component.id); // 웹타입 인덱스 if (!this.webTypeIndex.has(component.webType)) { this.webTypeIndex.set(component.webType, new Set()); } this.webTypeIndex.get(component.webType)!.add(component.id); // 이름 인덱스 (검색 최적화) const nameWords = component.name.toLowerCase().split(/\s+/); nameWords.forEach((word) => { if (word.length > 1) { if (!this.nameIndex.has(word)) { this.nameIndex.set(word, new Set()); } this.nameIndex.get(word)!.add(component.id); } }); } /** * 최적화된 컴포넌트 검색 */ static optimizedSearch( query: string, components: ComponentDefinition[], options: { useCache?: boolean; useIndex?: boolean } = {}, ): ComponentDefinition[] { const { useCache = true, useIndex = true } = options; const normalizedQuery = query.toLowerCase().trim(); const startTime = performance.now(); // 캐시 확인 if (useCache && this.searchCache.has(normalizedQuery)) { this.cacheHits++; this.cacheRequests++; const endTime = performance.now(); this.metrics.searchTime = endTime - startTime; console.log(`🎯 캐시 히트: "${query}" (${Math.round(this.metrics.searchTime)}ms)`); return this.searchCache.get(normalizedQuery)!; } this.cacheRequests++; let results: ComponentDefinition[] = []; // 인덱스 기반 검색 (활성화된 경우) if (useIndex && this.options.enableIndexing) { results = this.indexBasedSearch(normalizedQuery, components); } else { // 전체 검색 results = this.fullSearch(normalizedQuery, components); } const endTime = performance.now(); this.metrics.searchTime = endTime - startTime; // 캐시에 저장 (크기 제한) if (useCache && this.searchCache.size < this.options.cacheSize) { this.searchCache.set(normalizedQuery, results); } // 캐시 히트율 업데이트 this.metrics.cacheHitRate = this.cacheHits / this.cacheRequests; console.log(`🔍 검색 완료: "${query}" → ${results.length}개 (${Math.round(this.metrics.searchTime)}ms)`); return results; } /** * 인덱스 기반 빠른 검색 */ private static indexBasedSearch(query: string, components: ComponentDefinition[]): ComponentDefinition[] { const componentMap = new Map(components.map((c) => [c.id, c])); const matchingIds = new Set(); // 이름 인덱스에서 검색 const queryWords = query.split(/\s+/); queryWords.forEach((word) => { const ids = this.nameIndex.get(word); if (ids) { ids.forEach((id) => matchingIds.add(id)); } }); // 부분 문자열 검색 (이름 인덱스에서 부족한 부분) this.nameIndex.forEach((ids, indexedWord) => { if (indexedWord.includes(query) || query.includes(indexedWord)) { ids.forEach((id) => matchingIds.add(id)); } }); // 결과 변환 return Array.from(matchingIds) .map((id) => componentMap.get(id)) .filter((component): component is ComponentDefinition => component !== undefined); } /** * 전체 검색 (폴백) */ private static fullSearch(query: string, components: ComponentDefinition[]): ComponentDefinition[] { return components.filter( (component) => component.name.toLowerCase().includes(query) || component.description.toLowerCase().includes(query) || component.tags?.some((tag) => tag.toLowerCase().includes(query)), ); } /** * 메모리 최적화 */ static optimizeMemory(): void { if (!this.options.enableMemoryOptimization) return; const before = this.getMemoryUsage(); // 검색 캐시 정리 (오래된 항목 제거) if (this.searchCache.size > this.options.cacheSize * 0.8) { const entries = Array.from(this.searchCache.entries()); entries.slice(0, Math.floor(entries.length / 2)).forEach(([key]) => { this.searchCache.delete(key); }); } // 가비지 컬렉션 제안 (브라우저에서 가능한 경우) if ((window as any).gc) { (window as any).gc(); } const after = this.getMemoryUsage(); console.log(`🧹 메모리 최적화: ${before}MB → ${after}MB (${before - after}MB 절약)`); } /** * 지연 로딩 컴포넌트 관리 */ static async lazyLoadComponent(componentId: string): Promise { if (!this.options.enableLazyLoading) { return null; } const startTime = performance.now(); try { // 동적 import를 통한 지연 로딩 시뮬레이션 // 실제 구현에서는 컴포넌트 파일을 동적으로 로드 await new Promise((resolve) => setTimeout(resolve, 50)); // 네트워크 지연 시뮬레이션 const endTime = performance.now(); console.log(`🔄 지연 로딩 완료: ${componentId} (${Math.round(endTime - startTime)}ms)`); return null; // 실제 구현에서는 로드된 컴포넌트 반환 } catch (error) { console.error(`❌ 지연 로딩 실패: ${componentId}`, error); return null; } } /** * 성능 메트릭 조회 */ static getMetrics(): PerformanceMetrics { this.metrics.memoryUsage = this.getMemoryUsage(); return { ...this.metrics }; } /** * 메모리 사용량 측정 */ private static getMemoryUsage(): number { if (typeof (performance as any).memory !== "undefined") { return Math.round(((performance as any).memory.usedJSHeapSize / 1024 / 1024) * 100) / 100; } return 0; } /** * 메모리 모니터링 시작 */ private static startMemoryMonitoring(): void { setInterval(() => { const usage = this.getMemoryUsage(); if (usage > 100) { // 100MB 초과 시 경고 console.warn(`⚠️ 높은 메모리 사용량: ${usage}MB`); this.optimizeMemory(); } }, 30000); // 30초마다 체크 } /** * 성능 마크 시작 */ static markStart(name: string): void { this.performanceMarks.set(name, performance.now()); } /** * 성능 마크 종료 및 측정 */ static markEnd(name: string): number { const startTime = this.performanceMarks.get(name); if (!startTime) { console.warn(`⚠️ 성능 마크를 찾을 수 없음: ${name}`); return 0; } const duration = performance.now() - startTime; this.performanceMarks.delete(name); console.log(`⏱️ ${name}: ${Math.round(duration)}ms`); return duration; } /** * 인덱스 상태 조회 */ static getIndexStats(): { categories: number; webTypes: number; nameWords: number; totalIndexEntries: number; } { return { categories: this.categoryIndex.size, webTypes: this.webTypeIndex.size, nameWords: this.nameIndex.size, totalIndexEntries: Array.from(this.categoryIndex.values()).reduce((sum, set) => sum + set.size, 0) + Array.from(this.webTypeIndex.values()).reduce((sum, set) => sum + set.size, 0) + Array.from(this.nameIndex.values()).reduce((sum, set) => sum + set.size, 0), }; } /** * 캐시 상태 조회 */ static getCacheStats(): { searchCacheSize: number; hitRate: number; requests: number; hits: number; } { return { searchCacheSize: this.searchCache.size, hitRate: this.metrics.cacheHitRate, requests: this.cacheRequests, hits: this.cacheHits, }; } /** * 모든 캐시와 인덱스 클리어 */ static clearAll(): void { this.searchCache.clear(); this.categoryIndex.clear(); this.webTypeIndex.clear(); this.nameIndex.clear(); this.performanceMarks.clear(); this.cacheHits = 0; this.cacheRequests = 0; console.log("🧹 모든 캐시와 인덱스 클리어 완료"); } /** * 설정 업데이트 */ static updateOptions(newOptions: Partial): void { this.options = { ...this.options, ...newOptions }; console.log("⚙️ 성능 최적화 설정 업데이트:", this.options); } /** * 벤치마크 실행 */ static async runBenchmark(components: ComponentDefinition[]): Promise<{ batchRegistration: number; indexedSearch: number; fullSearch: number; memoryUsage: number; }> { console.log("🏃 성능 벤치마크 실행 중..."); // 배치 등록 벤치마크 this.markStart("batchRegistration"); await this.batchRegisterComponents(components); const batchTime = this.markEnd("batchRegistration"); // 인덱스 검색 벤치마크 this.markStart("indexedSearch"); this.optimizedSearch("button", components, { useIndex: true }); const indexedSearchTime = this.markEnd("indexedSearch"); // 전체 검색 벤치마크 this.markStart("fullSearch"); this.optimizedSearch("button", components, { useIndex: false }); const fullSearchTime = this.markEnd("fullSearch"); const memoryUsage = this.getMemoryUsage(); const results = { batchRegistration: batchTime, indexedSearch: indexedSearchTime, fullSearch: fullSearchTime, memoryUsage, }; console.log("📊 벤치마크 결과:", results); return results; } } // 자동 초기화 if (typeof window !== "undefined") { PerformanceOptimizer.initialize(); console.log("⚡ 성능 최적화 시스템이 초기화되었습니다."); }