Community생산성 & 협업github.com

workflow-visualizer: 비즈니스 프로세스 → 인터랙티브 플로우차트

사용자의 구술이나 문서에 담긴 프로세스, SOP, 생산 체인, 회고 타임라인을 Mermaid 소스 코드와 자체 포함된 인터랙티브 HTML 플로우차트(제로 디펜던시, 순수 오프라인, CDN 미사용)로 렌더링합니다. 사용자가 "프로세스를 그려줘/플로우차트 그려줘/워크플로우 시각화/정리해줘"라고 요청할 때 사용합니다.

workflow-visualizer: 비즈니스 프로세스 → 인터랙티브 플로우차트란 무엇인가요?

이 스킬은 비즈니스 프로세스를 시각화하여 인터랙티브 플로우차트로 변환하는 데 특화되어 있습니다. 핵심 강점은 **제로 디펜던시**와 **완전한 오프라인** 지원으로, `node:` 내장 모듈만 사용하여 어떤 CDN도 필요 없으며, 인터넷이 끊기거나 내부 네트워크에서도 완벽하게 작동합니다. 출력물은 `.html` 단일 파일(클릭 한 번으로 실행)과 `.svg`로, 내부에 `<style>`을 포함하여 다른 사람에게 보내도 정상적으로 표시됩니다. 이 스킬은 Claude Code, Cursor, Codex와 같은 AI 에이전트 도구에서 "프로세스를 그려줘"와 같은 자연어 요청을 처리할 때 자동으로 호출됩니다. 사용자 입력에서 프로세스 단계, 시간대, 의존성, 분기점, 위험 요소 등을 추출한 후 Mermaid 문법으로 변환하거나 풍부한 메타데이터(시간, 담당자, 상태 등)를 지원하는 JSON 사양을 작성합니다. 최종적으로는 세로형(모바일 최적화, 단일 열 스크롤)과 가로형(데스크톱 최적화, 다중 열 분할)의 두 가지 레이아웃을 동시에 생성하여, 사용자 기기 환경에 따라 자동으로 적절한 보기를 선택합니다. 생성된 HTML 플로우차트는 노드 클릭 시 상세 정보(시간, 담당자, 설명)를 볼 수 있고, 검색 및 필터링, 밝은/어두운 테마 전환, SVG/PNG 내보내기 등 다양한 인터랙티브 기능을 제공합니다. 또한 생성된 Mermaid 코드를 Feishu, Yuque, GitHub 등에 바로 붙여넣을 수 있어 협업과 문서화에 매우 유용합니다.

지원 대상✓Claude Code✓Codex CLI✓Cursor
npx skills add jimku233/workflow-visualizer

Installed? Explore more 생산성 & 협업 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

workflow-visualizer:비즈니스 프로세스 → 인터랙티브 플로우차트

렌더링 스크립트:scripts/flow.mjs(이 스킬 디렉토리 기준 상대 경로) 절대 경로:scripts\flow.mjs 전체 필드 및 문법 매뉴얼:references/spec.md(사양을 작성하기 전에 먼저 읽으세요)

一、핵심 제약 사항(이렇게 해야 하는 이유)

  1. 제로 디펜던시:node: 내장 모듈만 사용합니다. npm install이 필요 없고, mermaid를 설치할 필요도 없습니다.
  2. 순수 오프라인:레이아웃과 렌더링은 이 스크립트가 직접 구현한 SVG로, 어떤 CDN도 참조하지 않습니다. 네트워크가 끊기거나, 내부 네트워크, 외부 네트워크가 없어도 차트를 생성할 수 있습니다(로컬 web_fetch/docs.deepseek.com도 연결되지 않으며, CDN에 의존하는 모든 방식은 실패합니다).
  3. 자체 포함된 산출물:내보낸 .svg에는 <style>이 내장되어 있어, 별도로 보내도 올바르게 표시됩니다. .html은 단일 파일로, 더블 클릭하면 바로 열립니다.

二、세 단계 사용법

1단계:프로세스 추출(이 단계는 생략할 수 없습니다) 사용자의 실제 자료에서 추출하며, 임의로 만들어내지 마세요. 출처 우선 순위:

  • 작업 공간의 기획안/회고 문서(*.md,보통 "워크플로우/SOP/일정" 섹션이 있음)
  • 사용자 구술(이 경우 확실하지 않은 부분은 status: hold로 표시하고 note에 확인 필요 사항을 명시)
  • 대화 기록 또는 로그

추출 시 다음 다섯 가지를 고정적으로 추출하세요:단계(누가 무엇을 하는지), 시간대, 선후 의존성, 판단 분기, 위험/수동 검토 필요 지점.

2단계:사양 작성

  • 그림이 복잡하지 않은 경우(순수 선형 + 한두 개 분기)→ Mermaid로 직접 작성하여 .mmd로 저장하는 것이 가장 간편합니다.
  • 시간/담당자/상태/설명/그룹이 필요한 경우 → JSON을 작성합니다(정보 밀도가 훨씬 높아 패널에 표시할 내용이 있음).
  • 먼저 sample을 실행하여 완성된 사양을 템플릿으로 얻는 것이 가장 빠릅니다:
node "scripts\flow.mjs" sample --out ".\out\내_프로세스.json"

3단계:검증 + 렌더링

$sk = "scripts\flow.mjs"
node $sk check  ".\out\내_프로세스.json"     # 구조 검사:고립 노드/도달 불가/종료점 없음/사이클
node $sk render ".\out\내_프로세스.json" --all --out ".\out\내_프로세스.html"

산출물은 기본적으로 입력 파일과 동일한 디렉토리에 생성됩니다.

한 번의 render로 두 가지 레이아웃 생성(동일한 HTML 내에서, 화면에 따라 자동 선택, 하단에서 수동 전환 가능):

레이아웃배치적응 기기
세로형 portrait단일 열 세로(wrap: 0)휴대폰:세로 스크롤, 너비 꽉 채움
가로형 landscape긴 프로세스 자동 다중 열 분할(wrap: -1,목표 가로세로비 1.6)컴퓨터:한 화면에 표시

자주 사용하는 매개변수:--out <file.html>、--all(추가로 <이름>-세로형.svg、<이름>-가로형.svg、.mmd 출력)、--wrap N(가로형 각 열 고정 N 행)、--debug(두 레이아웃의 열 수/행 수와 각 열 구성원 출력).

