import { useMemo } from "react"; import codeCache from "@/lib/cache/codeCache"; /** * 엔티티 조인 최적화 훅 * 테이블 간의 관계를 분석하여 최적화된 조인 전략을 제공합니다. */ interface JoinOptimization { strategy: "eager" | "lazy" | "batch"; priority: number; estimatedCost: number; } interface EntityRelation { fromTable: string; toTable: string; joinType: "inner" | "left" | "right" | "full"; cardinality: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many"; } export const useEntityJoinOptimization = ( relations: EntityRelation[], queryContext?: { expectedResultSize?: number; performanceProfile?: "fast" | "balanced" | "memory-efficient"; }, ) => { const optimization = useMemo(() => { const cacheKey = `join-optimization:${JSON.stringify(relations)}:${JSON.stringify(queryContext)}`; // 캐시에서 먼저 확인 const cached = codeCache.get(cacheKey); if (cached) { return cached; } // 최적화 전략 계산 const optimizations: Record = {}; relations.forEach((relation) => { const key = `${relation.fromTable}-${relation.toTable}`; // 카디널리티에 따른 기본 전략 let strategy: JoinOptimization["strategy"] = "eager"; let priority = 1; let estimatedCost = 1; switch (relation.cardinality) { case "one-to-one": strategy = "eager"; priority = 3; estimatedCost = 1; break; case "one-to-many": strategy = "lazy"; priority = 2; estimatedCost = 2; break; case "many-to-one": strategy = "eager"; priority = 2; estimatedCost = 1.5; break; case "many-to-many": strategy = "batch"; priority = 1; estimatedCost = 3; break; } // 성능 프로필에 따른 조정 if (queryContext?.performanceProfile === "fast") { if (strategy === "lazy") strategy = "eager"; priority += 1; } else if (queryContext?.performanceProfile === "memory-efficient") { if (strategy === "eager") strategy = "lazy"; estimatedCost *= 0.8; } // 예상 결과 크기에 따른 조정 if (queryContext?.expectedResultSize && queryContext.expectedResultSize > 1000) { if (strategy === "eager") strategy = "batch"; estimatedCost *= 1.2; } optimizations[key] = { strategy, priority, estimatedCost, }; }); // 결과를 캐시에 저장 (1분 TTL) codeCache.set(cacheKey, optimizations, 60 * 1000); return optimizations; }, [relations, queryContext]); const getOptimizationForRelation = (fromTable: string, toTable: string): JoinOptimization | null => { const key = `${fromTable}-${toTable}`; return optimization[key] || null; }; const getSortedRelations = (): Array<{ relation: EntityRelation; optimization: JoinOptimization }> => { return relations .map((relation) => ({ relation, optimization: getOptimizationForRelation(relation.fromTable, relation.toTable)!, })) .filter((item) => item.optimization) .sort((a, b) => b.optimization.priority - a.optimization.priority); }; const getTotalEstimatedCost = (): number => { return Object.values(optimization).reduce((total, opt) => total + opt.estimatedCost, 0); }; return { optimization, getOptimizationForRelation, getSortedRelations, getTotalEstimatedCost, }; };