CastCanvas Lab: React Flow CustomEdge를 활용한 지능형 노드 연결 시스템 구현

단순히 노드를 배치하는 것을 넘어, 노드 간의 논리적 관계를 명시적으로 드러내는 ‘연결선(Edge)’ 시스템을 구축했다. 기본 엣지의 한계를 극복하기 위해 사용자 정의 엣지 컴포넌트를 도입하고, 상태 관리 라이브러리와의 결합을 통해 인터랙티브한 연결 경험을 제공하는 과정을 정리한다.


목표

  • 디자인 일관성 확보: 시스템 테마와 연동된 전용 엣지 스타일링 구현
  • 동적 연결 관리: Zustand 스토어를 통해 엣지의 생성, 변경, 삭제 과정을 선언적으로 처리
  • 확장 가능한 타입 시스템: 다양한 형태의 엣지를 손쉽게 추가할 수 있는 구조 설계

핵심 구현 포인트

1) SVG 기반의 CustomEdge 구현 (Visual Identity)

React Flow의 getBezierPath를 활용하여 유연한 곡선 엣지를 생성하고, 디자인 토큰(--color-canvas-edge)을 SVG의 stroke 속성에 바인딩하여 다크 모드 등 테마 변경에 즉각 대응하도록 구현했다.

// src/features/canvas/components/CustomEdge/CustomEdge.tsx
export const CustomEdge = ({ id, sourceX, sourceY, targetX, targetY, ...props }: EdgeProps) => {
  const [edgePath, labelX, labelY] = getBezierPath({
    sourceX,
    sourceY,
    targetX,
    targetY,
    sourcePosition: props.sourcePosition,
    targetPosition: props.targetPosition,
  });

  return (
    <path
      id={id}
      className={styles.edgePath}
      d={edgePath}
      markerEnd={props.markerEnd}
    />
  );
};

2) 중앙 집중식 연결 로직 (State-Driven Connection)

onConnect 핸들러를 Zustand 스토어 내부에 정의하여, 사용자가 노드를 연결하는 순간 특정 타입(custom)의 엣지가 생성되도록 강제하고 전역 상태를 업데이트한다.

// src/features/canvas/stores/canvasStore.ts
onConnect: (connection) => {
  set({ 
    edges: addEdge({ ...connection, type: "custom" }, get().edges) 
  });
},

트러블슈팅 / 고민 포인트

엣지 타입의 명시적 지정 문제

  • 문제: React Flow는 기본적으로 default 타입을 사용하지만, 프로젝트의 디자인 요구사항에 맞춘 CustomEdge를 모든 연결에 적용해야 했다.
  • 해결: onConnect 시점에서 connection 객체에 { type: 'custom' } 속성을 병합(Merge)하여, 사용자가 의식하지 않아도 항상 정의된 커스텀 엣지가 렌더링되도록 설계했다.

결과

  • 인터랙티브 연결 시스템: 노드 핸들 드래그를 통한 실시간 엣지 생성 기능 완료
  • 테마 대응 스타일링: 시스템 설정에 따라 색상이 변하는 유연한 엣지 UI 적용
  • 간편한 관리: Delete/Backspace 키를 통해 불필요한 연결을 즉시 제거 가능
  • 확장성 확보: edgeTypes 설정을 통해 향후 레이블이 있는 엣지나 직선 엣지 등으로의 확장이 용이함

다음 개선 아이디어

  • 엣지 레이블: 연결선의 의미를 설명할 수 있는 텍스트 레이블 추가
  • 애니메이션 효과: 데이터 흐름을 시각화하기 위한 엣지 애니메이션(Path animation)
  • 스냅 기능: 엣지 연결 시 가장 가까운 핸들로 자동 스냅되는 기능 보완