From 37fac630b9e90baeaa2af32c4ea0acbfef8db5b1 Mon Sep 17 00:00:00 2001 From: hyeonsu Date: Mon, 8 Sep 2025 10:33:00 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B8=B0=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/화면간_데이터_관계_설정_시스템_설계.md | 9 ++- frontend/app/(main)/admin/dataflow/page.tsx | 5 +- .../components/dataflow/DataFlowDesigner.tsx | 28 +++----- frontend/components/dataflow/TableNode.tsx | 67 ++----------------- 4 files changed, 25 insertions(+), 84 deletions(-) diff --git a/docs/화면간_데이터_관계_설정_시스템_설계.md b/docs/화면간_데이터_관계_설정_시스템_설계.md index f28845d4..2688af38 100644 --- a/docs/화면간_데이터_관계_설정_시스템_설계.md +++ b/docs/화면간_데이터_관계_설정_시스템_설계.md @@ -623,13 +623,16 @@ PUT /api/external-call-configs/:id - [x] 노드 간 드래그앤드롭 연결 기능 - [x] 줌, 팬, 미니맵 등 React Flow 기본 기능 -### Phase 2: 관계 설정 기능 (2주) - 🚧 **진행 중 (70% 완료)** +### Phase 2: 관계 설정 기능 (2주) - 🚧 **진행 중 (85% 완료)** - [x] 연결 설정 모달 UI 구현 - [x] 1:1, 1:N, N:1, N:N 관계 타입 선택 UI - [x] 단순 키값, 데이터 저장, 외부 호출 연결 종류 UI - [x] 컬럼-to-컬럼 연결 시스템 (클릭 기반) - [x] 선택된 컬럼 정보 표시 및 순서 보장 +- [x] 드래그 다중 선택 기능 (부분 터치 선택 지원) +- [x] 테이블 기반 시스템으로 전환 (화면 → 테이블) +- [x] 코드 정리 및 최적화 (불필요한 props 제거) - [ ] 연결 생성 로직 구현 (모달에서 실제 엣지 생성) - [ ] 생성된 연결의 시각적 표시 (React Flow 엣지) - [ ] 연결 데이터 백엔드 저장 API 연동 @@ -698,13 +701,15 @@ PUT /api/external-call-configs/:id **주요 개선사항:** 1. **스크롤 충돌 해결**: 노드 내부 스크롤과 React Flow 줌/팬 기능 분리 -2. **노드 리사이징**: NodeResizer를 통한 노드 크기 조정 및 내용 반영 +2. **테이블 기반 시스템**: 화면 기반에서 테이블 기반으로 완전 전환 3. **컬럼-to-컬럼 연결**: 드래그앤드롭 대신 클릭 기반 컬럼 선택 방식 4. **2개 테이블 제한**: 최대 2개 테이블에서만 컬럼 선택 가능 5. **선택 순서 보장**: 사이드바와 모달에서 컬럼 선택 순서 정확히 반영 6. **실제 데이터 연동**: 테이블 관리 시스템의 실제 테이블/컬럼 데이터 사용 7. **사용자 경험**: react-hot-toast를 통한 친화적인 알림 시스템 8. **React 안정성**: 렌더링 중 상태 변경 문제 해결 +9. **드래그 다중 선택**: 부분 터치로도 노드 선택 가능한 고급 선택 기능 +10. **코드 최적화**: 불필요한 props 및 컴포넌트 제거로 성능 향상 **다음 단계:** Phase 2 - 실제 연결 생성 및 시각적 표시 기능 구현 diff --git a/frontend/app/(main)/admin/dataflow/page.tsx b/frontend/app/(main)/admin/dataflow/page.tsx index d7a7e316..69111e56 100644 --- a/frontend/app/(main)/admin/dataflow/page.tsx +++ b/frontend/app/(main)/admin/dataflow/page.tsx @@ -4,18 +4,19 @@ import React from "react"; import { Toaster } from "react-hot-toast"; import { DataFlowDesigner } from "@/components/dataflow/DataFlowDesigner"; import { useAuth } from "@/hooks/useAuth"; +import { TableRelationship } from "@/lib/api/dataflow"; export default function DataFlowPage() { const { user } = useAuth(); - const handleSave = (relationships: any[]) => { + const handleSave = (relationships: TableRelationship[]) => { console.log("저장된 관계:", relationships); // TODO: API 호출로 관계 저장 }; return (
- +
); diff --git a/frontend/components/dataflow/DataFlowDesigner.tsx b/frontend/components/dataflow/DataFlowDesigner.tsx index d210ff8f..72897720 100644 --- a/frontend/components/dataflow/DataFlowDesigner.tsx +++ b/frontend/components/dataflow/DataFlowDesigner.tsx @@ -8,10 +8,10 @@ import { Edge, Controls, Background, - MiniMap, useNodesState, useEdgesState, BackgroundVariant, + SelectionMode, } from "@xyflow/react"; import "@xyflow/react/dist/style.css"; import { TableNode } from "./TableNode"; @@ -435,10 +435,9 @@ export const DataFlowDesigner: React.FC = ({ companyCode,
-
+
{displayName}
-
{tableName}
{columns.map((column, columnIndex) => ( @@ -466,9 +465,9 @@ export const DataFlowDesigner: React.FC = ({ companyCode, @@ -506,19 +505,14 @@ export const DataFlowDesigner: React.FC = ({ companyCode, panOnScroll={false} zoomOnScroll={true} zoomOnPinch={true} - panOnDrag={true} + panOnDrag={[1, 2]} + selectionOnDrag={true} + multiSelectionKeyCode={null} + selectNodesOnDrag={false} + selectionMode={SelectionMode.Partial} > - { - switch (node.type) { - case "tableNode": - return "#3B82F6"; - default: - return "#6B7280"; - } - }} - /> + diff --git a/frontend/components/dataflow/TableNode.tsx b/frontend/components/dataflow/TableNode.tsx index aa0077ef..0fee5869 100644 --- a/frontend/components/dataflow/TableNode.tsx +++ b/frontend/components/dataflow/TableNode.tsx @@ -1,7 +1,6 @@ "use client"; import React from "react"; -import { Handle, Position, NodeResizer } from "@xyflow/react"; interface TableColumn { name: string; @@ -21,66 +20,22 @@ interface TableNodeData { onColumnClick: (tableName: string, columnName: string) => void; onScrollAreaEnter?: () => void; onScrollAreaLeave?: () => void; - selected?: boolean; selectedColumns?: string[]; // 선택된 컬럼 목록 } -export const TableNode: React.FC<{ data: TableNodeData; selected?: boolean }> = ({ data, selected }) => { +export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => { const { table, onColumnClick, onScrollAreaEnter, onScrollAreaLeave, selectedColumns = [] } = data; - // 컬럼 개수에 따른 높이 계산 - // 헤더: ~80px (제목 + 설명 + 패딩) - const headerHeight = table.description ? 80 : 65; - - // 컬럼 높이: 각 컬럼은 실제로 더 높음 (px-2 py-1 + 텍스트 2줄 + 설명 + space-y-1) - // 설명이 있는 컬럼: ~45px, 없는 컬럼: ~35px, 간격: ~4px - const avgColumnHeight = 45; // 여유있게 계산 - const idealColumnHeight = table.columns.length * avgColumnHeight; - - // 컨테이너 패딩 - const padding = 20; - - // 이상적인 높이 vs 최대 허용 높이 (너무 길면 스크롤) - const idealHeight = headerHeight + idealColumnHeight + padding; - const maxAllowedHeight = 800; // 최대 800px - const calculatedHeight = Math.max(200, Math.min(idealHeight, maxAllowedHeight)); - - // 스크롤이 필요한지 판단 - const needsScroll = idealHeight > maxAllowedHeight; - return ( -
- {/* NodeResizer for resizing functionality */} - - +
{/* 테이블 헤더 */} -
+

{table.displayName}

-

{table.tableName}

{table.description &&

{table.description}

}
{/* 컬럼 목록 */} -
+
{table.columns.map((column) => { const isSelected = selectedColumns.includes(column.name); @@ -103,20 +58,6 @@ export const TableNode: React.FC<{ data: TableNodeData; selected?: boolean }> = })}
- - {/* React Flow Handles */} - -
); };