From 511884f323e3fc29aec40a603194eb644c5771bc Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 3 Nov 2025 12:13:56 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=BB=AC=EB=9F=BC=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=20=EB=84=88=EB=B9=84=20=EC=9D=B4=ED=95=98=EB=A1=9C=20=EC=A4=84?= =?UTF-8?q?=EC=96=B4=EB=93=A4=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ 해결 방법: - 백분율 defaultWidth 제거, 초기값은 undefined로 설정 - ref callback에서 첫 렌더링 시 실제 offsetWidth 측정 - 측정한 실제 너비를 columnWidths state에 저장 - 이후 드래그로 80px까지 줄일 수 있음 ✅ 적용 파일: - TableListComponent.tsx (실제 화면) - InteractiveDataTable.tsx (디자인 모드) ✅ 기술적 개선: - table-layout: fixed + 동적 초기 너비 측정 - DOM 직접 조작으로 부드러운 리사이즈 - 최소 80px 보장 (Math.max) --- .../screen/InteractiveDataTable.tsx | 38 +++++++++++++++---- .../table-list/TableListComponent.tsx | 17 +++++++-- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index 8a31756c..c9b24892 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -1921,7 +1921,7 @@ export const InteractiveDataTable: React.FC = ({ {visibleColumns.length > 0 ? ( <>
- +
{/* 체크박스 컬럼 (삭제 기능이 활성화된 경우) */} @@ -1935,15 +1935,25 @@ export const InteractiveDataTable: React.FC = ({ )} {visibleColumns.map((column: DataTableColumn, columnIndex) => { const columnWidth = columnWidths[column.id]; - const defaultWidth = `${((column.gridColumns || 2) / totalGridColumns) * 100}%`; return ( { + // 첫 렌더링 시 실제 너비를 측정해서 상태에 저장 + if (el && !columnWidth) { + const measuredWidth = el.offsetWidth; + if (measuredWidth > 0) { + setColumnWidths(prev => ({ + ...prev, + [column.id]: measuredWidth + })); + } + } + }} className="relative bg-gradient-to-r from-gray-50 to-slate-50 px-4 font-semibold text-gray-700 select-none" style={{ - width: columnWidth ? `${columnWidth}px` : defaultWidth, - minWidth: '80px', + width: columnWidth ? `${columnWidth}px` : undefined, userSelect: 'none' }} > @@ -1956,10 +1966,13 @@ export const InteractiveDataTable: React.FC = ({ onClick={(e) => e.stopPropagation()} onMouseDown={(e) => { e.preventDefault(); - e.stopPropagation(); // 상위 컴포넌트 드래그 이벤트 방지 + e.stopPropagation(); + + const thElement = e.currentTarget.parentElement as HTMLTableCellElement; + if (!thElement) return; const startX = e.clientX; - const startWidth = columnWidth || (e.currentTarget.parentElement?.offsetWidth || 100); + const startWidth = columnWidth || thElement.offsetWidth; // 드래그 중 텍스트 선택 방지 document.body.style.userSelect = 'none'; @@ -1967,12 +1980,23 @@ export const InteractiveDataTable: React.FC = ({ const handleMouseMove = (moveEvent: MouseEvent) => { moveEvent.preventDefault(); + const diff = moveEvent.clientX - startX; const newWidth = Math.max(80, startWidth + diff); - setColumnWidths(prev => ({ ...prev, [column.id]: newWidth })); + + // 직접 DOM 스타일 변경 (리렌더링 없음) + if (thElement) { + thElement.style.width = `${newWidth}px`; + } }; const handleMouseUp = () => { + // 최종 너비를 state에 저장 + if (thElement) { + const finalWidth = Math.max(80, thElement.offsetWidth); + setColumnWidths(prev => ({ ...prev, [column.id]: finalWidth })); + } + // 텍스트 선택 복원 document.body.style.userSelect = ''; document.body.style.cursor = ''; diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index 4fa655e2..6a753734 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -1023,19 +1023,30 @@ export const TableListComponent: React.FC = ({ {visibleColumns.map((column, columnIndex) => { const columnWidth = columnWidths[column.columnName]; - const defaultWidth = `${100 / visibleColumns.length}%`; return (
(columnRefs.current[column.columnName] = el)} + ref={(el) => { + columnRefs.current[column.columnName] = el; + // 첫 렌더링 시 실제 너비를 측정해서 상태에 저장 + if (el && !columnWidth) { + const measuredWidth = el.offsetWidth; + if (measuredWidth > 0) { + setColumnWidths(prev => ({ + ...prev, + [column.columnName]: measuredWidth + })); + } + } + }} className={cn( "relative h-10 px-2 py-2 text-xs font-semibold text-foreground overflow-hidden text-ellipsis bg-background select-none sm:h-12 sm:px-6 sm:py-3 sm:text-sm sm:whitespace-nowrap", column.sortable && "cursor-pointer" )} style={{ textAlign: column.align || "left", - width: columnWidth ? `${columnWidth}px` : defaultWidth, + width: columnWidth ? `${columnWidth}px` : undefined, userSelect: 'none' }} onClick={() => column.sortable && handleSort(column.columnName)}