추가 감소

This commit is contained in:
dohyeons 2025-09-29 17:29:58 +09:00
parent 808a317ed0
commit 6e8f529cd3
1 changed files with 91 additions and 15 deletions

View File

@ -1,6 +1,6 @@
"use client";
import React, { useState, useEffect, useMemo } from "react";
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
import { TableListConfig, ColumnConfig } from "./types";
import { WebType } from "@/types/common";
import { tableTypeApi } from "@/lib/api/screen";
@ -35,6 +35,51 @@ const cleanupTableCache = () => {
if (typeof window !== "undefined") {
setInterval(cleanupTableCache, 10 * 60 * 1000);
}
// 요청 디바운싱을 위한 전역 타이머
const debounceTimers = new Map<string, NodeJS.Timeout>();
// 진행 중인 요청 추적 (중복 요청 방지)
const activeRequests = new Map<string, Promise<any>>();
// 디바운싱된 API 호출 함수 (중복 요청 방지 포함)
const debouncedApiCall = <T extends any[], R>(key: string, fn: (...args: T) => Promise<R>, delay: number = 300) => {
return (...args: T): Promise<R> => {
// 이미 진행 중인 동일한 요청이 있으면 그 결과를 반환
const activeRequest = activeRequests.get(key);
if (activeRequest) {
console.log(`🔄 진행 중인 요청 재사용: ${key}`);
return activeRequest as Promise<R>;
}
return new Promise((resolve, reject) => {
// 기존 타이머 제거
const existingTimer = debounceTimers.get(key);
if (existingTimer) {
clearTimeout(existingTimer);
}
// 새 타이머 설정
const timer = setTimeout(async () => {
try {
// 요청 시작 시 활성 요청으로 등록
const requestPromise = fn(...args);
activeRequests.set(key, requestPromise);
const result = await requestPromise;
resolve(result);
} catch (error) {
reject(error);
} finally {
debounceTimers.delete(key);
activeRequests.delete(key);
}
}, delay);
debounceTimers.set(key, timer);
});
};
};
import { useEntityJoinOptimization } from "@/lib/hooks/useEntityJoinOptimization";
import { Button } from "@/components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
@ -148,6 +193,15 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const [joinColumnMapping, setJoinColumnMapping] = useState<Record<string, string>>({});
const [columnMeta, setColumnMeta] = useState<Record<string, { webType?: string; codeCategory?: string }>>({}); // 🎯 컬럼 메타정보 (웹타입, 코드카테고리)
// 컬럼 정보 메모이제이션
const memoizedColumnInfo = useMemo(() => {
return {
labels: columnLabels,
meta: columnMeta,
visibleColumns: (tableConfig.columns || []).filter((col) => col.visible !== false),
};
}, [columnLabels, columnMeta, tableConfig.columns]);
// 고급 필터 관련 state
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
@ -369,8 +423,20 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
}
};
// 테이블 데이터 가져오기
const fetchTableData = async () => {
// 디바운싱된 테이블 데이터 가져오기
const fetchTableDataDebounced = useCallback(
debouncedApiCall(
`fetchTableData_${tableConfig.selectedTable}_${currentPage}_${localPageSize}`,
async () => {
return fetchTableDataInternal();
},
200, // 200ms 디바운스
),
[tableConfig.selectedTable, currentPage, localPageSize, searchTerm, sortColumn, sortDirection, searchValues],
);
// 실제 테이블 데이터 가져오기 함수
const fetchTableDataInternal = async () => {
if (!tableConfig.selectedTable) {
setData([]);
return;
@ -558,15 +624,25 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
codeColumns.map(([col, meta]) => `${col}(${meta.codeCategory})`),
);
// 필요한 코드 카테고리들을 추출하여 배치 로드
const categoryList = codeColumns.map(([, meta]) => meta.codeCategory).filter(Boolean) as string[];
// 필요한 코드 카테고리들을 추출하여 배치 로드 (중복 제거)
const categoryList = [
...new Set(codeColumns.map(([, meta]) => meta.codeCategory).filter(Boolean)),
] as string[];
// 이미 캐시된 카테고리는 제외
const uncachedCategories = categoryList.filter((category) => !codeCache.getCodeSync(category));
if (uncachedCategories.length > 0) {
try {
await codeCache.preloadCodes(categoryList);
console.log(`📋 새로운 코드 카테고리 로딩: ${uncachedCategories.join(", ")}`);
await codeCache.preloadCodes(uncachedCategories);
console.log("📋 모든 코드 캐시 로드 완료 (전역 캐시)");
} catch (error) {
console.error("❌ 코드 캐시 로드 중 오류:", error);
}
} else {
console.log("📋 모든 코드 카테고리가 이미 캐시됨");
}
}
// 🎯 Entity 조인된 컬럼 처리 - 사용자가 설정한 컬럼들만 사용
@ -759,18 +835,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const handleAdvancedSearch = () => {
setCurrentPage(1);
fetchTableData();
fetchTableDataDebounced();
};
const handleClearAdvancedFilters = () => {
setSearchValues({});
setCurrentPage(1);
fetchTableData();
fetchTableDataDebounced();
};
// 새로고침
const handleRefresh = () => {
fetchTableData();
fetchTableDataDebounced();
};
// 체크박스 핸들러들
@ -872,7 +948,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
useEffect(() => {
if (tableConfig.autoLoad && !isDesignMode) {
fetchTableData();
fetchTableDataDebounced();
}
}, [
tableConfig.selectedTable,
@ -896,7 +972,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
console.log("🔄 선택 상태 초기화 - 빈 배열 전달");
onSelectedRowsChange?.([], []);
// 테이블 데이터 새로고침
fetchTableData();
fetchTableDataDebounced();
}
}, [refreshKey]);