From 44def0979ca5077d31a26f145e51316982006861 Mon Sep 17 00:00:00 2001 From: kjs Date: Thu, 6 Nov 2025 10:37:20 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=ED=99=94=EB=A9=B4=20=ED=8E=B8=EC=A7=91?= =?UTF-8?q?=EA=B8=B0=20=EB=86=92=EC=9D=B4=20=EC=9E=85=EB=A0=A5=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=201px=20=EB=8B=A8=EC=9C=84=20=EC=A1=B0=EC=A0=88=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 문제: 높이 입력 시 10 단위로만 입력 가능 (예: 1080 입력 불가) - 원인: 격자 스냅 로직이 onChange마다 높이를 10/20 단위로 강제 반올림 - 해결: 1. 모든 number input 필드에 step="1" 추가 2. ScreenDesigner.tsx의 격자 스냅 로직 수정 (높이 스냅 제거) 3. UnifiedPropertiesPanel.tsx에 로컬 상태 추가하여 입력 중 스냅 방지 4. onBlur/Enter 시에만 실제 값 업데이트 수정 파일: - frontend/components/screen/ScreenDesigner.tsx - frontend/components/screen/panels/UnifiedPropertiesPanel.tsx - frontend/components/screen/panels/PropertiesPanel.tsx - frontend/components/screen/panels/ResolutionPanel.tsx - frontend/components/screen/panels/RowSettingsPanel.tsx - frontend/components/screen/panels/webtype-configs/NumberTypeConfigPanel.tsx - frontend/components/screen/panels/webtype-configs/TextTypeConfigPanel.tsx --- .../screen/InteractiveDataTable.tsx | 60 +------------- frontend/components/screen/ScreenDesigner.tsx | 6 +- .../screen/panels/PropertiesPanel.tsx | 26 +++--- .../screen/panels/ResolutionPanel.tsx | 2 + .../screen/panels/RowSettingsPanel.tsx | 3 + .../screen/panels/UnifiedPropertiesPanel.tsx | 35 +++++++- .../webtype-configs/NumberTypeConfigPanel.tsx | 3 + .../webtype-configs/TextTypeConfigPanel.tsx | 2 + .../screen/templates/NumberingRuleTemplate.ts | 1 + .../components/screen/widgets/FlowWidget.tsx | 79 +++++++------------ .../table-category/CategoryValueManager.tsx | 3 +- frontend/lib/api/numberingRule.ts | 1 + .../table-list/TableListComponent.tsx | 49 ------------ 동적_테이블_접근_시스템_개선_완료.md | 1 + 14 files changed, 98 insertions(+), 173 deletions(-) diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index 3523518a..3165bfa8 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -181,7 +181,6 @@ export const InteractiveDataTable: React.FC = ({ // 🆕 전역 테이블 새로고침 이벤트 리스너 useEffect(() => { const handleRefreshTable = () => { - console.log("🔄 InteractiveDataTable: 전역 새로고침 이벤트 수신"); if (component.tableName) { loadData(currentPage, searchValues); } @@ -206,15 +205,6 @@ export const InteractiveDataTable: React.FC = ({ return webType === "category"; }); - console.log(`🔍 InteractiveDataTable 카테고리 컬럼 확인:`, { - tableName: component.tableName, - totalColumns: component.columns?.length, - categoryColumns: categoryColumns?.map(c => ({ - name: c.columnName, - webType: getColumnWebType(c.columnName) - })) - }); - if (!categoryColumns || categoryColumns.length === 0) return; // 각 카테고리 컬럼의 값 목록 조회 @@ -239,12 +229,6 @@ export const InteractiveDataTable: React.FC = ({ } } - console.log(`✅ InteractiveDataTable 카테고리 매핑 완료:`, { - tableName: component.tableName, - mappedColumns: Object.keys(mappings), - mappings - }); - setCategoryMappings(mappings); } catch (error) { console.error("카테고리 매핑 로드 실패:", error); @@ -403,7 +387,6 @@ export const InteractiveDataTable: React.FC = ({ // 대체 URL 생성 (직접 파일 경로 사용) if (previewImage.path) { const altUrl = getDirectFileUrl(previewImage.path); - // console.log("대체 URL 시도:", altUrl); setAlternativeImageUrl(altUrl); } else { toast.error("이미지를 불러올 수 없습니다."); @@ -469,7 +452,6 @@ export const InteractiveDataTable: React.FC = ({ try { return tableColumn?.detailSettings ? JSON.parse(tableColumn.detailSettings) : {}; } catch { - // console.warn("상세 설정 파싱 실패:", tableColumn?.detailSettings); return {}; } }, @@ -672,15 +654,6 @@ export const InteractiveDataTable: React.FC = ({ const handleRefreshFileStatus = async (event: CustomEvent) => { const { tableName, recordId, columnName, targetObjid, fileCount } = event.detail; - // console.log("🔄 InteractiveDataTable 파일 상태 새로고침 이벤트 수신:", { - // tableName, - // recordId, - // columnName, - // targetObjid, - // fileCount, - // currentTableName: component.tableName - // }); - // 현재 테이블과 일치하는지 확인 if (tableName === component.tableName) { // 해당 행의 파일 상태 업데이트 @@ -690,13 +663,6 @@ export const InteractiveDataTable: React.FC = ({ [recordId]: { hasFiles: fileCount > 0, fileCount }, [columnKey]: { hasFiles: fileCount > 0, fileCount }, })); - - // console.log("✅ 파일 상태 업데이트 완료:", { - // recordId, - // columnKey, - // hasFiles: fileCount > 0, - // fileCount - // }); } }; @@ -1104,7 +1070,6 @@ export const InteractiveDataTable: React.FC = ({ setIsAdding(true); // 실제 API 호출로 데이터 추가 - // console.log("🔥 추가할 데이터:", addFormData); await tableTypeApi.addTableData(component.tableName, addFormData); // 모달 닫기 및 폼 초기화 @@ -1127,9 +1092,6 @@ export const InteractiveDataTable: React.FC = ({ setIsEditing(true); // 실제 API 호출로 데이터 수정 - // console.log("🔥 수정할 데이터:", editFormData); - // console.log("🔥 원본 데이터:", editingRowData); - if (editingRowData) { await tableTypeApi.editTableData(component.tableName, editingRowData, editFormData); @@ -1200,7 +1162,6 @@ export const InteractiveDataTable: React.FC = ({ const selectedData = Array.from(selectedRows).map((index) => data[index]); // 실제 삭제 API 호출 - // console.log("🗑️ 삭제할 데이터:", selectedData); await tableTypeApi.deleteTableData(component.tableName, selectedData); // 선택 해제 및 다이얼로그 닫기 @@ -1488,12 +1449,6 @@ export const InteractiveDataTable: React.FC = ({ case "category": { // 카테고리 셀렉트 (동적 import) const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent"); - console.log("🎯 카테고리 렌더링 (편집 폼):", { - tableName: component.tableName, - columnName: column.columnName, - columnLabel: column.label, - value, - }); return (
= ({ case "category": { // 카테고리 셀렉트 (동적 import) const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent"); - console.log("🎯 카테고리 렌더링 (추가 폼):", { - tableName: component.tableName, - columnName: column.columnName, - columnLabel: column.label, - value, - }); return (
= ({ const handleDeleteLinkedFile = useCallback( async (fileId: string, fileName: string) => { try { - // console.log("🗑️ 파일 삭제 시작:", { fileId, fileName }); - // 삭제 확인 다이얼로그 if (!confirm(`"${fileName}" 파일을 삭제하시겠습니까?`)) { return; @@ -1884,7 +1831,6 @@ export const InteractiveDataTable: React.FC = ({ }); const result = response.data; - // console.log("📡 파일 삭제 API 응답:", result); if (!result.success) { throw new Error(result.message || "파일 삭제 실패"); @@ -1901,15 +1847,11 @@ export const InteractiveDataTable: React.FC = ({ try { const response = await getLinkedFiles(component.tableName, recordId); setLinkedFiles(response.files || []); - // console.log("📁 파일 목록 새로고침 완료:", response.files?.length || 0); } catch (error) { - // console.error("파일 목록 새로고침 실패:", error); + // 파일 목록 새로고침 실패 시 무시 } } - - // console.log("✅ 파일 삭제 완료:", fileName); } catch (error) { - // console.error("❌ 파일 삭제 실패:", error); toast.error(`"${fileName}" 파일 삭제에 실패했습니다.`); } }, diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 561d4e9d..a7c3c2cf 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -619,7 +619,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const fullColumnWidth = columnWidth + (gap || 16); // 외부 격자와 동일한 크기 const widthInColumns = Math.max(1, Math.round(newComp.size.width / fullColumnWidth)); const snappedWidth = widthInColumns * fullColumnWidth - (gap || 16); // gap 제거하여 실제 컴포넌트 크기 - const snappedHeight = Math.max(10, Math.round(newComp.size.height / 10) * 10); + // 높이는 사용자가 입력한 값 그대로 사용 (스냅 제거) + const snappedHeight = Math.max(10, newComp.size.height); newComp.position = { x: Math.max(padding, snappedX), // 패딩만큼 최소 여백 확보 @@ -3028,7 +3029,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const fullColumnWidth = columnWidth + (gap || 16); // 외부 격자와 동일한 크기 const widthInColumns = Math.max(1, Math.round(comp.size.width / fullColumnWidth)); const snappedWidth = widthInColumns * fullColumnWidth - (gap || 16); // gap 제거하여 실제 컴포넌트 크기 - const snappedHeight = Math.max(40, Math.round(comp.size.height / 20) * 20); + // 높이는 사용자가 입력한 값 그대로 사용 (스냅 제거) + const snappedHeight = Math.max(40, comp.size.height); newPosition = { x: Math.max(padding, snappedX), // 패딩만큼 최소 여백 확보 diff --git a/frontend/components/screen/panels/PropertiesPanel.tsx b/frontend/components/screen/panels/PropertiesPanel.tsx index 277c6e9c..3d429123 100644 --- a/frontend/components/screen/panels/PropertiesPanel.tsx +++ b/frontend/components/screen/panels/PropertiesPanel.tsx @@ -695,6 +695,7 @@ const PropertiesPanelComponent: React.FC = ({ { const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; if (isDragging) { @@ -725,6 +726,7 @@ const PropertiesPanelComponent: React.FC = ({ { const isDragging = dragState?.isDragging && dragState.draggedComponent?.id === selectedComponent?.id; if (isDragging) { @@ -762,6 +764,7 @@ const PropertiesPanelComponent: React.FC = ({ type="number" min={1} max={gridSettings?.columns || 12} + step="1" value={(selectedComponent as any)?.gridColumns || 1} onChange={(e) => { const value = parseInt(e.target.value, 10); @@ -961,27 +964,27 @@ const PropertiesPanelComponent: React.FC = ({
{ - const units = Math.max(1, Math.min(100, Number(e.target.value))); - const newHeight = units * 10; + const newHeight = Math.max(10, Number(e.target.value)); setLocalInputs((prev) => ({ ...prev, height: newHeight.toString() })); onUpdateProperty("size.height", newHeight); }} className="flex-1" /> - 단위 = {localInputs.height || 10}px + {localInputs.height || 40}px

- 1단위 = 10px (현재 {Math.round((localInputs.height || 10) / 10)}단위) - 내부 콘텐츠에 맞춰 늘어남 + 높이 자유 조절 (10px ~ 2000px, 1px 단위)

@@ -996,11 +999,12 @@ const PropertiesPanelComponent: React.FC = ({ - { const newValue = e.target.value; @@ -1266,6 +1270,7 @@ const PropertiesPanelComponent: React.FC = ({ type="number" min="1" max="12" + step="1" value={(selectedComponent as AreaComponent).layoutConfig?.gridColumns || 3} onChange={(e) => { const value = Number(e.target.value); @@ -1279,6 +1284,7 @@ const PropertiesPanelComponent: React.FC = ({ { const value = Number(e.target.value); @@ -1315,6 +1321,7 @@ const PropertiesPanelComponent: React.FC = ({ { const value = Number(e.target.value); @@ -1345,6 +1352,7 @@ const PropertiesPanelComponent: React.FC = ({ { const value = Number(e.target.value); diff --git a/frontend/components/screen/panels/ResolutionPanel.tsx b/frontend/components/screen/panels/ResolutionPanel.tsx index 90680f01..3fac225d 100644 --- a/frontend/components/screen/panels/ResolutionPanel.tsx +++ b/frontend/components/screen/panels/ResolutionPanel.tsx @@ -146,6 +146,7 @@ const ResolutionPanel: React.FC = ({ currentResolution, on onChange={(e) => setCustomWidth(e.target.value)} placeholder="1920" min="1" + step="1" className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }} style={{ fontSize: "12px" }} /> @@ -158,6 +159,7 @@ const ResolutionPanel: React.FC = ({ currentResolution, on onChange={(e) => setCustomHeight(e.target.value)} placeholder="1080" min="1" + step="1" className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }} style={{ fontSize: "12px" }} /> diff --git a/frontend/components/screen/panels/RowSettingsPanel.tsx b/frontend/components/screen/panels/RowSettingsPanel.tsx index 2bd48a12..1378ffe3 100644 --- a/frontend/components/screen/panels/RowSettingsPanel.tsx +++ b/frontend/components/screen/panels/RowSettingsPanel.tsx @@ -57,6 +57,7 @@ export const RowSettingsPanel: React.FC = ({ row, onUpdat placeholder="100" min={50} max={1000} + step="1" />
)} @@ -73,6 +74,7 @@ export const RowSettingsPanel: React.FC = ({ row, onUpdat placeholder="50" min={0} max={1000} + step="1" />
)} @@ -89,6 +91,7 @@ export const RowSettingsPanel: React.FC = ({ row, onUpdat placeholder="500" min={0} max={2000} + step="1" /> )} diff --git a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx index cc06b555..8d7cd091 100644 --- a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx +++ b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx @@ -104,6 +104,9 @@ export const UnifiedPropertiesPanel: React.FC = ({ }) => { const { webTypes } = useWebTypes({ active: "Y" }); const [localComponentDetailType, setLocalComponentDetailType] = useState(""); + + // 높이 입력 로컬 상태 (격자 스냅 방지) + const [localHeight, setLocalHeight] = useState(""); // 새로운 컴포넌트 시스템의 webType 동기화 useEffect(() => { @@ -114,6 +117,13 @@ export const UnifiedPropertiesPanel: React.FC = ({ } } }, [selectedComponent?.type, selectedComponent?.componentConfig?.webType, selectedComponent?.id]); + + // 높이 값 동기화 + useEffect(() => { + if (selectedComponent?.size?.height !== undefined) { + setLocalHeight(String(selectedComponent.size.height)); + } + }, [selectedComponent?.size?.height, selectedComponent?.id]); // 격자 설정 업데이트 함수 (early return 이전에 정의) const updateGridSetting = (key: string, value: any) => { @@ -180,6 +190,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ id="columns" type="number" min={1} + step="1" value={gridSettings.columns} onChange={(e) => { const value = parseInt(e.target.value, 10); @@ -361,11 +372,27 @@ export const UnifiedPropertiesPanel: React.FC = ({ { + // 입력 중에는 로컬 상태만 업데이트 (격자 스냅 방지) + setLocalHeight(e.target.value); + }} + onBlur={(e) => { + // 포커스를 잃을 때만 실제로 업데이트 const value = parseInt(e.target.value) || 0; - // 최소값 제한 없이, 1px 단위로 조절 가능 - handleUpdate("size.height", Math.max(1, value)); + if (value >= 1) { + handleUpdate("size.height", value); + } + }} + onKeyDown={(e) => { + // Enter 키를 누르면 즉시 적용 + if (e.key === "Enter") { + const value = parseInt(e.currentTarget.value) || 0; + if (value >= 1) { + handleUpdate("size.height", value); + } + e.currentTarget.blur(); // 포커스 제거 + } }} step={1} placeholder="10" @@ -430,6 +457,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ type="number" min={1} max={gridSettings?.columns || 12} + step="1" value={(selectedComponent as any).gridColumns || 1} onChange={(e) => { const value = parseInt(e.target.value, 10); @@ -455,6 +483,7 @@ export const UnifiedPropertiesPanel: React.FC = ({ handleUpdate("position.z", parseInt(e.target.value) || 1)} className="h-6 w-full px-2 py-0 text-xs" diff --git a/frontend/components/screen/panels/webtype-configs/NumberTypeConfigPanel.tsx b/frontend/components/screen/panels/webtype-configs/NumberTypeConfigPanel.tsx index aaa66688..924feeee 100644 --- a/frontend/components/screen/panels/webtype-configs/NumberTypeConfigPanel.tsx +++ b/frontend/components/screen/panels/webtype-configs/NumberTypeConfigPanel.tsx @@ -132,6 +132,7 @@ export const NumberTypeConfigPanel: React.FC = ({ co updateConfig("min", e.target.value ? Number(e.target.value) : undefined)} className="mt-1" @@ -146,6 +147,7 @@ export const NumberTypeConfigPanel: React.FC = ({ co updateConfig("max", e.target.value ? Number(e.target.value) : undefined)} className="mt-1" @@ -181,6 +183,7 @@ export const NumberTypeConfigPanel: React.FC = ({ co type="number" min="0" max="10" + step="1" value={localValues.decimalPlaces} onChange={(e) => updateConfig("decimalPlaces", e.target.value ? Number(e.target.value) : undefined)} className="mt-1" diff --git a/frontend/components/screen/panels/webtype-configs/TextTypeConfigPanel.tsx b/frontend/components/screen/panels/webtype-configs/TextTypeConfigPanel.tsx index cb46ec50..abb35347 100644 --- a/frontend/components/screen/panels/webtype-configs/TextTypeConfigPanel.tsx +++ b/frontend/components/screen/panels/webtype-configs/TextTypeConfigPanel.tsx @@ -168,6 +168,7 @@ export const TextTypeConfigPanel: React.FC = ({ config id="minLength" type="number" min="0" + step="1" value={localValues.minLength} onChange={(e) => updateConfig("minLength", e.target.value ? Number(e.target.value) : undefined)} className="mt-1" @@ -183,6 +184,7 @@ export const TextTypeConfigPanel: React.FC = ({ config id="maxLength" type="number" min="0" + step="1" value={localValues.maxLength} onChange={(e) => updateConfig("maxLength", e.target.value ? Number(e.target.value) : undefined)} className="mt-1" diff --git a/frontend/components/screen/templates/NumberingRuleTemplate.ts b/frontend/components/screen/templates/NumberingRuleTemplate.ts index ee386c4b..fbe15d8d 100644 --- a/frontend/components/screen/templates/NumberingRuleTemplate.ts +++ b/frontend/components/screen/templates/NumberingRuleTemplate.ts @@ -75,3 +75,4 @@ export const numberingRuleTemplate = { ], }; + diff --git a/frontend/components/screen/widgets/FlowWidget.tsx b/frontend/components/screen/widgets/FlowWidget.tsx index 2e174554..e5a2e541 100644 --- a/frontend/components/screen/widgets/FlowWidget.tsx +++ b/frontend/components/screen/widgets/FlowWidget.tsx @@ -66,6 +66,35 @@ export function FlowWidget({ const { isPreviewMode } = useScreenPreview(); // 프리뷰 모드 확인 const { user } = useAuth(); // 사용자 정보 가져오기 + // 🆕 전역 상태 관리 + const setSelectedStep = useFlowStepStore((state) => state.setSelectedStep); + const resetFlow = useFlowStepStore((state) => state.resetFlow); + + const [flowData, setFlowData] = useState(null); + const [steps, setSteps] = useState([]); + const [stepCounts, setStepCounts] = useState>({}); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [connections, setConnections] = useState([]); // 플로우 연결 정보 + + // 선택된 스텝의 데이터 리스트 상태 + const [selectedStepId, setSelectedStepId] = useState(null); + const [stepData, setStepData] = useState([]); + const [stepDataColumns, setStepDataColumns] = useState([]); + const [stepDataLoading, setStepDataLoading] = useState(false); + const [selectedRows, setSelectedRows] = useState>(new Set()); + const [columnLabels, setColumnLabels] = useState>({}); // 컬럼명 -> 라벨 매핑 + + // 🆕 검색 필터 관련 상태 + const [searchFilterColumns, setSearchFilterColumns] = useState>(new Set()); // 검색 필터로 사용할 컬럼 + const [isFilterSettingOpen, setIsFilterSettingOpen] = useState(false); // 필터 설정 다이얼로그 + const [searchValues, setSearchValues] = useState>({}); // 검색 값 + const [allAvailableColumns, setAllAvailableColumns] = useState([]); // 전체 컬럼 목록 + const [filteredData, setFilteredData] = useState([]); // 필터링된 데이터 + + // 카테고리 값 매핑 캐시 (컬럼명 -> {코드 -> 라벨}) + const [categoryMappings, setCategoryMappings] = useState>>({}); + // 값 포맷팅 함수 (숫자, 카테고리 등) const formatValue = useCallback((value: any, columnName?: string): string => { if (value === null || value === undefined || value === "") { @@ -97,35 +126,6 @@ export function FlowWidget({ return String(value); }, [categoryMappings]); - // 🆕 전역 상태 관리 - const setSelectedStep = useFlowStepStore((state) => state.setSelectedStep); - const resetFlow = useFlowStepStore((state) => state.resetFlow); - - const [flowData, setFlowData] = useState(null); - const [steps, setSteps] = useState([]); - const [stepCounts, setStepCounts] = useState>({}); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [connections, setConnections] = useState([]); // 플로우 연결 정보 - - // 선택된 스텝의 데이터 리스트 상태 - const [selectedStepId, setSelectedStepId] = useState(null); - const [stepData, setStepData] = useState([]); - const [stepDataColumns, setStepDataColumns] = useState([]); - const [stepDataLoading, setStepDataLoading] = useState(false); - const [selectedRows, setSelectedRows] = useState>(new Set()); - const [columnLabels, setColumnLabels] = useState>({}); // 컬럼명 -> 라벨 매핑 - - // 🆕 검색 필터 관련 상태 - const [searchFilterColumns, setSearchFilterColumns] = useState>(new Set()); // 검색 필터로 사용할 컬럼 - const [isFilterSettingOpen, setIsFilterSettingOpen] = useState(false); // 필터 설정 다이얼로그 - const [searchValues, setSearchValues] = useState>({}); // 검색 값 - const [allAvailableColumns, setAllAvailableColumns] = useState([]); // 전체 컬럼 목록 - const [filteredData, setFilteredData] = useState([]); // 필터링된 데이터 - - // 카테고리 값 매핑 캐시 (컬럼명 -> {코드 -> 라벨}) - const [categoryMappings, setCategoryMappings] = useState>>({}); - // 🆕 그룹 설정 관련 상태 const [isGroupSettingOpen, setIsGroupSettingOpen] = useState(false); // 그룹 설정 다이얼로그 const [groupByColumns, setGroupByColumns] = useState([]); // 그룹화할 컬럼 목록 @@ -382,12 +382,6 @@ export function FlowWidget({ }); setFilteredData(filtered); - console.log("🔍 검색 실행:", { - totalRows: stepData.length, - filteredRows: filtered.length, - searchValues, - hasSearchValue, - }); }, [searchValues, stepData]); // stepData와 searchValues가 변경될 때마다 실행 // 선택된 스텝의 데이터를 다시 로드하는 함수 @@ -471,7 +465,6 @@ export function FlowWidget({ // 프리뷰 모드에서는 샘플 데이터만 표시 if (isPreviewMode) { - console.log("🔒 프리뷰 모드: 플로우 데이터 로드 차단 - 샘플 데이터 표시"); setFlowData({ id: flowId || 0, flowName: flowName || "샘플 플로우", @@ -648,16 +641,9 @@ export function FlowWidget({ try { // 컬럼 라벨 조회 const labelsResponse = await getStepColumnLabels(flowId!, stepId); - console.log("🏷️ 컬럼 라벨 조회 결과:", { - stepId, - success: labelsResponse.success, - labelsCount: labelsResponse.data ? Object.keys(labelsResponse.data).length : 0, - labels: labelsResponse.data, - }); if (labelsResponse.success && labelsResponse.data) { setColumnLabels(labelsResponse.data); } else { - console.warn("⚠️ 컬럼 라벨 조회 실패 또는 데이터 없음:", labelsResponse); setColumnLabels({}); } @@ -761,13 +747,6 @@ export function FlowWidget({ // 선택된 데이터를 상위로 전달 const selectedData = Array.from(newSelected).map((index) => stepData[index]); - console.log("🌊 FlowWidget - 체크박스 토글, 상위로 전달:", { - rowIndex, - newSelectedSize: newSelected.size, - selectedData, - selectedStepId, - hasCallback: !!onSelectedDataChange, - }); onSelectedDataChange?.(selectedData, selectedStepId); }; diff --git a/frontend/components/table-category/CategoryValueManager.tsx b/frontend/components/table-category/CategoryValueManager.tsx index ceadf8b9..b03b809d 100644 --- a/frontend/components/table-category/CategoryValueManager.tsx +++ b/frontend/components/table-category/CategoryValueManager.tsx @@ -72,7 +72,8 @@ export const CategoryValueManager: React.FC = ({ const loadCategoryValues = async () => { setIsLoading(true); try { - const response = await getCategoryValues(tableName, columnName); + // includeInactive: true로 비활성 값도 포함 + const response = await getCategoryValues(tableName, columnName, true); if (response.success && response.data) { setValues(response.data); setFilteredValues(response.data); diff --git a/frontend/lib/api/numberingRule.ts b/frontend/lib/api/numberingRule.ts index 294fa353..1f655790 100644 --- a/frontend/lib/api/numberingRule.ts +++ b/frontend/lib/api/numberingRule.ts @@ -133,3 +133,4 @@ export async function resetSequence(ruleId: string): Promise> } } + diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index ba827020..338ef0f6 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -186,9 +186,7 @@ export const TableListComponent: React.FC = ({ // 객체인 경우 tableName 속성 추출 시도 if (typeof finalSelectedTable === "object" && finalSelectedTable !== null) { - console.warn("⚠️ selectedTable이 객체입니다:", finalSelectedTable); finalSelectedTable = (finalSelectedTable as any).tableName || (finalSelectedTable as any).name || tableName; - console.log("✅ 객체에서 추출한 테이블명:", finalSelectedTable); } tableConfig.selectedTable = finalSelectedTable; @@ -282,13 +280,10 @@ export const TableListComponent: React.FC = ({ if (savedOrder) { try { const parsedOrder = JSON.parse(savedOrder); - console.log("📂 localStorage에서 컬럼 순서 불러오기:", { storageKey, columnOrder: parsedOrder }); setColumnOrder(parsedOrder); // 부모 컴포넌트에 초기 컬럼 순서 전달 if (onSelectedRowsChange && parsedOrder.length > 0) { - console.log("✅ 초기 컬럼 순서 전달:", parsedOrder); - // 초기 데이터도 함께 전달 (컬럼 순서대로 재정렬) const initialData = data.map((row: any) => { const reordered: any = {}; @@ -306,8 +301,6 @@ export const TableListComponent: React.FC = ({ return reordered; }); - console.log("📊 초기 화면 표시 데이터 전달:", { count: initialData.length, firstRow: initialData[0] }); - // 전역 저장소에 데이터 저장 if (tableConfig.selectedTable) { tableDisplayStore.setTableData( @@ -584,8 +577,6 @@ export const TableListComponent: React.FC = ({ }; const handleSort = (column: string) => { - console.log("🔄 정렬 클릭:", { column, currentSortColumn: sortColumn, currentSortDirection: sortDirection }); - let newSortColumn = column; let newSortDirection: "asc" | "desc" = "asc"; @@ -599,9 +590,6 @@ export const TableListComponent: React.FC = ({ newSortDirection = "asc"; } - console.log("📊 새로운 정렬 정보:", { newSortColumn, newSortDirection }); - console.log("🔍 onSelectedRowsChange 존재 여부:", !!onSelectedRowsChange); - // 정렬 변경 시 선택 정보와 함께 정렬 정보도 전달 if (onSelectedRowsChange) { const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index))); @@ -651,16 +639,6 @@ export const TableListComponent: React.FC = ({ return reordered; }); - console.log("✅ 정렬 정보 전달:", { - selectedRowsCount: selectedRows.size, - selectedRowsDataCount: selectedRowsData.length, - sortBy: newSortColumn, - sortOrder: newSortDirection, - columnOrder: columnOrder.length > 0 ? columnOrder : undefined, - tableDisplayDataCount: reorderedData.length, - firstRowAfterSort: reorderedData[0]?.[newSortColumn], - lastRowAfterSort: reorderedData[reorderedData.length - 1]?.[newSortColumn] - }); onSelectedRowsChange( Array.from(selectedRows), selectedRowsData, @@ -681,8 +659,6 @@ export const TableListComponent: React.FC = ({ newSortDirection ); } - } else { - console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!"); } }; @@ -774,8 +750,6 @@ export const TableListComponent: React.FC = ({ const isCurrentlySelected = selectedRows.has(rowKey); handleRowSelection(rowKey, !isCurrentlySelected); - - console.log("행 클릭:", { row, index, isSelected: !isCurrentlySelected }); }; // 컬럼 드래그앤드롭 기능 제거됨 (테이블 옵션 모달에서 컬럼 순서 변경 가능) @@ -820,12 +794,6 @@ export const TableListComponent: React.FC = ({ // columnOrder에 없는 새로운 컬럼들 추가 const remainingCols = cols.filter(c => !columnOrder.includes(c.columnName)); - console.log("🔄 columnOrder 기반 정렬:", { - columnOrder, - orderedColsCount: orderedCols.length, - remainingColsCount: remainingCols.length - }); - return [...orderedCols, ...remainingCols]; } @@ -836,19 +804,11 @@ export const TableListComponent: React.FC = ({ const lastColumnOrderRef = useRef(""); useEffect(() => { - console.log("🔍 [컬럼 순서 전달 useEffect] 실행됨:", { - hasCallback: !!onSelectedRowsChange, - visibleColumnsLength: visibleColumns.length, - visibleColumnsNames: visibleColumns.map(c => c.columnName), - }); - if (!onSelectedRowsChange) { - console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!"); return; } if (visibleColumns.length === 0) { - console.warn("⚠️ visibleColumns가 비어있습니다!"); return; } @@ -856,23 +816,14 @@ export const TableListComponent: React.FC = ({ .map(col => col.columnName) .filter(name => name !== "__checkbox__"); // 체크박스 컬럼 제외 - console.log("🔍 [컬럼 순서] 체크박스 제외 후:", currentColumnOrder); - // 컬럼 순서가 실제로 변경되었을 때만 전달 (무한 루프 방지) const columnOrderString = currentColumnOrder.join(","); - console.log("🔍 [컬럼 순서] 비교:", { - current: columnOrderString, - last: lastColumnOrderRef.current, - isDifferent: columnOrderString !== lastColumnOrderRef.current, - }); if (columnOrderString === lastColumnOrderRef.current) { - console.log("⏭️ 컬럼 순서 변경 없음, 전달 스킵"); return; } lastColumnOrderRef.current = columnOrderString; - console.log("📊 현재 화면 컬럼 순서 전달:", currentColumnOrder); // 선택된 행 데이터 가져오기 const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index))); diff --git a/동적_테이블_접근_시스템_개선_완료.md b/동적_테이블_접근_시스템_개선_완료.md index 82e77416..ea214f5d 100644 --- a/동적_테이블_접근_시스템_개선_완료.md +++ b/동적_테이블_접근_시스템_개선_완료.md @@ -375,3 +375,4 @@ interface TablePermission { **이제 테이블을 만들 때마다 코드를 수정할 필요가 없습니다!** +