diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index 12bdb7d1..a8356721 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -322,6 +322,7 @@ export const TableListComponent: React.FC = ({ const [searchTerm, setSearchTerm] = useState(""); const [sortColumn, setSortColumn] = useState(null); const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc"); + const hasInitializedSort = useRef(false); const [columnLabels, setColumnLabels] = useState>({}); const [tableLabel, setTableLabel] = useState(""); const [localPageSize, setLocalPageSize] = useState(tableConfig.pagination?.pageSize || 20); @@ -508,6 +509,28 @@ export const TableListComponent: React.FC = ({ unregisterTable, ]); + // ๐ŸŽฏ ์ดˆ๊ธฐ ๋กœ๋“œ ์‹œ localStorage์—์„œ ์ •๋ ฌ ์ƒํƒœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ + useEffect(() => { + if (!tableConfig.selectedTable || !userId || hasInitializedSort.current) return; + + const storageKey = `table_sort_state_${tableConfig.selectedTable}_${userId}`; + const savedSort = localStorage.getItem(storageKey); + + if (savedSort) { + try { + const { column, direction } = JSON.parse(savedSort); + if (column && direction) { + setSortColumn(column); + setSortDirection(direction); + hasInitializedSort.current = true; + console.log("๐Ÿ“‚ localStorage์—์„œ ์ •๋ ฌ ์ƒํƒœ ๋ณต์›:", { column, direction }); + } + } catch (error) { + console.error("โŒ ์ •๋ ฌ ์ƒํƒœ ๋ณต์› ์‹คํŒจ:", error); + } + } + }, [tableConfig.selectedTable, userId]); + // ๐Ÿ†• ์ดˆ๊ธฐ ๋กœ๋“œ ์‹œ localStorage์—์„œ ์ปฌ๋Ÿผ ์ˆœ์„œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ useEffect(() => { if (!tableConfig.selectedTable || !userId) return; @@ -955,6 +978,20 @@ export const TableListComponent: React.FC = ({ newSortDirection = "asc"; } + // ๐ŸŽฏ ์ •๋ ฌ ์ƒํƒœ๋ฅผ localStorage์— ์ €์žฅ (์‚ฌ์šฉ์ž๋ณ„) + if (tableConfig.selectedTable && userId) { + const storageKey = `table_sort_state_${tableConfig.selectedTable}_${userId}`; + try { + localStorage.setItem(storageKey, JSON.stringify({ + column: newSortColumn, + direction: newSortDirection + })); + console.log("๐Ÿ’พ ์ •๋ ฌ ์ƒํƒœ ์ €์žฅ:", { column: newSortColumn, direction: newSortDirection }); + } catch (error) { + console.error("โŒ ์ •๋ ฌ ์ƒํƒœ ์ €์žฅ ์‹คํŒจ:", error); + } + } + console.log("๐Ÿ“Š ์ƒˆ๋กœ์šด ์ •๋ ฌ ์ •๋ณด:", { newSortColumn, newSortDirection }); console.log("๐Ÿ” onSelectedRowsChange ์กด์žฌ ์—ฌ๋ถ€:", !!onSelectedRowsChange); @@ -1876,11 +1913,59 @@ export const TableListComponent: React.FC = ({ }; }, [tableConfig.selectedTable, isDesignMode]); - // ์ดˆ๊ธฐ ์ปฌ๋Ÿผ ๋„ˆ๋น„ ์ธก์ • (ํ•œ ๋ฒˆ๋งŒ) + // ๐ŸŽฏ ์ปฌ๋Ÿผ ๋„ˆ๋น„ ์ž๋™ ๊ณ„์‚ฐ (๋‚ด์šฉ ๊ธฐ๋ฐ˜) + const calculateOptimalColumnWidth = useCallback((columnName: string, displayName: string): number => { + // ๊ธฐ๋ณธ ๋„ˆ๋น„ ์„ค์ • + const MIN_WIDTH = 100; + const MAX_WIDTH = 400; + const PADDING = 48; // ์ขŒ์šฐ ํŒจ๋”ฉ + ์—ฌ์œ  ๊ณต๊ฐ„ + const HEADER_PADDING = 60; // ํ—ค๋” ์ถ”๊ฐ€ ์—ฌ์œ  (์ •๋ ฌ ์•„์ด์ฝ˜ ๋“ฑ) + + // ํ—ค๋” ํ…์ŠคํŠธ ๋„ˆ๋น„ ๊ณ„์‚ฐ (๋Œ€๋žต 8px per character) + const headerWidth = (displayName?.length || columnName.length) * 10 + HEADER_PADDING; + + // ๋ฐ์ดํ„ฐ ์…€ ๋„ˆ๋น„ ๊ณ„์‚ฐ (์ƒ์œ„ 50๊ฐœ ์ƒ˜ํ”Œ๋ง) + const sampleSize = Math.min(50, data.length); + let maxDataWidth = headerWidth; + + for (let i = 0; i < sampleSize; i++) { + const cellValue = data[i]?.[columnName]; + if (cellValue !== null && cellValue !== undefined) { + const cellText = String(cellValue); + // ์ˆซ์ž๋Š” ์ข๊ฒŒ, ํ…์ŠคํŠธ๋Š” ๋„“๊ฒŒ ๊ณ„์‚ฐ + const isNumber = !isNaN(Number(cellValue)) && cellValue !== ""; + const charWidth = isNumber ? 8 : 9; + const cellWidth = cellText.length * charWidth + PADDING; + maxDataWidth = Math.max(maxDataWidth, cellWidth); + } + } + + // ์ตœ์†Œ/์ตœ๋Œ€ ๋ฒ”์œ„ ๋‚ด๋กœ ์ œํ•œ + return Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.ceil(maxDataWidth))); + }, [data]); + + // ๐ŸŽฏ localStorage์—์„œ ์ปฌ๋Ÿผ ๋„ˆ๋น„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ฐ ์ดˆ๊ธฐ ๊ณ„์‚ฐ useEffect(() => { - if (!hasInitializedWidths.current && visibleColumns.length > 0) { - // ์•ฝ๊ฐ„์˜ ์ง€์—ฐ์„ ๋‘๊ณ  DOM์ด ์™„์ „ํžˆ ๋ Œ๋”๋ง๋œ ํ›„ ์ธก์ • + if (!hasInitializedWidths.current && visibleColumns.length > 0 && data.length > 0) { const timer = setTimeout(() => { + const storageKey = tableConfig.selectedTable && userId + ? `table_column_widths_${tableConfig.selectedTable}_${userId}` + : null; + + // 1. localStorage์—์„œ ์ €์žฅ๋œ ๋„ˆ๋น„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ + let savedWidths: Record = {}; + if (storageKey) { + try { + const saved = localStorage.getItem(storageKey); + if (saved) { + savedWidths = JSON.parse(saved); + } + } catch (error) { + console.error("์ปฌ๋Ÿผ ๋„ˆ๋น„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹คํŒจ:", error); + } + } + + // 2. ์ž๋™ ๊ณ„์‚ฐ ๋˜๋Š” ์ €์žฅ๋œ ๋„ˆ๋น„ ์ ์šฉ const newWidths: Record = {}; let hasAnyWidth = false; @@ -1888,13 +1973,18 @@ export const TableListComponent: React.FC = ({ // ์ฒดํฌ๋ฐ•์Šค ์ปฌ๋Ÿผ์€ ์ œ์™ธ (๊ณ ์ • 48px) if (column.columnName === "__checkbox__") return; - const thElement = columnRefs.current[column.columnName]; - if (thElement) { - const measuredWidth = thElement.offsetWidth; - if (measuredWidth > 0) { - newWidths[column.columnName] = measuredWidth; - hasAnyWidth = true; - } + // ์ €์žฅ๋œ ๋„ˆ๋น„๊ฐ€ ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ + if (savedWidths[column.columnName]) { + newWidths[column.columnName] = savedWidths[column.columnName]; + hasAnyWidth = true; + } else { + // ์ €์žฅ๋œ ๋„ˆ๋น„๊ฐ€ ์—†์œผ๋ฉด ์ž๋™ ๊ณ„์‚ฐ + const optimalWidth = calculateOptimalColumnWidth( + column.columnName, + columnLabels[column.columnName] || column.displayName + ); + newWidths[column.columnName] = optimalWidth; + hasAnyWidth = true; } }); @@ -1902,11 +1992,11 @@ export const TableListComponent: React.FC = ({ setColumnWidths(newWidths); hasInitializedWidths.current = true; } - }, 100); + }, 150); // DOM ๋ Œ๋”๋ง ๋Œ€๊ธฐ return () => clearTimeout(timer); } - }, [visibleColumns]); + }, [visibleColumns, data, tableConfig.selectedTable, userId, calculateOptimalColumnWidth, columnLabels]); // ======================================== // ํŽ˜์ด์ง€๋„ค์ด์…˜ JSX @@ -2241,7 +2331,21 @@ export const TableListComponent: React.FC = ({ // ์ตœ์ข… ๋„ˆ๋น„๋ฅผ state์— ์ €์žฅ if (thElement) { const finalWidth = Math.max(80, thElement.offsetWidth); - setColumnWidths((prev) => ({ ...prev, [column.columnName]: finalWidth })); + setColumnWidths((prev) => { + const newWidths = { ...prev, [column.columnName]: finalWidth }; + + // ๐ŸŽฏ localStorage์— ์ปฌ๋Ÿผ ๋„ˆ๋น„ ์ €์žฅ (์‚ฌ์šฉ์ž๋ณ„) + if (tableConfig.selectedTable && userId) { + const storageKey = `table_column_widths_${tableConfig.selectedTable}_${userId}`; + try { + localStorage.setItem(storageKey, JSON.stringify(newWidths)); + } catch (error) { + console.error("์ปฌ๋Ÿผ ๋„ˆ๋น„ ์ €์žฅ ์‹คํŒจ:", error); + } + } + + return newWidths; + }); } // ํ…์ŠคํŠธ ์„ ํƒ ๋ณต์›