ERP-node/frontend/lib/hooks/useEntityJoinOptimization.ts

126 lines
3.5 KiB
TypeScript

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<string, JoinOptimization> = {};
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,
};
};