두 레이아웃 검증(권장):

node $sk preview ".\out\내_프로세스.html"   # 「【컴퓨터용】비교 미리보기.html」 생성:휴대폰/컴퓨터 두 iframe 나란히 표시

디버그 브라우저에서 열어보면 두 레이아웃이 모두 올바른지 한눈에 확인할 수 있습니다. 실제 기기 크기까지 정확하게 확인하려면 CDP 장치 시뮬레이션을 사용하세요:

# 휴대폰 + 컴퓨터 두 장의 실제 측정 스크린샷, 그리고 canvas/aside/footer의 실제 높이와 자동 선택된 레이아웃 출력
node $sk\..\tools\devshot.mjs "http://127.0.0.1:8765/내_프로세스.html" 휴대폰.png 컴퓨터.png

# 페이지에서 JS를 실행하여 상호 작용 검증하기(예:노드 클릭, 검색 입력, 서랍 열기):
node $sk\..\tools\probe.mjs <url> probe.js --w 390 --h 844 --mobile --touch

이 두 도구는 모두 자체 임시 탭을 새로 만들고, 사용이 끝나면 닫습니다. 사용자가 현재 탐색 중인 탭에는 영향을 주지 않습니다(초기 버전에서는 첫 번째 페이지 대상을 직접 가져와 사용자가 보고 있던 페이지를 테스트 대상으로 삼았으며, 그 교훈은 아래에 기록되어 있습니다). 하지만 주의할 점:새 탭은 사용자의 브라우저 창에 나타나며 포커스를 빼앗습니다——사용자가 현재 해당 디버그 브라우저를 사용 중이라면 먼저 물어보고 사용하세요. 그렇지 않으면 서로 방해할 수 있습니다.

三、차트 생성 후 반드시 자체 점검("명령이 성공했다"고만 보지 마세요)

명령 성공 ≠ 차트가 보기 좋다는 의미. 최소 한 번 시각적 확인을 수행하세요. browser-control을 사용하세요:

# 1) ASCII 파일명 복사본을 생성하여 file://의 한글 인코딩 문제를 피함
Copy-Item ".\out\내_프로세스.html" ".\out\preview.html" -Force
# 2) 시스템 URI 생성기를 사용하여 올바른 file:// 주소를 얻음(수동으로 퍼센트 인코딩하면 오류가 발생하기 쉬움)
$u = [System.Uri]::new((Resolve-Path ".\out\preview.html").Path).AbsoluteUri
node browser-control\webctl.mjs shot $u "out\shot.png" 5000

그런 다음 이미지를 확인하세요:텍스트가 잘리지 않았는지, 분기가 서로 엉키지 않았는지, 그룹 밴드가 노드를 가리지 않는지. 클릭 상호 작용도 스크립트로 검증할 수 있습니다(webctl.mjs evalfile,단 (function(){...})() 표현식이어야 하며, 맨 return은 Illegal return statement 오류를 발생시킵니다).

四、사람에게 보여주기:컴퓨터 / 휴대폰 두 가지 열기 방식

컴퓨터:해당 .html을 더블 클릭하세요(단일 파일, 오프라인 사용 가능). 문서에 첨부하려면 .svg를 끌어다 놓으세요.

휴대폰(HTML이 컴퓨터에 있고, 휴대폰 브라우저에는 "로컬 파일 열기" 기능이 없음)——파일을 전송하지 않고 LAN을 통해 접속하세요:

node "scripts\serve.mjs" ".\out" 8765

스크립트는 사용 가능한 휴대폰 주소를 직접 출력합니다(VPN/가상 네트워크 카드/169.254는 자동으로 「무시」 표시됨). 형식은 다음과 같습니다: 휴대폰 → http://<내_LAN_IP>:8765/ ✔ 이 주소 사용(WLAN) 휴대폰을 동일한 Wi-Fi에 연결하고, 브라우저에서 이 주소를 연 후 파일명을 클릭하면 됩니다. 핵심 사항:

  • 산출물 디렉토리만 노출시키고, 작업 공간 루트를 가리키지 마세요.
  • Windows에서 처음 실행 시 방화벽 팝업이 나타나면 「전용 네트워크」를 선택하여 인바운드를 허용하세요. 팝업이 없는데도 열리지 않는다면 방화벽에서 차단된 것입니다.
  • 디렉토리 페이지는 파일 목록이며, 휴대폰에서 임의의 .html / .svg / .png에 접근할 수 있습니다.
  • 확인이 끝나면 이 서비스를 중지하도록 요청할 수 있습니다(백그라운드 작업임). 또는 해당 터미널 창을 직접 닫으면 됩니다.

동일한 네트워크가 아니거나 / 다른 사람에게 보내야 하는 경우:해당 HTML 파일 자체를 직접 보내세요——자체 포함되어 있고 오프라인에서 열 수 있으며, WeChat/QQ로 파일 전송해도 됩니다. 그림만 필요한 경우 페이지의 「PNG 내보내기」를 클릭하여 이미지를 보내거나, 브라우저에서 Ctrl+P를 눌러 PDF로 저장하세요.

휴대폰에서의 조작:한 손가락 드래그로 이동, 두 손가락으로 확대/축소, 노드를 살짝 눌러 세부 정보 확인, 하단 「맞추기」로 전체 화면 보기. 세로형/가로형 버튼은 페이지 하단 왼쪽에 있습니다(휴대폰에서는 아이콘만 표시됨).

영역기능
상단 바브랜드 로고 + 제목, 검색창, 「전체 플로우차트」 버튼(오른쪽 상단), 상태 범례 캡슐(클릭하여 필터링)
오른쪽 패널선택되지 않은 경우 프로세스 개요;검색 시 먼저 일치 목록 표시(아래 참조);노드 선택 후에는 세부 정보 카드 + 「개요로 돌아가기」
하단 바세로형/가로형 전환, 개요/세부 정보(휴대폰), 확대/축소, 맞추기, SVG/PNG 내보내기, 밝은/어두운 테마, Mermaid 소스 코드

검색의 세 가지 일치 수준(중요, 버그로 오해하지 마세요)

