"use client"; /** * 노드 기반 플로우 에디터 메인 컴포넌트 */ import { useCallback, useRef } from "react"; import ReactFlow, { Background, Controls, MiniMap, Panel, ReactFlowProvider, useReactFlow } from "reactflow"; import "reactflow/dist/style.css"; import { useFlowEditorStore } from "@/lib/stores/flowEditorStore"; import { NodePalette } from "./sidebar/NodePalette"; import { PropertiesPanel } from "./panels/PropertiesPanel"; import { FlowToolbar } from "./FlowToolbar"; import { TableSourceNode } from "./nodes/TableSourceNode"; import { ExternalDBSourceNode } from "./nodes/ExternalDBSourceNode"; import { ConditionNode } from "./nodes/ConditionNode"; import { FieldMappingNode } from "./nodes/FieldMappingNode"; import { InsertActionNode } from "./nodes/InsertActionNode"; import { UpdateActionNode } from "./nodes/UpdateActionNode"; import { DeleteActionNode } from "./nodes/DeleteActionNode"; import { UpsertActionNode } from "./nodes/UpsertActionNode"; import { DataTransformNode } from "./nodes/DataTransformNode"; import { RestAPISourceNode } from "./nodes/RestAPISourceNode"; import { CommentNode } from "./nodes/CommentNode"; import { LogNode } from "./nodes/LogNode"; // 노드 타입들 const nodeTypes = { // 데이터 소스 tableSource: TableSourceNode, externalDBSource: ExternalDBSourceNode, restAPISource: RestAPISourceNode, // 변환/조건 condition: ConditionNode, fieldMapping: FieldMappingNode, dataTransform: DataTransformNode, // 액션 insertAction: InsertActionNode, updateAction: UpdateActionNode, deleteAction: DeleteActionNode, upsertAction: UpsertActionNode, // 유틸리티 comment: CommentNode, log: LogNode, }; /** * FlowEditor 내부 컴포넌트 */ function FlowEditorInner() { const reactFlowWrapper = useRef(null); const { screenToFlowPosition } = useReactFlow(); const { nodes, edges, onNodesChange, onEdgesChange, onConnect, addNode, showPropertiesPanel, selectNodes, selectedNodes, removeNodes, } = useFlowEditorStore(); /** * 노드 선택 변경 핸들러 */ const onSelectionChange = useCallback( ({ nodes: selectedNodes }: { nodes: any[] }) => { const selectedIds = selectedNodes.map((node) => node.id); selectNodes(selectedIds); console.log("🔍 선택된 노드:", selectedIds); }, [selectNodes], ); /** * 키보드 이벤트 핸들러 (Delete/Backspace 키로 노드 삭제) */ const onKeyDown = useCallback( (event: React.KeyboardEvent) => { if ((event.key === "Delete" || event.key === "Backspace") && selectedNodes.length > 0) { event.preventDefault(); console.log("🗑️ 선택된 노드 삭제:", selectedNodes); removeNodes(selectedNodes); } }, [selectedNodes, removeNodes], ); /** * 드래그 앤 드롭 핸들러 */ const onDragOver = useCallback((event: React.DragEvent) => { event.preventDefault(); event.dataTransfer.dropEffect = "move"; }, []); const onDrop = useCallback( (event: React.DragEvent) => { event.preventDefault(); const type = event.dataTransfer.getData("application/reactflow"); if (!type) return; const position = screenToFlowPosition({ x: event.clientX, y: event.clientY, }); const newNode: any = { id: `node_${Date.now()}`, type, position, data: { displayName: `새 ${type} 노드`, }, }; addNode(newNode); }, [screenToFlowPosition, addNode], ); return (
{/* 좌측 노드 팔레트 */}
{/* 중앙 캔버스 */}
{/* 배경 그리드 */} {/* 컨트롤 버튼 */} {/* 미니맵 */} { // 노드 타입별 색상 (추후 구현) return "#3B82F6"; }} maskColor="rgba(0, 0, 0, 0.1)" /> {/* 상단 툴바 */}
{/* 우측 속성 패널 */} {showPropertiesPanel && (
)}
); } /** * FlowEditor 메인 컴포넌트 (Provider로 감싸기) */ export function FlowEditor() { return (
); }