Skip to Content
ComponentsIcignalWorkflowDesigner

WorkflowDesigner

캔버스 기반 워크플로우 편집기 컴포넌트


개요

WorkflowDesigner는 트리 구조의 워크플로우를 시각적으로 설계할 수 있는 캔버스 기반 편집기입니다. 노드와 커넥터를 자동 레이아웃하며, 팔레트에서 노드를 추가하고 캔버스에서 편집할 수 있습니다.

주요 특징

  • 자동 레이아웃: parentId 기반 트리 구조를 자동으로 배치
  • 팔레트: 좌측 사이드바에서 노드 유형 선택 및 추가
  • 캔버스 조작: 줌(휠), 패닝(스페이스+드래그, 미들 클릭), 전체 화면
  • 분기 노드: 조건 분기(Yes/No 등)와 라벨/색상 지원
  • 커스텀 렌더링: renderNode로 노드 내부 커스터마이징
  • 읽기 전용: readOnly 모드 지원
  • Controlled/Uncontrolled: 선택 상태, 노드 배열 모두 제어 가능

아키텍처

영역설명
Palette좌측 사이드바. nodeTypes에 정의된 노드 유형을 그룹별로 표시
Header캔버스 상단. title/description 또는 커스텀 header 렌더링
Canvas중앙 캔버스. 노드, 커넥터, + 버튼을 렌더링
Toolbar좌측 하단. 줌, 패닝/선택 모드, 화면 맞춤, 전체 화면 버튼

기본 사용

워크플로우
시작
트리거
실행
액션
흐름
조건
시작
이메일 발송
알림 전송

Node Status

노드는 status 속성으로 시각적 상태를 표현합니다.

워크플로우 구성
액션
완료됨
완료
오류 발생
오류
비활성
잠김

Status 설명

Status설명시각적 표현
default기본 상태기본 테두리
selected선택됨Primary 테두리 + 그림자
completed완료됨녹색 테두리
error오류빨간 테두리 + 그림자
disabled비활성50% 투명도, 클릭 불가
locked잠김70% 투명도, 잠금 아이콘

사용 예시

예시 1: 분기 워크플로우

조건 노드로 Yes/No 분기를 만들 수 있습니다.

워크플로우
시작
트리거
흐름
조건
실행
액션
YesNo
고객 방문
VIP 여부
VIP 혜택 안내
일반 안내

예시 2: onNodesChange를 활용한 편집 모드

onNodesChange를 전달하면 내장 추가/삭제 로직이 활성화됩니다.

import { WorkflowDesigner } from "@vortex/ui-icignal" import type { WorkflowNode, NodeTypeDefinition } from "@vortex/ui-icignal" import { useState } from "react" function WorkflowEditor() { const [nodes, setNodes] = useState<WorkflowNode[]>([ { id: "1", type: "trigger", title: "시작", parentId: null }, ]) const nodeTypes: NodeTypeDefinition[] = [ { type: "trigger", label: "트리거", maxCount: 1, maxChildren: 1 }, { type: "action", label: "액션", maxChildren: 1 }, ] return ( <div style={{ height: 600 }}> <WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} onNodesChange={setNodes} onNodeSelect={(id) => console.log("선택:", id)} /> </div> ) }

예시 3: 커스텀 노드 렌더링

renderNode prop으로 노드 내부를 자유롭게 커스터마이징할 수 있습니다.

<WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} onNodesChange={setNodes} renderNode={({ node, isSelected }) => ( <div className="p-3"> <div className="font-semibold">{node.title}</div> {node.description && ( <div className="text-xs text-muted-foreground mt-1"> {node.description} </div> )} {isSelected && ( <div className="text-xs text-primary mt-2">선택됨</div> )} </div> )} />

예시 4: 커스텀 헤더

header prop으로 캔버스 상단에 커스텀 UI를 배치하거나, title/description으로 간단히 텍스트를 표시합니다.

// 간단한 텍스트 헤더 <WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} title="캠페인 워크플로우" description="고객 여정을 설계하세요" /> // 커스텀 헤더 <WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} header={ <div className="flex items-center justify-between"> <h3>캠페인 A</h3> <button>저장</button> </div> } />

예시 5: 수평 레이아웃

layoutDirection="horizontal"로 왼→오른쪽 방향 트리를 구성합니다.

<WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} layoutDirection="horizontal" onNodesChange={setNodes} />

API Reference

WorkflowDesignerProps

Props

PropTypeDefaultDescription
nodesWorkflowNode[]필수노드 목록
nodeTypesNodeTypeDefinition[]필수노드 유형 정의 (팔레트에 표시)
connectorsWorkflowConnector[]자동 생성커넥터 목록 (생략 시 parentId에서 자동 생성)
selectedNodeIdstring | null-현재 선택된 노드 ID (controlled)
focusedNodeIdstring | nullnull포커스 하이라이트 노드 ID
invalidNodeIdsstring[][]유효성 오류 노드 ID 목록
renderNode(props: WorkflowNodeRenderProps) => ReactNode-커스텀 노드 렌더러
nodeActionsWorkflowNodeAction[]기본(삭제, 설정)노드 hover/선택 시 표시되는 액션 버튼
paletteTitlestring"워크플로우 구성"팔레트 타이틀
titlestring-캔버스 상단 타이틀
descriptionstring-캔버스 상단 설명
headerReactNode-커스텀 헤더 (설정 시 title/description보다 우선)
readOnlybooleanfalse읽기 전용 모드
defaultScalenumber1초기 줌 레벨
minScalenumber0.25최소 줌 레벨
maxScalenumber2최대 줌 레벨
nodeWidthnumber200노드 기본 너비 (px)
nodeGapXnumber60노드 간 수평 간격 (px)
nodeGapYnumber60노드 간 수직 간격 (px)
layoutDirection"vertical" | "horizontal""vertical"레이아웃 방향
generateNodeId() => stringcrypto.randomUUID노드 ID 생성기
classNamestring-루트 요소 CSS 클래스

Events

EventTypeDescription
onNodeSelect(nodeId: string | null) => void노드 선택 시
onNodeDblClick(nodeId: string) => void노드 더블클릭 시
onNodeAdd(nodeType: string, parentNodeId: string, insertBeforeNodeId?: string) => void노드 추가 시
onNodeDelete(nodeId: string) => void노드 삭제 시
onNodeSettings(nodeId: string) => void노드 설정 버튼 클릭 시
onNodesChange(nodes: WorkflowNode[]) => void내장 추가/삭제 후 노드 배열 변경 시
onChange(nodes: WorkflowNode[], connectors: WorkflowConnector[]) => void데이터 변경 시
onCanvasClick() => void캔버스 빈 영역 클릭 시 (선택 초기화)

WorkflowNode

노드 데이터 구조입니다.

PropertyTypeDefaultDescription
idstring필수노드 고유 식별자
typestring필수노드 유형 (NodeTypeDefinition.type과 매칭)
titlestring필수노드 타이틀
descriptionstring-노드 설명 (요약 정보)
captionstring-우상단 캡션 (날짜, 상태 텍스트 등)
statusWorkflowNodeStatus"default"노드 상태
iconReactNode-노드 아이콘
badgestring-상태 뱃지 텍스트
parentIdstring | null-부모 노드 ID (null = 루트 노드)
branchIndexnumber0부모 노드의 분기 인덱스 (0-based)
maxChildrennumber-이 노드의 최대 자식 수 (개별 오버라이드)
dataRecord<string, unknown>-사용자 정의 데이터
lockedbooleanfalse잠금 상태
invalidbooleanfalse유효성 오류 여부

NodeTypeDefinition

팔레트에 표시되는 노드 유형 정의입니다.

PropertyTypeDefaultDescription
typestring필수노드 유형 식별자
labelstring필수노드 유형 라벨
iconReactNode-노드 아이콘
groupstring-팔레트 그룹명
allowedAfterstring[]-허용된 부모 노드 유형 (비어있으면 제한 없음)
maxCountnumber-최대 배치 가능 수
maxChildrennumber1기본 최대 자식 수 (1=선형, 2+=분기)
isBranchingbooleanfalse조건 분기 노드 여부
branchLabelsstring[]-분기 라벨 (예: ["Yes", "No"])
branchColorsstring[]-분기 색상 (예: ["#22c55e", "#ef4444"])
childrenLayout"vertical" | "horizontal" | string[]"vertical"자식 배치 방향

WorkflowNodeAction