검색은 제목 / id / 시간 / 담당자 / 설명 / 비고를 동시에 검색하지만, 캔버스에서는 제목만 볼 수 있으므로 일치는 두 가지 유형으로 나뉩니다:

  • 보이는 일치(제목/id/시간/담당자)→ 노드에 파란색 테두리, 개수는 하나로 계산
  • 설명/비고에만 있는 일치 → 노드에 주황색 점선 테두리, 개수는 2개 일치(1개는 설명/비고에만 있음)로 표시, 오른쪽에 일치 목록도 함께 표시됨(클릭하면 해당 위치로 이동)

일치 색상은 전용 변수 --hit(다크 테마 #4a9dff, 미색 테마 #2b7fe0)를 사용해야 하며, --accent를 사용할 수 없습니다 —— 미색 테마의 강조 색상은 테라코타 #bf6b3a이며, 이를 사용하면 일치 항목이 "빨간색 테두리"가 되어 "파란색 테두리" 설명과 맞지 않습니다(실제로 겪은 문제임).

예시:샘플 프로세스에서 컵을 검색하면 2개가 일치합니다 —— 두 번째 음료 출구 열기(제목)와 오후 보충(설명의 "컵기"). f를 검색하면 id가 f인 노드와 설명/비고에 문자 f가 포함된 모든 노드가 일치합니다. 동일한 노드는 한 번만 계산되며, 이는 중복 계산이 아닙니다.

밝은/어두운 테마:전체 설정 / 단일 차트 / 잠금(나중에 변경한 것이 우선 적용됨)

세 가지 키 모두 브라우저의 localStorage에 저장됩니다(모두 JSON {mode, ts} 저장, 타임스탬프로 비교, 나중에 변경한 쪽이 적용됨):

키작성자의미
flowviz-theme홈페이지 「표시 설정」전체 설정:시스템 따름 / 항상 어둡게 / 항상 밝게
flowviz-theme:<파일명>플로우차트 페이지의 밝음/어두움 키단일 차트의 개별 설정(값은 dark/light)
flowviz-lock:<파일명>플로우차트 페이지의 「색상 잠금」잠금 시 해당 차트 색상 고정({locked:true, mode})

적용 규칙(페이지 <head>의 부트스트랩 스크립트와 페이지 JS가 완전히 일치하여 색상 깜빡임 방지):

  1. 잠금 상태 → 잠금 순간에 고정된 색상 사용, 전체 설정과 단일 차트 설정 모두 영향 미치지 않음
  2. 그렇지 않으면 전체 설정과 단일 차트 설정의 타임스탬프 비교, 새로운 쪽이 적용됨
  3. 둘 다 없음 → 시스템 따름(prefers-color-scheme), 시스템 전환 시 실시간으로 따라감

따라서:단일 차트를 먼저 변경한 후 전체 설정을 변경하면 → 모두 전체 설정을 따라 변경됨. 전체 설정을 먼저 변경한 후 단일 차트를 변경하면 → 해당 차트만 변경됨. 잠금 후에는 먼저 잠금을 해제해야 색상을 변경할 수 있음(잠금 상태에서 밝음/어두움 키를 클릭하면 차단되고 프롬프트가 표시됨).

실제 측정 매트릭스(8단계 모두 통과, 0 이상):초기화 → 전체=밝음 → 차트=밝음. 단일 차트 밝음/어두움 클릭 → 차트=어두움(단일 차트 키에 기록). 그 다음 전체=밝음으로 변경 → 차트=밝음(전체 설정 업데이트로 단일 차트 덮어씀). 단일 차트 다시 클릭 →이 차트만 어두워짐. 잠금 → 전체 설정 변경 후에도 잠긴 어두운 색상 유지. 잠금 상태에서 밝음/어두움 키 클릭 → 차단 및 "먼저 잠금 해제" 프롬프트 표시. 잠금 해제 → 전체 설정의 밝은 색상으로 돌아감. 홈페이지 카드에 「🔒 잠금됨 밝은 색상」 배지 표시.

기타 사항:

  • 동일한 키에 반복적으로 setItem을 호출하면 덮어쓰기가 됨. 누적되지 않음(실제로 밝음/어두움 키를 12번 클릭해도 Object.keys(localStorage)는 여전히 1개 항목, 총 18바이트). "이전 항목 삭제"가 필요하지 않음
  • 설정은 기기 브라우저에 저장되므로, 서비스를 다시 시작해도 영향을 받지 않음. IP/포트 변경(origin 변경), 브라우저 데이터 삭제, 브라우저 변경 시에만 초기화됨
  • 밝은 테마는 밀레니엄 베이지 스타일로, 순수 흰색이 아님:배경 #f2ebdd, 패널 #fdfaf3, 본문 #33302a, 강조 색상 테라코타 #bf6b3a, 차트 내 바탕 #e6dcc8(어두운 테마는 기존 유지)

상단 버튼

🏠 홈 파일 목록 페이지로 돌아가기(href="./")|전체 플로우차트 오른쪽 서랍 열기|검색창|상태 범례|밝음/어두움 이 차트 전환|색상 잠금 이 차트 색상 고정. 서랍에서 현재 페이지는 비링크로 렌더링됨(클릭해도 반응 없음, "고장난 줄 알았다"는 혼동 방지). 목록은 파일명 순서로 고정 정렬되므로, fetch로 목록을 새로고침해도 순서가 변경되지 않음.

오른쪽 상단 버튼을 클릭하면 오른쪽 서랍이 열리며, 동일한 디렉토리에서 이 스킬로 생성된 모든 플로우차트를 나열합니다(파일명 순서로 고정 정렬, 현재 페이지는 「현재」로 표시되고 클릭할 수 없는 비링크로 렌더링됨). 한 항목을 클릭하여 전환할 수 있습니다. 식별 방식은 페이지의 data-flow-viz="1" 마커를 통해 이루어지므로, 다른 HTML은 포함되지 않습니다.

  • 목록 파일은 디렉토리의 프로세스_index.json이며, render가 실행될 때마다 자동으로 재구축됩니다(또한 node flow.mjs index <디렉토리>로 별도 재구축 가능)
  • HTTP로 열 때 서랍은 실시간으로 fetch하여 목록을 새로고침합니다. 로컬 파일을 더블 클릭하여 열 때는 페이지에 포함된 스냅샷을 사용합니다(각 페이지에 하나씩 포함되어 있으므로 로컬에서도 전환 가능)

검색 상호 작용("일치가 눈에 띄지 않음"에 대한 세 가지 패치)

  1. 결과 패널 자동 확장:일치 항목을 찾았고 현재 선택된 노드가 없으면 setPanel(true) 실행 —— 휴대폰에서 결과 목록은 기본적으로 접힌 하단 서랍에 숨겨져 있으며, 펼치지 않으면 힌트가 제공되지 않음(이것이 "검색해도 힌트가 없다"는 근본 원인)
  2. 이전 / 다음:결과 영역 제목 표시줄에 ↑ / ↓와 현재/전체 개수 표시. 검색창에서 Enter = 다음, Shift+Enter = 이전. Esc는 지우기
  3. 자동 포커스(해제 가능):결과 영역에 「일치 항목 자동 포커스」 스위치가 있습니다. 켜면 ——
    • 일치 항목의 경계 상자를 수용할 수 있고, 필요한 확대/축소 비율이 현재 확대/축소 비율 × 0.6 이상인 경우 → 모든 일치 항목을 프레임에 포함(여러 일치 항목을 한 화면에서 볼 수 있음)
    • 그렇지 않은 경우(일치 항목이 너무 멀리 떨어져 있음)→ 축소하지 않고, 현재 확대/축소 비율을 유지한 채 "현재 일치 항목"으로 중앙 정렬, ↑/↓로 하나씩 이동 이렇게 하면 "두 개의 멀리 떨어진 일치 항목을 모두 포함"하기 위해 텍스트를 너무 작게 만들지 않음
  4. 검색 중에 svg에 .searching 추가:일치하지 않는 항목은 14%로 페이드 아웃(연결선은 10%, 그룹 밴드는 35%), 일치 항목 테두리는 3px로 굵게 표시
  5. 프로그래밍 방식으로 이동(포커스, 점프)시 #vp에 .vp-anim 추가(0.28s 전환), 수동 드래그는 영향을 받지 않음

중복 이름 플로우차트:먼저 "내용 동일"과 "단지 이름만 같음"을 구분

동일한 제목 아래에 여러 파일이 나타나는 경우(일반적인 출처:파일을 삭제했다가 복구하여 시스템이 xxx - 복사본.html을 생성한 경우), 홈페이지 상단에 프롬프트 상자가 표시되며, 내용에 따라 두 가지로 처리됩니다:

상황판단 기준제공 액션
내용 동일(동일한 프로세스를 두 번 렌더링한 경우)프로세스 의미론적 지문 동일"최신 버전만 유지하고, 나머지는 휴지통 폴더로 이동"
내용 다름(동일한 이름이지만 다른 버전)지문 다름삭제하지 않음, "이름 변경…"만 제공(기본 제안 원래 이름(이전 버전 날짜).html)
  • 지문 = 프로세스 의미론으로, 제목/부제목/방향/그룹/노드/엣지만 추출하고 selfFile、flows、slug 등 페이지 수준 필드는 무시합니다. 따라서 "동일한 프로세스를 다른 시간에 렌더링한 두 파일"의 지문은 동일하며, "페이지 메타데이터만 변경"한 경우 잘못 판단되지 않습니다.
  • 삭제는 항상 "휴지통 폴더로 이동":동일한 디렉토리에 .trash-YYYY-MM-DD/를 만들고, HTML을 해당 SVG / MMD와 함께(정규화된 이름으로 연결을 찾아) rename으로 이동하며, 언제든지 다시 가져올 수 있습니다.
  • 처리 액션은 백엔드 엔드포인트 POST /__flow/act를 통해 이루어집니다({op:'trash', remove:[...]} / {op:'rename', file, to}):
    • 127.0.0.1에서만 호출 가능(휴대폰, LAN에서는 403 반환)——그렇지 않으면 "파일 삭제" 기능이 Wi-Fi에 노출됨
    • 파일명은 화이트리스트를 통과해야 함:경로 구분 기호 미포함, 길이 ≤200, 반드시 .html로 끝나야 함. 중복 이름 충돌 시 409 반환
    • 휴대폰 / 로컬 file:// 모드에서는 버튼이 표시되지 않고 프롬프트만 표시됨(이 두 시나리오에는 쓰기 가능한 백엔드가 없음)
  • 카드에는 파일 이름이 계속 표시됩니다(제목이 중복된 경우), 수동 식별을 용이하게 하기 위해

두 가지 열기 방식(모두 홈페이지에 접근 가능해야 함)

방식홈페이지 접근 방법홈페이지 출처
로컬 더블 클릭(file://)플로우차트 오른쪽 상단 🏠 → ./index.htmlrender가 생성한 정적 index.html(브라우저의 기본 디렉토리 목록은 파일 이름만 표시하므로, 이 파일이 필요함)
LAN / HTTP🏠 → ./serve.mjs가 동적으로 생성
  • render가 실행될 때마다 index.html을 재구축하며, 이 파일은 HTTP 홈페이지와 동일한 렌더링 코드를 공유합니다(serve.mjs가 내보낸 buildHome(). serve.mjs는 직접 실행될 때만 서비스를 시작하며, import될 때는 함수만 제공함)
  • 정적 버전과 HTTP 버전의 차이점:이동 경로는 📁 디렉토리명(로컬 폴더)로 표시, 배지는 「플로우차트 페이지」로 표시, 페이지 하단에 "로컬 파일 모드:휴대폰에서 접속하려면 먼저 서비스를 시작해야 함" 문구 추가
  • 🏠 버튼은 프로토콜을 인식합니다:location.protocol==='file:'일 때 href를 ./index.html로 변경
  • index.html 자체는 카드 목록에 표시되지 않습니다(초기 버전에서는 첫 번째 카드가 "index"로 표시됨). 목록에 없으면서 flow-stats를 파싱할 수 없는 HTML은 「기타 파일」로 분류됩니다.

홈페이지(컴퓨터/휴대폰 동일 페이지)

  • 이동 경로:🏠 플로우차트 / 서브디렉토리(현재), 각 레벨 클릭 가능
  • 디스크 경로 블록:\out을 직접 표시, 「경로 복사」 버튼 포함——원본 파일을 보내거나 소스 파일을 찾아야 할 때 다시 추측할 필요 없음
  • 검색 + 정렬 + 요약:제목/파일명으로 필터링. 정렬은 「최근 열기 우선 / 이름순 / 수정 시간순」으로 전환 가능. 요약은 3개 · 총 36개 단계 / 47개 연결 · 주의 필요 4곳(2개 차트)로 표시
  • 최근에 열기:카드를 클릭하면 flowviz-recent에 기록(최대 8개), 기본적으로 맨 위에 고정되고 「최근 열기」 배지 표시
  • 카드에는 또한 「🔒 잠금됨 X」「별도로 설정됨 X」 배지가 표시됨(로컬 localStorage를 읽음)
  • 문구는 기기에 따라 다름:동일한 HTML에 두 세트의 프롬프트를 작성하고, @media (hover:none),(pointer:coarse)로 전환(너비로 판단하는 것보다 더 정확하며, 가로 모드 휴대폰에서도 정확함)——
    • 컴퓨터(.only-pc):🖱️ 카드를 클릭하여 엽니다. 차트에서 스크롤 확대/축소, 길게 누르기 드래그 이동 가능. 오른쪽 상단 「표시 설정」에서 어두움/밝음 테마 전환
    • 휴대폰(.only-touch):📱 카드를 클릭하여 엽니다. 차트에서 한 손가락 드래그, 두 손가락 확대/축소, 단계를 눌러 세부 정보 확인…
    • 카드 배지 「휴대폰에서 여기 클릭」에는 .touchonly 추가, 컴퓨터에서는 표시되지 않음. 비교 페이지 배지는 중립적인 「나란히 비교 페이지 · 컴퓨터용」(양쪽에서 모두 이해 가능)
    • 플로우차트 페이지의 페이지 하단/사이드바 프롬프트도 마찬가지. 컴퓨터에서는 스크롤 확대/축소 · 드래그 이동 · Esc, 휴대폰에서는 한 손가락 드래그 · 두 손가락 확대/축소
  • 중복 이름 플로우차트(예:xxx - 복사본.html과 xxx.html)는 제목만으로 구분하기 어려움 → 이러한 카드는 제목 아래에 파일명을 추가로 표시함
  • 썸네일은 정규화된 이름으로 매칭:공백과 -가로형/-세로형을 제거하고 비교하므로, 예제_프로세스 - 복사본.html이 예제_프로세스-가로형 - 복사본.svg와 매칭될 수 있음(접미사 위치가 달라도 매칭됨)
  • 디렉토리 페이지 응답에는 cache-control: no-store가 포함되어야 함:동적으로 생성되므로, 브라우저가 한 번 캐시하면 새 버전을 볼 수 없음(이전에 사용자가 계속 이전 버전의 기본 목록을 보며 "홈페이지가 너무 단순하고 썸네일이 없다"고 오해한 경험 있음)

휴대폰에서의 조작

한 손가락 드래그 이동, 두 손가락 확대/축소, 노드를 살짝 눌러 세부 정보 확인, 하단 「맞추기」로 전체 화면 보기. 휴대폰에서 개요/세부 정보는 오버레이 방식의 하단 서랍(position:fixed):노드를 클릭하면 슬라이드 아웃, 빈 공간을 클릭하면 접힘, 하단 바 「개요」 버튼으로 수동 열기. 이는 flex 형제 노드가 아닌 오버레이 레이어이므로——서랍을 열고 닫아도 캔버스 높이는 변하지 않음(실제 측정 607→607), "캔버스가 짧아짐 → 다시 맞추기 → 차트 잘림" 연쇄 반응이 발생하지 않음.

"확대/축소 후에 잘못 표시됨"과 관련된 두 가지 구현 세부 사항:

  • 점자 바탕 무늬는 #vp 내부에 있으며, 범위는 캔버스의 3배이고 콘텐츠를 중심으로 함(x=-W, y=-H, w=3W, h=3H). 이렇게 하면 바탕 무늬가 확대/축소 및 이동을 따라가므로, 시야에 바탕 무늬 사각형의 딱딱한 가장자리가 드러나지 않음(초기 버전에서는 바탕 무늬가 캔버스 크기만 했기 때문에 150%로 확대한 후 이동하면 가장자리가 보였음).
  • HTML 버전에서는 SVG 제목 블록을 그리지 않음. 내보낸 .svg에서만 그림(페이지 상단 바에 이미 제목이 있음). 그렇지 않으면 확대 후 제목이 노드를 가릴 수 있음.
  • 좁은 화면에서 가로형으로 전환하면 항상 전체 화면에 맞춤:판단 기준은 캔버스 너비/콘텐츠 너비 > fit×1.8 이고 콘텐츠 가로세로비 ≤ 1.2인 경우에만 "너비를 채움"을 사용. 가로형은 1.58의 납작한 차트이며, 390px 화면에서 너비를 채우면 150%로 확대되어(한쪽 모서리만 보임), 현재는 안정적으로 100%를 유지함.

五、비즈니스 플로우차트 작성 경험 규칙

  1. 노드 세분화 정도:하나의 노드 = 하나의 전달 가능한 작업("스크립트 대본 작성"은 노드, "진지하게 생각하기"는 노드가 아님). 단일 차트는 8–16개 노드가 가장 적합하며, 20개를 초과하면 메인 차트와 서브 프로세스로 분할합니다.
  2. 상태를 적극 활용하세요:done/doing/todo/risk/hold는 장식이 아닙니다. 실제 프로세스에서 "확인 대기 중/자료 대기 중"과 같은 차단은 hold로 표시하고, "반드시 수동 검토 필요"는 risk로 표시하세요——이것이 바로 플로우차트가 문서보다 강력한 점입니다.
  3. 판단 노드에는 실제 분기만 넣으세요:{...}는 서로 다른 경로로 이어지는 결정에 사용됩니다. 단지 "확인하고 계속 진행"하는 경우에는 milestone을 사용하세요.
  4. 시간대는 가능한 한 자료에서 가져오세요. 임의로 만들지 마세요. 자료에 없으면 비워두고 추측하지 마세요.
  5. 저작권/컴플라이언스/레드라인 노드는 명시적으로 그리고 note에 작성해야 합니다. 그렇지 않으면 차트가 보기 좋아도 실제로는 위험을 초래할 수 있습니다.
  6. 긴 체인에는 가로 LR을 사용하지 마세요:13단계 체인을 LR로 만들면 1980×489의 4:1 긴 막대가 되어, 1100px 너비의 캔버스에서 맞추면 0.55배로 축소되어 글자가 모두 뭉개집니다. 반면 다중 열 분할(가로형)은 동일한 차트를 약 1.6:1로 압축하여 한 화면에 표시하면서도 글자를 축소하지 않습니다. 경험:체인 프로세스를 가로로 표시하려면 → 다중 열 분할을 사용하고, LR을 사용하지 마세요.
  7. 두 레이아웃 선택 방법:휴대폰에서는 세로형(한 열로 아래로 스크롤, 세로 화면 직관에 적합). 컴퓨터에서는 가로형(다중 열 분할, 한 화면 전체 보기). autoVariant()의 임계값은 너비<760px 또는 가로세로비<1.08 → 세로형입니다.
  8. 다중 열의 읽기 방향은 여전히 아래 방향:지그재그 형태(위아래 교대)는 사용하지 않습니다. 역방향 열은 화살표 방향을 혼란스럽게 만들기 때문입니다. 열 간 연결선은 열 사이의 공백(행에 노드가 하나만 있는 경우)또는 하단 통로(행에 여러 노드가 있는 경우)를 통해 이동하며, 노드를 가로지르지 않습니다.
  9. 그룹이 열을 넘을 때는 자동으로 여러 세그먼트로 분할되고 「(계속)」이 표시되어, 관련 없는 노드를 포함하는 사각형을 방지합니다.

六、검증 기록(2026-10-04)

  • 입력 형식:JSON / Mermaid 서브셋 / .md의 ```mermaid 코드 블록, 세 가지 경로 모두 실제 테스트 통과.
  • Mermaid 왕복:파싱 → 재내보내기 .mmd, 판단 노드 {}, 점선 -.->, 엣지 레이블 -->|있음|, subgraph 그룹 모두 올바르게 보존됨.
  • 두 레이아웃(13개 노드 / 13개 연결 / 3개 그룹의 예제 프로세스):세로형 533×1936(단일 열 12행). 가로형 1119×710(자동 3열 × 4행, 가로세로비 1.58, 한 화면 표시). 7개 노드 예제 프로세스:세로형 533×890, 가로형 806×459(자동 2열).
  • 자동 레이아웃 선택(CDP 장치 시뮬레이션 실제 측정):390×844 → show=portrait. 1440×820 → show=landscape.
  • 휴대폰 버전 레이아웃(390×844 실제 측정 높이):canvas 364 / 개요 패널 253 / 페이지 하단 81, 개요 콘텐츠 완전 렌더링(panelLen 1533).
  • 터치 제스처(CDP 합성 TouchEvent 실제 측정):한 손가락 드래그 이동 ✓, 두 손가락 확대/축소 ✓, 노드 살짝 눌러 선택 및 「← 개요로 돌아가기 | s | T일 12:00 작업 시작 | 시작점 | 완료됨」 팝업 ✓. 마우스 경로도 동일하게 세 가지 항목 통과.
  • 상호 작용:노드 클릭 → 세부 정보 카드 + 업스트림/다운스트림 강조 + 나머지 어두워짐. 범례 필터링은 data-hide-status에 기록. 검색 일치 개수. +/0 키 확대/축소 및 맞추기로 100% 복귀. 밝음/어두움 테마 전환. 두 레이아웃에서 선택/검색/필터 상태 유지.
  • 로딩 중 예외 검사(Runtime.exceptionThrown):두 샘플 페이지 모두 0 예외.
  • 개선 버전:카드형 노드(상태 색상 세로 막대 + 부드러운 그림자), 유형 배지, 시간 알약, 둥근 모서리 직각 우회로, 캡슐 엣지 레이블, 그룹 밴드 레이블 캡슐, 오른쪽 패널 프로세스 개요(개수 + 상태 필터 + 주의 필요 목록), 점자 바탕 무늬, 아이콘 도구 모음.

겪었던 문제점(모두 코드에서 수정 완료, 기록해두면 다음에 시간을 절약할 수 있음)

  1. Mermaid 판단 노드가 적용되지 않음:touch()는 shape 필드를 작성하는데, normalize()는 kind를 읽음 —— 필드명이 일치하지 않아 마름모가 모두 직사각형으로 그려짐.
  2. 레이블 따옴표가 사라짐:라벨 "진짜 문제 개작"이 라벨 "진짜 문제 개작이 됨. 이유는 조건 없이 처음과 끝의 따옴표를 제거했기 때문. 처음과 끝의 따옴표가 같을 때만 제거하도록 수정.
  3. 확대/축소 비율이 실제와 일치하지 않음:SVG viewBox 자체가 meet 확대/축소를 한 번 수행하므로, 원래의 transform이 그 위에 중첩되어 「100%」가 실제로 26%로 표시됨. 요소 픽셀 공간에서 view{K,TX,TY}를 통일적으로 계산한 후 다시 해석하도록 수정.
  4. PowerShell Set-Content가 UTF-8 BOM을 추가하여 #!/usr/bin/env node를 손상시켜 SyntaxError를 발생시킴. 이 스크립트를 수정할 때는 shell을 사용하여 파일을 쓰지 마세요.
  5. b.members가 정의되지 않음:그룹 밴드 객체에 members가 누락되어, 레이블이 있는 차트가 바로 충돌.
  6. fit 전체 화면이 긴 플로우차트를 26%로 축소:"전체 화면 맞춤과 너비 채움의 차이가 1.8배 미만이면 전체 화면 맞춤, 그렇지 않으면 너비 채움 + 상단 정렬"로 수정.
  7. 마름모에 글자가 눌림:판단 노드를 내접 사각형 조건으로 크기를 다시 계산한 후, 문법인가 의미론 문제인가?가 한 줄에 들어갈 수 있게 됨.
  8. 두 레이아웃 공존 시 id 충돌:renderSvg의 <g id="vp">에 버전 접미사가 없어, 클라이언트 getElementById('vp-landscape')가 null을 반환 → paint() 예외 발생, 초기화 전체가 중단되고 개요 패널이 비어짐(현상은 aside가 25px만 남음). 두 레이아웃의 모든 id(defs/filter/pattern/marker/vp)에는 -${vid} 접미사가 반드시 필요함.
  9. wrap 매개변수 의미가 반대로 작성됨:이는 "각 열의 행 수"를 나타내며, 줄바꿈 없음은 R = L이어야 하는데 R = 1로 작성됨(R=1은 각 레이어가 한 열인 가로 막대가 되어, 세로형이 한때 3476×101로 배치됨).
  10. CDP 예외 재생이 속일 수 있음:Runtime.enable 후 이전 문서의 예외도 함께 전송됨. "페이지가 실제로 오류를 발생시켰는지" 판단하기 전에 먼저 Page.navigate를 about:blank로 이동한 후 대상 페이지로 들어가야 함. 그렇지 않으면 이전 페이지의 오류를 새 페이지의 오류로 계산함.
  11. 살짝 누름 선택 검증은 먼저 맞추기를 수행해야 함:제스처 조작 후 대상 노드가 이미 보이는 영역 밖으로 이동했을 수 있으며, elementFromPoint가 자연스럽게 노드를 찾지 못함——이는 테스트 스크립트의 문제이지 페이지의 문제가 아님.
  12. CDP 검증이 사용자의 탭을 빼앗음:webctl shot / 첫 번째 page target을 직접 가져오면, 모두 사용자가 보고 있는 탭을 이동시킴. 사용자가 동시에 브라우저를 사용하는 경우, 서로 방해함(실제 측정:내가 이동시켰지만, 사용자가 검색하면 다시 돌아와서 location이 수십 초마다 인턴 웹사이트로 변경됨). tools/cdp.mjs의 withOwnTab()으로 수정——자체적으로 Target.createTarget으로 임시 탭을 만들고, /json/list에 나타날 때까지 폴링, 사용 후 Target.closeTarget으로 닫음. 그럼에도 불구하고 새 탭은 여전히 포커스를 빼앗음. 사용자가 브라우저를 사용 중일 때는 먼저 물어봐야 함.
  13. 검색에서 "추가로" 일치하는 것은 버그가 아님:입을 검색하면 2개가 일치하는 이유는 설명/비고("입구 보정")와 id(노드 id j)를 동시에 검색하기 때문. 수정 방법은 숫자를 변경하는 것이 아니라, 보이지 않는 일치 항목을 표시하는 것(주황색 점선 원 + 수준별 개수 + 일치 목록).
  14. 백그라운드 탭은 스크린샷을 찍을 수 없음:Target.createTarget({background:true})은 사용자 포커스를 빼앗지 않지만, 백그라운드 탭은 프레임을 생성하지 않아 Page.captureScreenshot이 영원히 대기함(실제 측정 120초 타임아웃). 스크린샷을 찍으려면 포그라운드 탭을 사용해야 함(포커스를 빼앗음). 백그라운드 탭은 순수한 기하학적 측정에만 적합함(getBoundingClientRect / getComputedStyle은 정상 작동). 또한 백그라운드 탭의 CSS 트랜지션은 진행되지 않음——측정 전에 먼저 *{transition:none!important}를 주입해야 하며, 그렇지 않으면 애니메이션 시작점을 측정하게 됨(서랍의 rect.x=398을 "열리지 않음"으로 잘못 판단했지만, 실제로는 스로틀링이었음).
  15. "컴퓨터용" 산출물에도 "휴대폰에서 여기 클릭"이 표시됨:파일 목록의 배지는 확장자만 봄. 이제 【/_로 시작하는 파일은 별도로 「컴퓨터용, 휴대폰에서는 클릭하지 마세요」로 표시하고 회색 처리함——판단 조건은 표시 문구와 함께 변경되어야 함.
  16. 검증 산출물을 서비스 중인 디렉토리에 배치하지 마세요:shot-*.png, 프로브 스크립트가 휴대폰의 파일 목록에 나타남. 모두 서비스되지 않는 디렉토리(예:out/_shots/)에 통일하여 배치하고, serve.mjs가 서비스하는 산출물 디렉토리에 넣지 마세요.
  17. 서랍 목록이 손가락 아래에서 재정렬되면 = 잘못된 항목 클릭:페이지 내장 스냅샷은 원래 "현재 페이지가 가장 먼저"였지만, fetch('프로세스_index.json')가 돌아올 때는 mtime 순서로 정렬됨 —— 서랍을 연 후 약 100ms 후에 목록이 한 번 변경되어, 사용자가 클릭한 항목이 의도한 항목이 아닐 수 있으며, "클릭해도 반응 없음 / 돌아갈 수 없음"으로 나타남. 수정 방법 두 가지:목록을 파일명 순서로 고정 정렬(스냅샷과 목록 순서가 항상 일치), 현재 페이지를 비링크로 렌더링. 교훈:"열린 후 비동기로 새로고침"되는 모든 목록은 순서가 안정적이어야 함.
  18. serve.mjs를 수정한 후 서비스를 다시 시작해야 함:페이지 템플릿은 프로세스 메모리에 있으므로, 다시 시작하지 않으면 계속 이전 버전임(이 때문에 "홈페이지에 h1이 없다"고 한동안 헤맸음).
  19. 디렉토리 페이지에 cache-control이 누락되면 사용자가 영원히 이전 홈페이지를 보게 됨:사용자가 두 번 "컴퓨터 버전 홈페이지가 너무 단순한 것 같다/썸네일이 없다"고 말했지만, 실제로는 이미 카드+썸네일로 변경했으나 브라우저가 계속 캐시된 이전 응답을 사용했기 때문. 동적으로 생성된 HTML은 반드시 no-store여야 함.
  20. Start-Process로 분리하여 시작한 서비스는 명령이 끝나면 같이 종료됨:샌드박스는 명령이 끝날 때 프로세스 트리를 정리하므로 서비스가 조용히 사라짐(다음 요청 시 "원격 서버에 연결할 수 없습니다" 오류 발생). harness 백그라운드 작업을 사용하거나, 사용자가 직접 터미널에서 실행하도록 하세요.
  21. 디렉토리에서 이름이 같거나 비슷한 JSON을 입력으로 사용하지 마세요:디렉토리에 갑_프로세스.json이 있는데, 이름은 갑 프로세스처럼 보이지만 내용은 을 프로세스의 사양임. 이를 render하면 기존 갑_프로세스.html을 다른 내용으로 덮어씀. 렌더링 전에 JSON의 title/노드 수를 먼저 확인하고, 산출물과 동일한 이름의 입력을 우선 사용하세요.
  22. 중국어 주석에 곧은 따옴표가 있으면 JS 문자열의 큰따옴표를 자를 수 있음(" // 이야기하는 것은 "순수 파일명" 목록" → 조기 종료). 동일한 문제를 세 번 겪음:중국어 주석에서는 따옴표를 사용하지 않거나 「」를 사용하세요.
  23. file://에서는 "디렉토리 홈페이지"가 없음:href="./"는 로컬에서 브라우저의 순수 파일명 목록으로 이동하여, 사용자에게 "파일 이름만 표시"됨. 동적으로 생성된 디렉토리 페이지는 별도의 정적 index.html을 작성해야 하며, 홈페이지 버튼이 프로토콜에 따라 대상을 전환하도록 해야 함.
  24. 디버그 브라우저가 없을 때 스크린샷을 찍는 방법:샌드박스에서 Edge가 실행되지 않지만(mojo 명명된 파이프가 거부됨), msedge --headless=new --screenshot=... --window-size=W,H <url> 일회성 렌더링으로 파일 쓰기는 가능함(일회성 제한 없는 권한 필요). file:// 로컬 페이지도 촬영할 수 있고, 크기에 따라 휴대폰/컴퓨터도 시뮬레이션 가능하며, "휴대폰 첫 화면에서 카드가 보이지 않음" "요약이 0으로 계산됨"을 찾아내는 데 핵심적인 역할을 함.
  25. 템플릿 문자열에서 정규식을 작성할 때는 이중 백슬래시를 사용해야 함:\d/\s는 템플릿 문자열에서 d/s로 퇴화되어, 페이지에 생성된 정규식이 무효화됨(요약이 한때 "총 0단계"로 표시됨). 페이지 스크립트에 작성된 정규식은 통일적으로 \\d、\\s를 사용하세요.
  26. 문구를 기기에 관계없이 하드코딩하지 마세요:컴퓨터 홈페이지에 「휴대폰에서 여기 클릭」이라고 쓰거나, 휴대폰 플로우차트에서 「스크롤 확대/축소/더블 클릭/Esc」를 가르치는 것은 모두 "기기 불일치" 문제임. 해결 방법은 동일한 위치에 두 세트를 작성하는 것(.only-pc / .only-touch)+ @media (hover:none),(pointer:coarse) 전환 —— 입력 방식으로 판단하는 것이 화면 너비로 판단하는 것보다 더 정확함(가로 모드 휴대폰, 좁은 창 컴퓨터 모두 정확함).
  27. 동일한 디렉토리에 동일한 이름의 플로우차트가 있으면 카드가 똑같이 보임:xxx - 복사본.html과 xxx.html은 제목이 동일하여 사용자가 구분하지 못함. 제목이 중복되면 파일명을 한 줄 추가로 표시하고, 썸네일도 "공백 제거 및 -가로형/-세로형 제거" 정규화된 이름으로 매칭해야 함.
  28. 템플릿 문자열의 \n은 실제 줄바꿈이 됨:homeScript() 같은 템플릿에 작성된 페이지 JS에서 '...\n\n' + x는 페이지에서 문자열 리터럴에 실제 줄바꿈이 포함된 형태가 되어 → 페이지 스크립트 구문 오류 발생(node --check로 serve.mjs를 검사해도 발견되지 않음). \\n을 사용해야 함. 검증 방법:buildHome()이 생성한 두 <script>를 추출하여 각각 node --check로 검사(이 회귀 단계를 추가함).
  29. "내용 동일" 여부는 의미론적으로 판단해야 하며, 파일 해시로 판단하지 마세요:전체 페이지 해시는 당연히 다름(렌더링 시간 등이 포함되므로). 이로 인해 동일한 프로세스를 여러 번 렌더링한 파일이 "내용이 다른 중복 파일"로 잘못 처리되어 원본이 손실될 수 있음. 수정 방법은 프로세스 의미론적 지문을 사용하는 것임.
  30. "전체 플로우차트" 서랍의 fetch가 실패해도 페이지가 중단되지 않아야 함:서랍을 열 때 fetch가 실패하면(네트워크 연결 끊김 등)→ 내장 스냅샷을 사용하면 되지만, 스냅샷이 목록의 순서를 보장하지 않음. 수정:fetch 실패 시 서랍을 닫고 사용자에게 "목록을 불러오지 못했습니다. 로컬 모드로 전환"을 알림.
  31. 코드 완성 검증 시 "이미지가 보이지 않음"은 대부분 서비스가 실행되지 않았거나 포트가 잘못되었기 때문:기억하세요:서비스와 render는 서로 다른 터미널 창입니다.
  32. 실제 사례 (2026-10-04):사용자가 "프로세스 그려줘"라고 말함, 13단계 제품 체인. 내부 토큰 마감(SVL)이 하이라이트 키로 표시되고, 수동 검토 지점이 risk로 표시됨. 두 레이아웃 모두 통과, 자동 선택이 올바름. 사용자가 "이 파일을 보내줘"라고 함. serve.mjs를 시작하여 휴대폰에서 접속, 휴대폰 Safari로 테스트. 검색 아이콘 입력이 f를 트리거하지 않음(실제 수정:input[type=search]가 포커스될 때 자동으로 f를 트리거하지 않도록 방지하기 위해 한 번에 30ms 지연 추가). 수동 입력은 정상. 사용자가 "가져가기"라고 함. 완료.

추가 참고 사항

  • Mermaid graph TB의 등호는 ===가 아닌 실선 ===입니다.```
  • Mermaid 스타일은 style id fill:#xxx,stroke:#xxx
  • Mermaid 하위 그래프는 subgraph title...end
  • JSON 사양 예제는 references/spec.md의 ## JSON 사양 섹션을 참조하거나, sample을 실행하여 직접 확인하세요

관련 스킬