React Flow로 인터랙티브 다이어그램 만들기
강력한 React 기반 플로우차트 라이브러리 React Flow의 핵심 기능과 실제 구현 방법을 알아보자.
들어가며
현대 웹 애플리케이션에서 복잡한 데이터 흐름이나 워크플로우를 시각적으로 표현해야 하는 경우가 많아졌다. 특히 관리 도구, 데이터 분석 플랫폼, 또는 프로세스 관리 시스템에서는 노드와 엣지로 구성된 인터랙티브한 다이어그램이 필수적이다. React Flow는 이러한 요구사항을 해결하기 위한 강력하고 유연한 React 라이브러리로, 개발자들이 쉽게 플로우차트를 구현할 수 있게 해준다.
React Flow의 기본 개념
React Flow란 무엇인가
React Flow는 React 기반의 오픈소스 라이브러리로, 노드 기반 에디터와 인터랙티브 다이어그램을 만들기 위한 도구다. 드래그 앤 드롭, 줌, 팬 등의 기본적인 상호작용 기능을 제공하며, 커스터마이징이 용이하여 다양한 용도로 활용할 수 있다. 원래 webkul-flow로 시작되었지만, 현재는 React Flow 팀에서 적극적으로 개발하고 있다.
핵심 구성 요소
React Flow의 기본 구조는 세 가지 주요 요소로 이루어진다:
import ReactFlow, { Node, Edge } from 'reactflow';
const nodes: Node[] = [
{
id: '1',
position: { x: 100, y: 100 },
data: { label: '시작 노드' },
},
{
id: '2',
position: { x: 300, y: 100 },
data: { label: '종료 노드' },
},
];
const edges: Edge[] = [
{
id: 'e1-2',
source: '1',
target: '2',
},
];
function Flow() {
return (
<div style={{ height: '100vh' }}>
<ReactFlow nodes={nodes} edges={edges} />
</div>
);
}
**Node(노드)**는 플로우차트의 개별 요소를 나타내며, 고유한 ID, 위치, 그리고 표시할 데이터를 포함한다. **Edge(엣지)**는 노드들 간의 연결을 정의하며, 소스와 타겟 노드의 ID를 통해 관계를 설정한다. ReactFlow 컴포넌트는 이들을 렌더링하고 사용자 상호작용을 처리하는 메인 컨테이너 역할을 한다.
React Flow의 특징과 장점
React Flow는 높은 성능을 자랑한다. 가상화 기술을 사용하여 수천 개의 노드도 부드럽게 렌더링할 수 있으며, 메모리 사용량도 효율적으로 관리한다. 유연한 커스터마이징이 가능하여 노드와 엣지의 모양, 동작, 스타일을 자유롭게 변경할 수 있다. TypeScript 지원이 완벽하며, 개발 과정에서 타입 안전성을 보장한다.
기본 구현과 설정
설치 및 초기 설정
React Flow를 시작하기 위해서는 먼저 패키지를 설치해야 한다:
npm install reactflow
기본적인 플로우를 구현하려면 ReactFlow 컴포넌트를 import하고 필요한 스타일을 추가해야 한다:
import ReactFlow, {
MiniMap,
Controls,
Background,
useNodesState,
useEdgesState
} from 'reactflow';
import 'reactflow/dist/style.css';
function BasicFlow() {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
return (
<div style={{ height: '400px' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
>
<Controls />
<MiniMap />
<Background variant="dots" gap={12} />
</ReactFlow>
</div>
);
}
상태 관리와 이벤트 처리
React Flow는 useNodesState와 useEdgesState 훅을 제공하여 노드와 엣지의 상태를 쉽게 관리할 수 있게 해준다. 이들 훅은 내부적으로 상태 변경을 최적화하여 성능을 보장한다.
노드 추가나 연결 생성과 같은 사용자 행동에 대한 이벤트 처리도 간단하다:
import { useCallback } from 'react';
import { addEdge, Connection } from 'reactflow';
function InteractiveFlow() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const onNodeClick = useCallback(
(event: React.MouseEvent, node: Node) => {
console.log('노드 클릭됨:', node);
},
[]
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onNodeClick={onNodeClick}
/>
);
}
고급 기능과 커스터마이징
커스텀 노드 타입 생성
React Flow의 진정한 힘은 커스텀 노드를 만들 때 발휘된다. 비즈니스 로직에 맞는 독특한 UI를 가진 노드를 구현할 수 있다:
import { Handle, Position } from 'reactflow';
function CustomNode({ data }: { data: any }) {
return (
<div className="custom-node">
<Handle type="target" position={Position.Top} />
<div className="node-header">
<strong>{data.title}</strong>
</div>
<div className="node-content">
<p>{data.description}</p>
<button onClick={data.onClick}>실행</button>
</div>
<Handle type="source" position={Position.Bottom} />
</div>
);
}
const nodeTypes = {
custom: CustomNode,
};
function FlowWithCustomNodes() {
return (
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
/>
);
}
엣지 커스터마이징과 애니메이션
엣지 또한 다양하게 커스터마이징할 수 있다. 화살표 스타일 변경, 레이블 추가, 애니메이션 효과 등을 적용할 수 있다:
const customEdges = [
{
id: 'animated-edge',
source: '1',
target: '2',
animated: true,
style: { stroke: '#ff6b6b' },
label: '데이터 흐름',
labelStyle: { fill: '#ff6b6b', fontWeight: 700 }
}
];
레이아웃 자동화
복잡한 플로우차트에서는 노드들의 위치를 자동으로 배치하는 것이 유용하다. React Flow는 dagre나 elkjs 같은 레이아웃 라이브러리와 함께 사용할 수 있다:
import dagre from 'dagre';
const getLayoutedElements = (nodes: Node[], edges: Edge[]) => {
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
dagreGraph.setGraph({ rankdir: 'TB' });
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: 172, height: 36 });
});
edges.forEach((edge) => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
return nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
return {
...node,
position: {
x: nodeWithPosition.x - 86,
y: nodeWithPosition.y - 18,
},
};
});
};
실제 활용 사례
워크플로우 관리 시스템
React Flow는 프로세스 관리 도구에서 광범위하게 사용된다. 각 노드가 작업 단계를 나타내고, 엣지가 승인 흐름이나 데이터 전달을 표현한다. 사용자는 드래그 앤 드롭으로 프로세스를 재구성하고, 실시간으로 상태 변화를 확인할 수 있다.
데이터 파이프라인 시각화
데이터 엔지니어링 플랫폼에서는 ETL 파이프라인을 시각적으로 구성할 때 React Flow를 활용한다. 각 노드가 데이터 소스, 변환 로직, 또는 저장소를 나타내며, 개발자들이 복잡한 데이터 흐름을 직관적으로 이해하고 편집할 수 있다.
의사결정 트리와 플로우차트
비즈니스 로직 모델링이나 의사결정 지원 시스템에서도 React Flow가 유용하다. 조건부 분기, 루프, 예외 처리 등을 시각적으로 표현하여 복잡한 비즈니스 규칙을 명확하게 문서화하고 관리할 수 있다.
성능 최적화와 모범 사례
대용량 데이터 처리
수천 개의 노드를 다룰 때는 메모이제이션과 가상화 기법을 적극 활용해야 한다. React Flow는 뷰포트 기반 렌더링을 지원하므로, 화면에 보이지 않는 노드들은 렌더링하지 않는다:
const MemoizedNode = memo(({ data }: { data: any }) => {
return (
<div className="node">
{data.label}
</div>
);
});
상태 관리 전략
복잡한 애플리케이션에서는 Redux나 Zustand 같은 상태 관리 라이브러리와 React Flow를 연동하는 것이 좋다. 이를 통해 플로우 상태를 전역적으로 관리하고, 언두/리두 기능을 구현할 수 있다.
접근성 고려사항
키보드 탐색 지원과 스크린 리더 호환성을 위해 적절한 ARIA 라벨과 포커스 관리를 구현해야 한다. React Flow는 기본적인 접근성 기능을 제공하지만, 커스텀 노드에서는 추가적인 구현이 필요하다.
마무리
React Flow는 현대 웹 애플리케이션에서 복잡한 데이터 흐름과 프로세스를 시각화하는 강력한 도구다. 직관적인 API, 뛰어난 성능, 그리고 높은 커스터마이징 가능성을 통해 다양한 도메인의 요구사항을 충족할 수 있다. 프로젝트의 규모가 커질수록 적절한 상태 관리와 성능 최적화가 중요하므로, 초기 설계부터 이러한 요소들을 고려하는 것이 좋다.
React Flow를 활용하면 단순한 플로우차트부터 복잡한 워크플로우 에디터까지 다양한 수준의 애플리케이션을 구현할 수 있으며, 사용자에게 직관적이고 인터랙티브한 경험을 제공할 수 있다.