노드 hover/선택 시 상단에 표시되는 액션 버튼입니다.

PropertyTypeDescription
idstring액션 식별자
iconReactNode액션 아이콘
labelstring액션 라벨 (툴팁)
onClick(nodeId: string) => void클릭 핸들러
visible(node: WorkflowNode) => boolean표시 조건 (생략 시 항상 표시)

WorkflowNodeRenderProps

renderNode 콜백에 전달되는 props입니다.

PropertyTypeDescription
nodeWorkflowNode노드 데이터
isSelectedboolean선택 상태
isFocusedboolean포커스 상태
isHighlightedboolean하이라이트 상태

WorkflowConnector

노드 간 연결선 데이터입니다. 생략 시 parentId 관계에서 자동 생성됩니다.

PropertyTypeDescription
idstring커넥터 고유 식별자
sourceIdstring시작 노드 ID
targetIdstring끝 노드 ID
labelstring커넥터 라벨
colorstring커넥터 색상
branchIndexnumber분기 인덱스

기본 사용법

import { WorkflowDesigner } from "@vortex/ui-icignal" import type { WorkflowNode, NodeTypeDefinition } from "@vortex/ui-icignal" import { useState } from "react" const nodeTypes: NodeTypeDefinition[] = [ { type: "trigger", label: "트리거", maxCount: 1, maxChildren: 1 }, { type: "action", label: "액션", maxChildren: 1 }, { type: "condition", label: "조건", maxChildren: 2, isBranching: true, branchLabels: ["Yes", "No"], branchColors: ["#22c55e", "#ef4444"] }, ] function MyWorkflow() { const [nodes, setNodes] = useState<WorkflowNode[]>([ { id: "1", type: "trigger", title: "시작", parentId: null }, ]) const [selected, setSelected] = useState<string | null>(null) return ( <div style={{ height: 600 }}> <WorkflowDesigner nodes={nodes} nodeTypes={nodeTypes} selectedNodeId={selected} onNodeSelect={setSelected} onNodesChange={setNodes} onNodeDelete={(id) => console.log("삭제:", id)} onNodeSettings={(id) => console.log("설정:", id)} /> </div> ) }

캔버스 조작

마우스/키보드

조작동작
마우스 휠줌 인/아웃 (마우스 위치 기준)
스페이스 + 드래그캔버스 패닝
미들 클릭 + 드래그캔버스 패닝
노드 클릭노드 선택
노드 더블클릭onNodeDblClick 호출
캔버스 빈 영역 클릭선택 초기화

툴바 버튼

버튼동작
확대 (ZoomIn)줌 인 (+0.15 step)
축소 (ZoomOut)줌 아웃 (-0.15 step)
이동 (Hand)패닝 모드
선택 (MousePointer2)선택 모드 (기본)
화면 맞춤 (Maximize)모든 노드가 보이도록 줌/패닝 조정
전체 화면 (Fullscreen)전체 화면 토글

노드 추가/삭제 플로우

추가

  1. 커넥터 중간 또는 리프 노드 하단의 + 버튼 클릭
  2. 팔레트에서 추가 가능한 노드 유형이 하이라이트됨 (allowedAfter 기반 필터링)
  3. 노드 유형 클릭 → onNodesChange로 새 노드가 포함된 배열 전달
  4. onNodeAdd 콜백도 함께 호출 (기존 호환)

삭제

  1. 노드 hover/선택 시 상단 삭제 버튼 클릭
  2. onNodesChange가 있으면 내장 로직으로 자식 재연결 처리:
    • 자식이 1개이고 부모가 있으면 → 부모에 재연결
    • 그 외 → 하위 노드 모두 삭제
  3. onNodeDelete 콜백도 함께 호출

접근성

권장 사항

  • ✅ 각 노드에 title을 설정하여 용도를 명확히 전달
  • readOnly 모드에서 + 버튼과 노드 액션 버튼이 자동 숨김
  • ✅ 키보드: 스페이스바로 패닝 모드 전환
  • ❌ 컨테이너에 충분한 높이(min-height: 400px)를 지정하지 않으면 콘텐츠가 잘릴 수 있음

관련 컴포넌트

  • DataTable: 테이블 형태의 데이터 표시
  • Editor: 리치 텍스트 편집기
Last updated on