React Flow로 대화형 다이어그램 구축하기
React 애플리케이션에서 직관적인 노드 기반 시각화 인터페이스를 만들 수 있는 React Flow 라이브러리 완전 가이드
들어가며
현대 웹 애플리케이션에서 복잡한 데이터 관계나 프로세스 플로우를 시각적으로 표현해야 할 때가 많다. 특히 워크플로우 에디터, 데이터 파이프라인 설계 도구, 또는 시스템 아키텍처 다이어그램과 같은 기능을 구현할 때 React Flow가 강력한 해결책을 제공한다. React Flow는 React 생태계에서 노드 기반의 대화형 다이어그램을 쉽게 구축할 수 있게 해주는 라이브러리로, 드래그 앤 드롭, 줌/팬, 실시간 연결 등 다양한 기능을 제공한다.
React Flow 기본 개념
React Flow란 무엇인가
React Flow는 React 환경에서 노드 기반 에디터와 대화형 다이어그램을 만들기 위한 오픈소스 라이브러리다. 이 라이브러리는 플로우차트, 다이어그램, 노드 에디터, 워크플로우 빌더 등 다양한 시각화 도구를 구축할 수 있는 강력한 기능들을 제공한다. React Flow의 핵심은 노드(Node)와 엣지(Edge)라는 두 가지 주요 구성 요소로 이루어져 있으며, 이들을 조합하여 복잡한 관계를 시각적으로 표현할 수 있다.
핵심 구성 요소
React Flow의 기본 구조는 다음과 같이 구성된다:
import ReactFlow, {
Node,
Edge,
Controls,
Background,
MiniMap
} from 'reactflow';
import 'reactflow/dist/style.css';
const initialNodes: Node[] = [
{
id: '1',
type: 'input',
position: { x: 250, y: 5 },
data: { label: '시작' }
},
{
id: '2',
position: { x: 100, y: 100 },
data: { label: '처리 단계' }
}
];
const initialEdges: Edge[] = [
{
id: 'e1-2',
source: '1',
target: '2',
type: 'smoothstep'
}
];
function Flow() {
return (
<div style={{ width: '100vw', height: '100vh' }}>
<ReactFlow
nodes={initialNodes}
edges={initialEdges}
>
<Controls />
<MiniMap />
<Background />
</ReactFlow>
</div>
);
}
React Flow의 주요 특징
React Flow는 현대 웹 개발에 필요한 다양한 기능들을 기본적으로 제공한다. 완전한 TypeScript 지원으로 타입 안정성을 보장하며, SSR(Server-Side Rendering) 호환성을 통해 Next.js와 같은 프레임워크에서도 원활하게 작동한다. 플러그인 시스템을 통해 기능을 확장할 수 있고, 다양한 노드 타입과 엣지 스타일을 지원한다. 또한 접근성(a11y)을 고려하여 설계되어 키보드 내비게이션과 스크린 리더를 지원한다.
노드(Node) 시스템
기본 노드 타입
React Flow는 여러 가지 기본 노드 타입을 제공한다. input
노드는 플로우의 시작점을 나타내며 타겟 핸들만 가진다. default
노드는 일반적인 처리 단계를 표현하며 소스와 타겟 핸들을 모두 가진다. output
노드는 플로우의 끝점을 나타내며 소스 핸들만 가진다. 이러한 기본 타입들은 대부분의 일반적인 다이어그램 요구사항을 만족시킨다.
커스텀 노드 생성
더 복잡한 기능이 필요할 때는 커스텀 노드를 생성할 수 있다:
import { Handle, Position, NodeProps } from 'reactflow';
interface CustomNodeData {
title: string;
description: string;
status: 'pending' | 'processing' | 'completed';
}
function CustomNode({ data }: NodeProps<CustomNodeData>) {
const statusColor = {
pending: '#f39c12',
processing: '#3498db',
completed: '#27ae60'
};
return (
<div className="custom-node"
style={{
border: `2px solid ${statusColor[data.status]}`,
borderRadius: '10px',
padding: '15px',
background: 'white'
}}>
<Handle
type="target"
position={Position.Top}
style={{ background: '#555' }}
/>
<div>
<h3>{data.title}</h3>
<p>{data.description}</p>
<span className={`status ${data.status}`}>
{data.status}
</span>
</div>
<Handle
type="source"
position={Position.Bottom}
style={{ background: '#555' }}
/>
</div>
);
}
const nodeTypes = {
customNode: CustomNode
};
노드 상태 관리
노드의 동적 업데이트를 위해서는 상태 관리가 중요하다. React Flow는 onNodesChange
콜백을 통해 노드 변경사항을 처리할 수 있다. 이를 통해 노드의 위치 변경, 선택 상태, 삭제 등의 이벤트를 효과적으로 관리할 수 있다. 또한 useReactFlow
훅을 사용하여 프로그래머틱하게 노드를 조작할 수 있다.
엣지(Edge)와 연결 시스템
엣지 타입과 스타일링
React Flow는 다양한 엣지 타입을 제공한다. default
엣지는 직선 연결을 제공하고, straight
엣지는 완전한 직선을, step
엣지는 계단식 연결을, smoothstep
엣지는 부드러운 곡선 연결을 제공한다. 각 엣지는 색상, 두께, 애니메이션 등 다양한 스타일 옵션을 적용할 수 있다.
동적 연결 생성
사용자가 런타임에 노드 간 연결을 생성할 수 있도록 하려면 onConnect
콜백을 사용한다:
import { useCallback } from 'react';
import { Connection, addEdge, useNodesState, useEdgesState } from 'reactflow';
function FlowWithConnections() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params: Connection) => {
const newEdge = {
...params,
id: `e${params.source}-${params.target}`,
type: 'smoothstep',
animated: true,
style: { stroke: '#3498db', strokeWidth: 2 }
};
setEdges((eds) => addEdge(newEdge, eds));
},
[setEdges]
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
connectionMode="loose"
/>
);
}
연결 검증과 제한
특정 비즈니스 로직에 따라 연결을 제한해야 할 때가 있다. isValidConnection
함수를 통해 연결 가능 여부를 검증할 수 있다. 예를 들어, 같은 타입의 노드끼리만 연결을 허용하거나, 순환 연결을 방지하는 등의 로직을 구현할 수 있다.
고급 기능과 최적화
성능 최적화 전략
대량의 노드와 엣지를 다룰 때는 성능 최적화가 중요하다. React Flow는 nodesDraggable
, nodesConnectable
, elementsSelectable
등의 props를 통해 불필요한 기능을 비활성화할 수 있다. 또한 onlyRenderVisibleElements
옵션을 사용하여 뷰포트에 보이는 요소만 렌더링하여 성능을 향상시킬 수 있다.
레이아웃 알고리즘 적용
자동 레이아웃을 위해 Dagre나 ELK와 같은 그래프 레이아웃 라이브러리를 통합할 수 있다:
import dagre from 'dagre';
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
function getLayoutedElements(nodes: Node[], edges: Edge[]) {
dagreGraph.setGraph({ rankdir: 'TB' });
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: 150, height: 50 });
});
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 - 75,
y: nodeWithPosition.y - 25,
},
};
});
}
데이터 영속성과 저장
플로우 데이터의 저장과 복원을 위해서는 toObject()
메서드를 사용하여 현재 상태를 직렬화하고, 필요시 복원할 수 있다. 이는 사용자가 작성한 다이어그램을 데이터베이스에 저장하거나 파일로 내보내기할 때 유용하다.
실제 구현 사례
워크플로우 빌더
워크플로우 빌더는 React Flow의 대표적인 활용 사례다. 사용자가 작업 단계를 노드로 정의하고, 조건부 분기를 엣지로 연결하여 복잡한 비즈니스 프로세스를 시각적으로 설계할 수 있다. 각 노드는 특정 작업 타입(API 호출, 조건 검사, 데이터 변환 등)을 나타내며, 실행 상태를 실시간으로 시각화할 수 있다.
데이터 파이프라인 에디터
데이터 엔지니어링에서 ETL(Extract, Transform, Load) 프로세스를 시각적으로 설계할 때 React Flow를 활용할 수 있다. 데이터 소스 노드, 변환 로직 노드, 대상 시스템 노드를 연결하여 복잡한 데이터 파이프라인을 직관적으로 구성하고 관리할 수 있다.
시스템 아키텍처 다이어그램
마이크로서비스 아키텍처나 클라우드 인프라를 시각화할 때도 유용하다. 각 서비스를 노드로 표현하고, 서비스 간 의존성을 엣지로 연결하여 전체 시스템의 구조를 한눈에 파악할 수 있는 대화형 다이어그램을 생성할 수 있다.
마무리
React Flow는 React 생태계에서 노드 기반 시각화를 구현하기 위한 완성도 높은 솔루션이다. 간단한 플로우차트부터 복잡한 워크플로우 에디터까지 다양한 용도로 활용할 수 있으며, 풍부한 커스터마이징 옵션과 성능 최적화 기능을 제공한다. TypeScript 지원과 활발한 커뮤니티, 지속적인 업데이트를 통해 프로덕션 환경에서도 안정적으로 사용할 수 있다. 복잡한 데이터 관계나 프로세스를 사용자 친화적인 방식으로 표현해야 하는 프로젝트라면 React Flow를 고려해볼 만하다.