# ⚡ 버튼 제어관리 성능 최적화 전략 ## 🎯 성능 목표 설정 ### 허용 가능한 응답 시간 - **즉시 반응**: 0-100ms (사용자가 지연을 느끼지 않음) - **빠른 응답**: 100-300ms (약간의 지연이지만 허용 가능) - **보통 응답**: 300-1000ms (Loading 스피너 필요) - **❌ 느린 응답**: 1000ms+ (사용자 불만 발생) ### 현실적 목표 - **간단한 제어관리**: 200ms 이내 - **복잡한 제어관리**: 500ms 이내 - **매우 복잡한 로직**: 1초 이내 (비동기 처리) ## 🚀 핵심 최적화 전략 ### 1. **즉시 응답 + 백그라운드 실행 패턴** ```typescript const handleButtonClick = async (component: ComponentData) => { const config = component.webTypeConfig; // 🔥 즉시 UI 응답 (0ms) setButtonState("executing"); toast.success("처리를 시작했습니다."); try { // Step 1: 기존 액션 우선 실행 (빠른 응답) if (config?.actionType && config?.dataflowTiming !== "replace") { await executeOriginalAction(config.actionType, component); // 사용자에게 즉시 피드백 toast.success(`${getActionDisplayName(config.actionType)} 완료`); } // Step 2: 제어관리는 백그라운드에서 실행 if (config?.enableDataflowControl) { // 🔥 비동기로 실행 (UI 블로킹 없음) executeDataflowInBackground(config, component.id) .then((result) => { if (result.success) { showDataflowResult(result); } }) .catch((error) => { console.error("Background dataflow failed:", error); // 조용히 실패 처리 (사용자 방해 최소화) }); } } finally { setButtonState("idle"); } }; // 백그라운드 실행 함수 const executeDataflowInBackground = async ( config: ButtonTypeConfig, buttonId: string ): Promise => { // 성능 모니터링 const startTime = performance.now(); try { const result = await apiClient.post("/api/button-dataflow/execute-async", { buttonConfig: config, buttonId: buttonId, priority: "background", // 우선순위 낮게 설정 }); const executionTime = performance.now() - startTime; console.log(`⚡ Dataflow 실행 시간: ${executionTime.toFixed(2)}ms`); return result.data; } catch (error) { // 에러 로깅만 하고 사용자 방해하지 않음 console.error("Background dataflow error:", error); throw error; } }; ``` ### 2. **스마트 캐싱 시스템** ```typescript // 다층 캐싱 전략 class DataflowCache { private memoryCache = new Map(); // L1: 메모리 캐시 private persistCache: IDBDatabase | null = null; // L2: 브라우저 저장소 constructor() { this.initPersistentCache(); } // 버튼별 제어관리 설정 캐싱 async getButtonDataflowConfig( buttonId: string ): Promise { const cacheKey = `button_dataflow_${buttonId}`; // L1: 메모리에서 확인 (1ms) if (this.memoryCache.has(cacheKey)) { console.log("⚡ Memory cache hit:", buttonId); return this.memoryCache.get(cacheKey); } // L2: 브라우저 저장소에서 확인 (5-10ms) const cached = await this.getFromPersistentCache(cacheKey); if (cached && !this.isExpired(cached)) { console.log("💾 Persistent cache hit:", buttonId); this.memoryCache.set(cacheKey, cached.data); return cached.data; } // L3: 서버에서 로드 (100-300ms) console.log("🌐 Loading from server:", buttonId); const serverData = await this.loadFromServer(buttonId); // 캐시에 저장 this.memoryCache.set(cacheKey, serverData); await this.saveToPersistentCache(cacheKey, serverData); return serverData; } // 관계도별 실행 계획 캐싱 async getCachedExecutionPlan( diagramId: number ): Promise { // 자주 사용되는 실행 계획을 캐시 const cacheKey = `execution_plan_${diagramId}`; return this.getFromCache(cacheKey, async () => { return await this.loadExecutionPlan(diagramId); }); } } // 사용 예시 const dataflowCache = new DataflowCache(); const optimizedButtonClick = async (buttonId: string) => { // 🔥 캐시에서 즉시 로드 (1-10ms) const config = await dataflowCache.getButtonDataflowConfig(buttonId); if (config) { // 설정이 캐시되어 있으면 즉시 실행 await executeDataflow(config); } }; ``` ### 3. **데이터베이스 최적화** ```sql -- 🔥 버튼별 제어관리 조회 최적화 인덱스 CREATE INDEX CONCURRENTLY idx_dataflow_button_fast_lookup ON dataflow_diagrams USING GIN ((control->'buttonId')) WHERE category @> '["button-trigger"]' AND company_code IS NOT NULL; -- 🔥 실행 조건 빠른 검색 인덱스 CREATE INDEX CONCURRENTLY idx_dataflow_trigger_type ON dataflow_diagrams (company_code, ((control->0->>'triggerType'))) WHERE control IS NOT NULL; -- 🔥 자주 사용되는 관계도 우선 조회 CREATE INDEX CONCURRENTLY idx_dataflow_usage_priority ON dataflow_diagrams (company_code, updated_at DESC) WHERE category @> '["button-trigger"]'; ``` ```typescript // 최적화된 데이터베이스 조회 export class OptimizedEventTriggerService { // 🔥 버튼별 제어관리 직접 조회 (전체 스캔 제거) static async getButtonDataflowConfigs( buttonId: string, companyCode: string ): Promise { // 기존: 모든 관계도 스캔 (느림) // const allDiagrams = await prisma.$queryRaw`SELECT * FROM dataflow_diagrams WHERE...` // 🔥 새로운: 버튼별 직접 조회 (빠름) const configs = await prisma.$queryRaw` SELECT diagram_id, control, plan, category FROM dataflow_diagrams WHERE company_code = ${companyCode} AND control @> '[{"buttonId": ${buttonId}}]' AND category @> '["button-trigger"]' ORDER BY updated_at DESC LIMIT 5; -- 최대 5개만 조회 `; return configs as DataflowConfig[]; } // 🔥 조건 검증 최적화 (메모리 내 처리) static evaluateConditionsOptimized( conditions: DataflowCondition[], data: Record ): boolean { // 간단한 조건은 메모리에서 즉시 처리 (1-5ms) for (const condition of conditions) { if (condition.type === "condition") { const fieldValue = data[condition.field!]; const result = this.evaluateSimpleCondition( fieldValue, condition.operator!, condition.value ); if (!result) return false; } } return true; } private static evaluateSimpleCondition( fieldValue: any, operator: string, conditionValue: any ): boolean { switch (operator) { case "=": return fieldValue === conditionValue; case "!=": return fieldValue !== conditionValue; case ">": return fieldValue > conditionValue; case "<": return fieldValue < conditionValue; case ">=": return fieldValue >= conditionValue; case "<=": return fieldValue <= conditionValue; case "LIKE": return String(fieldValue) .toLowerCase() .includes(String(conditionValue).toLowerCase()); default: return true; } } } ``` ### 4. **배치 처리 및 큐 시스템** ```typescript // 🔥 제어관리 작업 큐 시스템 class DataflowQueue { private queue: Array<{ id: string; buttonId: string; config: ButtonDataflowConfig; priority: "high" | "normal" | "low"; timestamp: number; }> = []; private processing = false; // 작업 추가 (즉시 반환) enqueue( buttonId: string, config: ButtonDataflowConfig, priority: "high" | "normal" | "low" = "normal" ): string { const jobId = `job_${Date.now()}_${Math.random() .toString(36) .substr(2, 9)}`; this.queue.push({ id: jobId, buttonId, config, priority, timestamp: Date.now(), }); // 우선순위별 정렬 this.queue.sort((a, b) => { const priorityWeight = { high: 3, normal: 2, low: 1 }; return priorityWeight[b.priority] - priorityWeight[a.priority]; }); // 비동기 처리 시작 this.processQueue(); return jobId; // 작업 ID 즉시 반환 } // 배치 처리 private async processQueue(): Promise { if (this.processing || this.queue.length === 0) return; this.processing = true; try { // 동시에 최대 3개 작업 처리 const batch = this.queue.splice(0, 3); const promises = batch.map((job) => this.executeDataflowJob(job).catch((error) => { console.error(`Job ${job.id} failed:`, error); return { success: false, error }; }) ); await Promise.all(promises); } finally { this.processing = false; // 큐에 더 많은 작업이 있으면 계속 처리 if (this.queue.length > 0) { setTimeout(() => this.processQueue(), 10); } } } private async executeDataflowJob(job: any): Promise { const startTime = performance.now(); try { const result = await OptimizedEventTriggerService.executeButtonDataflow( job.buttonId, job.config ); const executionTime = performance.now() - startTime; console.log( `⚡ Job ${job.id} completed in ${executionTime.toFixed(2)}ms` ); return result; } catch (error) { console.error(`❌ Job ${job.id} failed:`, error); throw error; } } } // 전역 큐 인스턴스 const dataflowQueue = new DataflowQueue(); // 사용 예시: 즉시 응답하는 버튼 클릭 const optimizedButtonClick = async ( buttonId: string, config: ButtonDataflowConfig ) => { // 🔥 즉시 작업 큐에 추가하고 반환 (1-5ms) const jobId = dataflowQueue.enqueue(buttonId, config, "normal"); // 사용자에게 즉시 피드백 toast.success("작업이 시작되었습니다."); return jobId; }; ``` ### 5. **프론트엔드 최적화** ```typescript // 🔥 React 성능 최적화 const OptimizedButtonComponent = React.memo( ({ component }: { component: ComponentData }) => { const [isExecuting, setIsExecuting] = useState(false); const [executionTime, setExecutionTime] = useState(null); // 디바운싱으로 중복 클릭 방지 const handleClick = useDebouncedCallback(async () => { if (isExecuting) return; setIsExecuting(true); const startTime = performance.now(); try { await optimizedButtonClick(component.id, component.webTypeConfig); } finally { const endTime = performance.now(); setExecutionTime(endTime - startTime); setIsExecuting(false); } }, 300); // 300ms 디바운싱 return ( ); } ); // 리스트 가상화로 대량 버튼 렌더링 최적화 const VirtualizedButtonList = ({ buttons }: { buttons: ComponentData[] }) => { return ( {({ index, style, data }) => (
)}
); }; ``` ## 📊 성능 모니터링 ```typescript // 실시간 성능 모니터링 class PerformanceMonitor { private metrics: { buttonClicks: number; averageResponseTime: number; slowQueries: Array<{ query: string; time: number; timestamp: Date }>; cacheHitRate: number; } = { buttonClicks: 0, averageResponseTime: 0, slowQueries: [], cacheHitRate: 0, }; recordButtonClick(executionTime: number) { this.metrics.buttonClicks++; // 이동 평균으로 응답 시간 계산 this.metrics.averageResponseTime = this.metrics.averageResponseTime * 0.9 + executionTime * 0.1; // 느린 쿼리 기록 (500ms 이상) if (executionTime > 500) { this.metrics.slowQueries.push({ query: "button_dataflow_execution", time: executionTime, timestamp: new Date(), }); // 최대 100개만 보관 if (this.metrics.slowQueries.length > 100) { this.metrics.slowQueries.shift(); } } // 성능 경고 if (executionTime > 1000) { console.warn(`🐌 Slow button execution: ${executionTime}ms`); } } getPerformanceReport() { return { ...this.metrics, recommendation: this.getRecommendation(), }; } private getRecommendation(): string[] { const recommendations: string[] = []; if (this.metrics.averageResponseTime > 300) { recommendations.push( "평균 응답 시간이 느립니다. 캐싱 설정을 확인하세요." ); } if (this.metrics.cacheHitRate < 80) { recommendations.push("캐시 히트율이 낮습니다. 캐시 전략을 재검토하세요."); } if (this.metrics.slowQueries.length > 10) { recommendations.push("느린 쿼리가 많습니다. 인덱스를 확인하세요."); } return recommendations; } } // 전역 모니터 const performanceMonitor = new PerformanceMonitor(); // 사용 예시 const monitoredButtonClick = async (buttonId: string) => { const startTime = performance.now(); try { await executeButtonAction(buttonId); } finally { const executionTime = performance.now() - startTime; performanceMonitor.recordButtonClick(executionTime); } }; ``` ## 🎯 성능 최적화 로드맵 ### Phase 1: 즉시 개선 (1-2주) 1. ✅ **즉시 응답 패턴** 도입 2. ✅ **기본 캐싱** 구현 3. ✅ **데이터베이스 인덱스** 추가 4. ✅ **성능 모니터링** 설정 ### Phase 2: 고급 최적화 (3-4주) 1. 🔄 **작업 큐 시스템** 구현 2. 🔄 **배치 처리** 도입 3. 🔄 **다층 캐싱** 완성 4. 🔄 **가상화 렌더링** 적용 ### Phase 3: 고도화 (5-6주) 1. ⏳ **프리로딩** 시스템 2. ⏳ **CDN 캐싱** 도입 3. ⏳ **서버 사이드 캐싱** 4. ⏳ **성능 대시보드** 이렇게 단계적으로 최적화하면 사용자가 체감할 수 있는 성능 개선을 점진적으로 달성할 수 있습니다!