연결선(edge) 하나를 표현하는 방법을 모은 참고 라이브러리 — 모양 · 질감 · 라우팅 · 움직임 · 빛. 항목마다 강조색을 바꿔 보고 SVG · PNG · GIF · HTML로 꺼내 쓸 수 있습니다. 다크 모드로 보면 그대로 다크로 내보내집니다.
경로 모양 12 + 의미 표현 11 + 획 질감 4. 앞의 둘은 조합해서 쓰고, 질감은 문서 전체에 하나만 정합니다.
가장 짧고 그리기 쉬움. 노드가 적을 때만 — 많아지면 선끼리 겹쳐 금방 지저분해짐.
공학 도면의 정돈된 인상. 아키텍처 다이어그램의 표준. 겹치는 경로 관리가 필요.
엘보의 질서 + 부드러운 인상. 요즘 도구들의 기본값(FigJam, Mermaid 등). 무난한 1순위.
유기적인 흐름 강조. 마인드맵·데이터 흐름도에 어울림. 격자 정렬 느낌은 약해짐.
제어점은 감이 아니라 규칙으로 잡습니다 — 현의 중점에서 수직으로 현 길이의 10~20%.
그리고 제어점 둘은 현의 같은 쪽에 둬야 합니다(C든 Q든 하나만) —
곡률이 한 방향으로만 가고 변곡점이 안 생겨야 "휘어 간다"로 읽혀요.
S자(꺾임 둘)는 진짜 장애물을 피할 때만 쓰고 기본 모양으로 삼지 않습니다.
테두리를 얕은 각도로 스치는 곡선도 피하세요 — 지나가는 건지 붙은 건지 안 읽힙니다.
잭에서 나가는 접선이 늘 아래를 향합니다 — 상대가 위에 있어도 케이블은 먼저 처졌다가 올라가요. 같은 베지어 명령인데 곡선(bezier) 카드와 제어점 규칙이 다릅니다. 곡선은 두 상자를 잇는 선이라 접선이 진행 방향을 따르고, 이건 두 잭을 잇는 물건이라 접선이 중력을 따릅니다. 끝은 화살촉이 아니라 굵기×0.95 지름의 플러그 점. 패치 보드 · 배전반 · 모듈 구성도처럼 "꽂혀 있다"를 말할 때.
두 노드 사이의 왕복을 화살표 두 개가 아니라 한 획의 폐곡선(스타디움)으로. 대시가 돌면 방향이 보이고, 멈추면 "멈춘 순환"이 됩니다. 라우팅의 ⑤ 자기 참조 루프가 노드 하나의 재귀라면 이건 둘 사이의 끝없는 순환이고, 획이 하나라 "끊기지 않음"이 형태로 보여요. 복제 로그 · 큐 소비 · 폴링 루프. 다섯 중 가장 약한 후보라 둘의 차이가 안 보이면 빼도 됩니다.
우리 "둥근 엘보"는 Q 베지어라 모서리 반지름이 일정하지 않습니다 — 90° 모서리에서 참 원보다
최대 6.07% 바깥으로 부풀어요. 화면용 다이어그램이면 아무도 모르지만, 도면처럼 읽히거나 R값을 치수로 적는 그림이면
A r r 0 0 1 x y로 진짜 원호를 써야 합니다. 굳이 베지어를 써야 하면 3차 + 핸들 0.5523×R이 오차 0.03%로 사실상 같습니다.
같이 지킬 것 둘 — 반지름 종류는 문서 전체에 1~2개, 그리고 모든 접합은 의도한 모서리이거나 접선 연속이어야 합니다(둘 다 아니면 그건 실수).
지금 경로 축은 직선 · 직각 · 둥근 · 곡선 · 참 원호 5종인데, 대각선이 없습니다. 지하철 노선도와 PCB 아트워크가 공통으로 쓰는 규칙이 이거예요 — 선은 가로 · 세로 · 45° 세 방향만. 각도 종류를 3개로 못 박으니 밀집해도 눈이 안 지칩니다.
둘은 성격이 다릅니다. 직행은 꺾임을 1회로 줄이는 최단 경로(노선도용), 모따기는 직각을 그대로 두고 모서리만 8px 깎는 것(회로도·기존 도면에 섞어 쓰기용). 둥근 엘보와 달리 격자에 딱 맞아서 여러 선을 나란히 놓기 쉬워요.
선을 30°로 그리는 게 아닙니다. 평범한 정사각 격자 위에서 A*로 경로를 찾고(가로·세로·대각 8방향),
다 끝난 뒤 매트릭스 하나를 딱 한 번 먹입니다 — matrix(.707, −.409, .707, .409, 0, 0).
각도를 손으로 계산하는 코드가 아예 없어요.
이 매트릭스가 좋은 이유는 네 이동이 정확히 네 각도로 떨어지기 때문입니다 — 타일 +x는 −30°, +y는 +30°, 대각 둘은 0°와 90°. 즉 화면에 나올 수 있는 각도가 넷뿐이라 45° 옥틸리니어와 같은 종류의 규율이 공짜로 따라옵니다. 라우팅 로직은 2D 그대로 재사용하고, 투영만 갈아끼우면 같은 다이어그램을 평면과 아이소메트릭 양쪽으로 뽑을 수 있어요.
라벨은 같은 매트릭스에 넣으면 안 됩니다. 이 행렬에는 전단(skew)이 들어 있어서, 글자를 함께 투영하면
기울고 늘어나 읽을 수 없게 돼요. 원본 구현도 라벨을 변환 밖의 별도 레이어에 그립니다 —
경로의 가운데 타일을 투영해 화면 좌표만 얻고, 글자 자체는 정면으로 세웁니다.
CSS 모션 패스 카드의 offset-rotate: 0deg와 같은 원칙이에요 — 모양은 변환을 따라가고, 글자는 따라가지 않는다.
그래도 이건 포기해야 합니다 — 깊이가 생기니까 선이 노드 뒤로 지나갈 수 있고, 그러면 z-순서를 타일 좌표로 정렬해야 합니다. 케이싱이나 교차 홉으로는 해결이 안 돼요. 인프라·랙 구성도처럼 공간 배치 자체가 정보일 때만 값을 합니다.
지금 직각 엘보는 항상 중간에서 꺾습니다. 그런데 어디서 꺾느냐가 밀집도를 바꿔요.
표준 시각화 라이브러리는 curveStep(중간) · curveStepBefore · curveStepAfter 세 개로,
다른 그래프 라이브러리는 taxi-turn 하나의 숫자로 노출합니다. 그래프 엔진의 curvePosition도 같은 값이에요.
고르는 기준은 하나 — 가로줄과 세로줄 중 어느 쪽을 겹치게 만들 것인가. 출발 쪽으로 밀면 세로줄이 한 곳에 모이고 가로줄이 흩어져 부챗살이 되고, 도착 쪽으로 밀면 가로줄이 길게 나란히 붙어 빗처럼 됩니다. 트리·조직도처럼 도착지가 여럿이면 도착 쪽이 훨씬 정돈돼 보입니다. 중간 50%는 그 둘의 최악을 반씩 가져가는 값이라, 기본값으로는 나쁜 선택이에요.
지금 곡선 카드는 "베지어"라고만 되어 있고 어느 축으로 휘는지가 없습니다. 그런데 이건 취향이 아니라 규칙이에요 — 양 끝의 접선 방향은 레이아웃이 흐르는 축과 같아야 합니다.
한쪽은 curveBumpX/curveBumpY로, 다른 쪽은 cubic-horizontal/cubic-vertical로
똑같은 쌍을 따로 내놓은 게 근거입니다. 두 라이브러리가 독립적으로 같은 구분을 노출했다는 건 실무에서 계속 틀린다는 뜻이에요.
오른쪽처럼 어긋나면 선이 노드 옆구리에서 출발해 가로로 한 번 튀었다가 내려가서, 부모-자식 관계가 아니라 우회로처럼 보입니다.
둘 다 노드가 원 위에 있을 때만 성립하는 선입니다. 왼쪽 방사형 링크는 제어점을 "각도는 양 끝 그대로, 반지름만 두 링의 중간"으로 잡습니다 — 그래서 선이 항상 중심을 향해 휘고, 링 사이 간격이 눈에 보여요. 직교좌표 베지어로 그리면 이 성질이 깨집니다.
오른쪽 코드 리본은 정량 리본(Sankey)의 원형판인데 다른 게 하나 있어요 — ribbonArrow는
도착 쪽만 안으로 들여서 화살촉을 만듭니다. 그래서 같은 그림 안에서 "A→B 얼마"와 "A↔B 얼마"를 섞어 표현할 수 있습니다(초록은 화살 없는 양방향).
솔직히 조건부예요 — 아키텍처 다이어그램엔 못 쓰고, 닫힌 집합의 교환량(트래픽 매트릭스·이동·호출 통계)일 때만 맞습니다.
"실선과 성질이 다르다"는 신호 — 비동기, 선택적, 참조. 실선과 짝을 이룰 때만 의미가 생김.
양·중요도를 싣는 축. 즉시 구분되는 건 2단계(가늘게/굵게)까지 — 3단계부터는 애매해짐.
계열·상태 구분. 3색 이하 + 중립색 1개. 색맹 대비로 색에만 의존하지 말고 점선 등과 병행. 색에는 두 가지 다른 일이 있습니다 — 강조는 "이걸 먼저 봐라"라서 반드시 1개(둘이면 초점이 사라짐), 분류는 "이것들은 서로 다른 종류다"라서 색이 곧 이름이고 3~5개까지 됩니다. 둘을 한 그림에 섞으면 독자가 어느 쪽인지 몰라요. 분류색을 썼으면 강조는 굵기나 움직임으로 하세요.
"지금 흐르는 경로"를 보여줌. 화면 전용(인쇄 불가). 변형들은 아래 04 섹션에서 다양한 움직임 비교.
같은 그룹의 "굵기"는 굵다/가늘다 두 단계의 질적 구분일 뿐입니다. 이건 폭이 값 그 자체이고, 갈라질 때 폭의 합이 보존돼요. 선이 아니라 면이 되는 순간이라 새 등급입니다. 트래픽·예산·전환율·물량 배분에. 지킬 규칙은 하나 — 입력 폭 = 출력 폭 합. 눈대중으로 그리면 데이터가 아니라 삽화가 됩니다.
아키텍처 초안에서 제일 자주 필요한데 표기법이 없는 것 — 이 연결이 확정인지 추정인지 아직 모르는지.
점선은 이미 "비동기"로 예산을 써버렸으니 다른 축이 필요합니다. 물결선은 "대충 이렇게 갈 것",
그라데이션으로 끝을 지우면 "여기까지만 정해졌음". 촉이 없는 게 핵심 — 도착지가 없다는 뜻이니까. 구현 함정 둘: 그라데이션은 style=로 줘야 클래스 stroke를 이기고, 가로선은 bbox 높이가 0이라 gradientUnits="userSpaceOnUse"가 아니면 아예 안 그려집니다.
"색에만 의존하지 말라"는 말은 옆 카드에 이미 있는데 대안이 없었습니다. 실제로 서로 구분되는 대시 패턴 5종:
실선 · 10 6 · 2 3 · 16 4 3 4 · 8 3 8 9.
핵심은 주기와 리듬을 둘 다 다르게 하는 것 — 8 4와 9 5처럼 길이만 다르면 두 개 아니라 하나로 보입니다.
대시 값을 고정 숫자로 박아 두면 굵기를 바꾸는 순간 스타일이 바뀝니다. 4 4짜리 점선을 5px로 올리면
칸이 메워져서 거의 실선으로 읽혀요. 대시는 절대값이 아니라 굵기의 배수로 잡아야 합니다 —
대시 = 굵기×2, 굵기×2.
점(dotted)은 별개 스타일입니다. 짧은 대시로 흉내내지 말고 stroke-dasharray: 0, 굵기×1.8에
stroke-linecap: round를 겁니다 — 길이 0짜리 선분이 둥근 캡 때문에 완전한 원으로 찍혀요.
지금 컬렉션의 점선·색맹 패턴은 전부 대시라서 이 모양이 없었습니다. 실선 · 대시 · 점 셋이면
색 없이도 세 등급을 만들 수 있습니다.
대시 스케일 카드가 "굵어질 때"를 다뤘는데, 반대쪽은 대칭이 아닙니다 — 키울 때는 같이 늘리면 되지만, 줄일 때는 같이 줄이면 안 됩니다. 규칙은 줄이지 말고 빼라예요. 점 3개를 각각 1/4로 줄이면 셋 다 안 보이는데, 하나만 남기고 그 하나를 상대적으로 키우면 "선 위를 뭔가 지나간다"는 남습니다.
이걸 제대로 하는 곳은 큰 크기와 작은 크기를 배율이 아니라 별도 설계로 잡습니다 —
작은 쪽은 요소 수를 1/4로 줄이고 남은 요소의 반지름 배율을 1.5~2.5배로 올려요.
반지름 자체도 크기에 선형이 아니라 (크기 ÷ 300)0.6로 줄입니다 —
지수를 1보다 작게 두는 게 핵심이고, 그래야 작아질수록 마크가 상대적으로 커집니다.
2차원 격자처럼 개수가 쌍으로 오는 건 각 변에 √배율씩 나눠 주고요.
굵기만은 공식이 아니라 하한으로 막습니다 — 계산 결과가 얼마든 max(0.6, 계산값).
이유는 재 보면 나옵니다. 1배 화면에서 #d6453d 선을 굵기만 바꿔 그리면
1.8px는 rgb(214,69,61) 원색 그대로인데, 0.6px는 rgb(241,196,193),
0.35px는 rgb(245,219,216) — 거의 배경색입니다. 안 사라지고 연해지는 게 함정이에요.
선은 남아 있는데 색으로 실어둔 의미가 사라집니다.
같은 측정에서 하나 더 — 1배 화면의 1px 선은 픽셀 격자에 어디 걸리느냐로 색이 달라집니다.
중심이 반픽셀(y = 38.5)이면 원색인데 정수(y = 38)면 두 줄에 반씩 걸려
rgb(233,159,154)로 옅어져요. 가는 선은 좌표를 x.5에 두는 게 정석입니다.
2배 화면에서는 1px까지 안전하고, 0.6px부터 같은 현상이 시작됩니다.
실무에서 이게 걸리는 곳은 셋 — 썸네일, 인쇄 축소, 확대·축소되는 캔버스.
마지막 경우는 vector-effect:non-scaling-stroke로 굵기만 화면 좌표에 고정하는 게 답이지만,
대시 간격과 점 개수는 여전히 사람이 따로 정해줘야 합니다 — 그건 자동으로 안 따라옵니다.
출발점에서 두껍고 도착점에서 0으로 얇아지는 삼각형. 화살촉 없이 선 전체가 방향이 됩니다.
화살촉은 정보가 끝점 6px에만 몰려 있어서, 선이 겹치면 어느 촉이 어느 선 것인지 잃어버려요.
테이퍼는 아무 데나 잘라 봐도 굵은 쪽이 출발입니다. 주요 그래프 라이브러리가 straight-triangle이라는 이름으로 기본 제공합니다.
단, 근거가 갈립니다. 노드-링크 그래프 사용자 연구에서는 테이퍼가 화살촉보다 빨랐지만, 지도학 쪽 흐름도 연구는 반대로 "화살촉이 테이퍼보다 방향을 잘 나타낸다"고 결론냅니다 — 길이가 길고 굵기가 제각각인 흐름에서는 테이퍼의 굵기가 방향인지 수량인지 헷갈리기 때문. 짧고 굵기가 균일한 그래프용으로 쓰세요.
같은 경로를 굵게 한 번(6.5px), 배경색으로 가늘게 한 번(3px) 덮으면 속이 빈 관이 됩니다. P&ID·배선도에서 "이건 신호가 아니라 물리적 통로"를 뜻하는 오래된 관행이에요.
넣을 값어치는 굵기와 의미가 안 겹친다는 데 있습니다. 굵기는 이미 양을 맡고 있어서, "전용선 / VPN 터널 / 물리 케이블"처럼 종류가 다른 연결을 굵기로 표시하면 값과 충돌해요. 이중선은 굵기 축을 비워둔 채 종류만 바꿉니다. 색맹 안전 패턴 세트와 같은 계열의 도구.
같은 선을 2번 겹쳐 긋고 양 끝·제어점을 2px 안에서 흔듦(roughness 1, bowing 1). 화살촉만 흔들림을 0.6으로 낮춰야 방향이 안 뭉갬. 시드를 고정해야 다시 그려도 같은 모양 — 안 하면 매 렌더마다 손떨림이 달라집니다. 문서 전체에 하나만: 정밀과 섞으면 미완성으로 보임.
경로를 다시 계산하는 손그림 알고리즘 방식과 달리 필터 한 줄로 도형 전체를 흔듭니다(baseFrequency .02 / scale 1.5~2.4). 대신 글자에 걸면 못 읽게 되니 텍스트는 반드시 필터 그룹 밖에. 지오메트리가 안 바뀌어 정렬은 유지되지만, 시드가 지오메트리에 안 박혀 렌더러마다 미세하게 달라집니다.
이 컬렉션에도 필터 쓰는 카드가 셋 있습니다 — 손그림, 구이 브리지, 발광. 그런데
필터를 연결선에 걸 때만 나오는 함정이 하나 있어요. SVG 필터 영역의 기본 단위는
objectBoundingBox인데, 수평선의 바운딩 박스는 높이가 0입니다 — 획 굵기는 박스에 안 들어가거든요.
그러면 height="120%"도 0의 120%라 0이 되고, 크롬은 그 획을 아예 안 그립니다.
직접 재 봤습니다. 굵기 3px 수평선 하나에 x="-10%" y="-10%" width="120%" height="120%"짜리
흐림 필터를 걸면 잉크 0. 같은 선에 filterUnits="userSpaceOnUse"와 명시 영역을 주면 9321,
필터 없는 기준이 5760이니 정상 쪽은 번짐까지 제대로 얹힌 값입니다.
완전히 같은 선상에 놓인 선 두 개를 <g>로 묶어도 결과는 똑같이 0이에요 —
묶어서 해결되는 게 아니라 박스가 2차원이 되어야 삽니다.
고치는 법은 셋 — ① 그 획에는 필터를 안 건다, ② filterUnits="userSpaceOnUse"에 명시 영역을 준다,
③ 2차원 도형과 함께 묶는다. 셋째는 우연히 되는 것이라 규칙으로 삼으면 안 됩니다 —
나중에 누가 그 그룹에서 사각형 하나를 빼면 선이 통째로 사라지니까요.
퍼센트 영역을 쓰더라도 여백이 번짐 반경보다 커야 합니다. stdDeviation이 6이면
눈에 보이는 번짐은 대략 3σ = 18px인데, 기본값 −10%는 높이 44px 상자에서 4.4px밖에 안 돼요.
이 파일도 점검했습니다 — 발광 필터 둘은 처음부터 userSpaceOnUse라 안전했고,
손그림·구이는 내용이 2차원이라 영역을 전체 캔버스로 넓혀 다시 렌더해도 픽셀 차이 0이었습니다.
운이 좋았던 게 아니라 확인한 결과로 적어 둡니다.
같은 경로를 살짝 밀어 어둡고 옅게 한 번 더 그린 뒤 본선을 얹습니다. 선이 판 위에 떠 보이고, 교차에서 어느 선이 위인지를 그림자가 말해 줘요 — 위 그림에선 빨강이 파랑 위. 라우팅의 ⑰ 케이싱이 바탕색 테두리로 틈을 만들어 아래 선을 지운다면, 이건 어둠으로 깊이를 만들어 아래 선을 남깁니다. 겹침 자체가 정보일 때(케이블 다발 · 배선) 케이싱보다 덜 잃어요. 질감이라 문서 전체에 한 번 정합니다 — 어떤 선엔 있고 어떤 선엔 없으면 위아래가 거짓말이 됩니다.
기법이라기보다 마감. 화살촉은 한 다이어그램에 1종(+무방향)이면 충분하고, 라벨과 주석에는 각자 규칙이 있습니다.
노드를 잇는 선이 아니라 한 지점을 가리키는 선. 흐름 화살표와 헷갈리면 안 되니 세 가지를 반드시 다르게 — 화살촉 대신 착지 점, 실선 대신 점선(4,3), 산세리프 대신 이탤릭 세리프. 여백에만, 한 장에 최대 2개.
선 위 라벨은 배경색 사각형을 깔고 그 위에 글자를 얹습니다(좌우 여백 6px) — 반투명은 안 됩니다. 그리고 선을 전부 그린 뒤 노드를 그리면 선 끝을 정확히 맞출 필요가 없어집니다. 노드 채움이 알아서 잘라주니까요.
"선 먼저, 노드 나중"을 네 층으로 확장하면 다이어그램 전체의 규칙이 됩니다 — 캔버스 → 컨테이너 배경·테두리 → 연결선과 마커 → 노드 표면 → 아이콘·글자·라벨·범례. 여기서 미묘한 부분 하나. 선이 밑으로 지나가야 하는 노드는 자기 끝점의 노드뿐입니다. 무관한 노드 밑을 지나가는 건 가려진 것이고, 그건 페인트 순서 문제가 아니라 라우팅 실패예요 (⑱ 참조 — "그 선은 기하학적으로는 존재하는데 끊어진 것으로 읽힙니다").
그래서 가려짐은 기하와 따로 검사해야 합니다. 경로가 노드를 안 뚫는지(기하)와 연결선보다 나중에 그려진 불투명 면이 그 선을 덮는지(가려짐)는 다른 질문이고, 후자는 좌표만 봐서는 절대 안 잡힙니다. 캔버스 바탕만 예외로 두고 나머지 불투명 면은 전부 대상이에요.
도면 표준에서 가져온 둘. 브레이크는 "이 선은 실제로 훨씬 길지만 지면상 잘랐다" — 길이를 속이지 않고 줄이는 유일한 정직한 방법. 이중 사선 + ×N은 동일한 선 열두 개를 한 줄로 접습니다. 워커 12대에 선 12개를 그리는 건 정보가 아니라 노동이에요.
선 위에 꺾쇠(>)를 일정 간격으로 얹습니다. SVG의 marker-mid가 정확히 이걸 위한 기능인데,
곡선을 폴리라인으로 나눠 놓으면 중간 정점마다 마커가 접선 방향으로 자동 회전해서 붙어요.
넣어야 하는 진짜 이유는 정지 이미지입니다. 지금 방향을 표현하는 카드는 대부분 애니메이션(알갱이·혜성·패킷)인데, PNG로 내보내거나 인쇄하면 방향이 통째로 사라집니다. 셰브런은 흐름 애니메이션과 같은 인상을 주면서 한 프레임에 다 들어가요. 간격 규칙만 지키면 됩니다 — 선 굵기의 8~12배. 더 촘촘하면 점선으로 보이고, 더 성기면 방향이 끊깁니다.
markerUnits의 기본값이 strokeWidth라는 게 첫 함정입니다.
markerWidth="9"는 "9px"가 아니라 "획 굵기의 9배"예요 — 3px 획에 걸면 27px짜리 촉이 나옵니다.
"굵기를 올렸더니 촉만 커지더라"의 정체가 이겁니다.
markerUnits="userSpaceOnUse"를 기본으로 쓰고, refX를 뷰박스 오른쪽 끝에 두면
촉 끝이 경로 끝점에 정확히 옵니다.
둘째, markerWidth는 보이는 촉 크기가 아닙니다. 뷰박스 안에서 글리프가 실제로 차지하는 비율을 곱해야 해요.
흔한 열린 V M2 2 L10 6 L2 10은 뷰박스 12 중 8을 쓰니 보이는 촉 = markerWidth × 8/12.
계약은 보이는 촉 ≈ 획의 3배이고, 4배를 넘으면 촉만 보이고 2.5배 밑이면 방향이 약해집니다.
역산하면 markerWidth ≈ 획 × 4.5 — 획 2.0이면 9, 2.5면 11.25, 3.0이면 13.5.
셋째, 촉만 볼 게 아니라 축까지 같이 예산에 넣어야 합니다. 통로 = (목표의 시각적 왼쪽) − (출발의 시각적 오른쪽) — 그림자와 획까지 포함한 값이고, 여기서 양쪽 여백과 촉 길이와 카드에 가려지는 구간을 다 뺀 뒤에도 보이는 축이 12~16px은 남아야 합니다. "끝점 간격 8~12px은 맞췄는데 축이 거의 0"은 통과가 아니라 실패예요.
통로가 좁으면 촉을 줄이지 말고 형태를 바꿉니다 — 표준 화살표 → 컴팩트 화살표 → 전이 셰브런(선 없이 방향만) → 레이아웃 리플로우. 셰브런은 선이 의미를 안 실을 때 고르는 의도적인 선택이지, 축이 잘려서 도망칠 자리가 아닙니다. 손그림 처리에서는 변위가 촉을 얇아 보이게 해서 목표 비율을 3.5배(허용 3.0~4.5)로 올려 잡습니다.
중심끼리 이으면 선이 도형 위로 올라타고 화살촉이 채움 아래 묻힙니다 — 손으로 그린 다이어그램에서 제일 흔한 결함이에요.
끝점은 중심이 아니라 경계와의 교점이어야 합니다. 사각형이면 나눗셈 두 번이면 끝 — 중심에서 상대 중심으로 향하는 방향 d에 대해
t = min(hw/|dx|, hh/|dy|), 교점은 C + t·d. 둘 중 작은 쪽이 먼저 만나는 면입니다.
거기서 방향 단위벡터만큼 4~8px 더 물러나 촉 끝을 놓으면 테두리에 눌린 느낌이 사라집니다.
원이면 반지름 하나(C + r·d̂), 둥근 모서리는 사각형 값으로 잘라도 눈에 안 띕니다 — 오차가 모서리 반지름 안쪽이라서요.
연결점을 한 점에 박을지, 상대를 보고 매번 다시 고를지는 먼저 정해야 하는 갈림길인데 대개 그냥 기본값으로 흘러갑니다.
고정은 그 자리가 의미를 가질 때만 — 회로의 3번 핀, 서비스의 특정 입력구처럼요. 대신 노드를 옮기면 위 왼쪽처럼 반대편으로 돌아 들어옵니다.
떠다님은 ②처럼 상대 중심 방향으로 경계를 다시 잘라 나갈 면이 바뀝니다 — 일반 상자에는 이쪽이 기본값이어야 합니다.
계산은 경계에서 자르기와 같은 식 하나뿐이고, 달라지는 건 언제 다시 푸느냐입니다.
포트를 좌표가 아니라 0~1 비율로 적어두면 도형 크기가 바뀌어도 자리가 유지됩니다.
선 색을 바꾸는 순간 화살촉만 원래 색으로 남습니다. 마커 안쪽은 자기를 부른 도형에서 상속을 못 받기 때문이에요.
그래서 다들 색깔 수만큼 마커를 복제합니다 — 이 컬렉션의 공용 defs에도 같은 삼각형이 여섯 벌 있고,
상태가 바뀔 때 marker-end를 통째로 갈아끼우는 게 흔한 처방입니다.
값 하나로 끝납니다. fill="context-stroke"를 마커 내용에 주면 자기를 부른 선의 stroke를 씁니다.
채움이 없는 열린 V는 stroke="context-stroke"로 같은 일을 해요. 마커는 하나면 됩니다.
실측(Chromium 141): 클래스만 바꿔 선 색을 옮기면 촉이 같이 따라오고, var(--accent)도 해석되며,
직렬화해 <img>로 래스터해도 유지됩니다 — 이 컬렉션의 PNG·GIF 내보내기에서도 그대로라는 뜻입니다.
그러니 강조색 다이얼을 돌리면 촉까지 같이 물듭니다.
함정 하나. currentColor는 이걸 대신하지 못합니다. 위 ③가 실측인데,
선에 color를 줘도 촉은 그 색이 아니라 마커가 상속한 color(대개 SVG 루트나 defs)로 칠해집니다.
"선마다 다른 색"을 의도했다면 조용히 무너지는 쪽입니다.
폴백이 눈에 띕니다. 지원하지 않는 엔진에서 이 값은 그냥 무효값이고, 무효값을 넣어 재보면 채운 촉은 검정(fill 초기값)으로, 열린 촉은 아예 안 그려집니다(stroke 초기값 none). SVG 2 기능이라 엔진마다 도입 시점이 갈렸으니, 내보낸 파일이 어디서 열릴지 모르면 대상 브라우저에서 한 번 확인하고, 아니면 복제를 유지하세요. 크기 규칙은 옆의 화살촉 크기 계약과 함께 갑니다 — 색이 따라온다고 촉이 알맞아지진 않습니다.
여기까지는 "선 하나를 어떻게 그리나"였고, 실제 다이어그램이 지저분해지는 진짜 원인은 선 여러 개를 어디로 지나가게 하느냐입니다. 왼쪽이 방치, 오른쪽이 규칙 적용 — 선 종류는 하나도 안 늘었는데 읽히는 정도가 달라집니다. 스무 장 전부 정적이라 SVG·PNG로 그대로 씁니다.
한 노드에서 n개가 나가면 출발점을 (k − (n−1)/2) × 14px만큼 벌립니다. 한 점에서 부챗살로 퍼지면
시작 부분이 뭉쳐서 몇 개인지 안 세어지고, 화살표 근처도 마찬가지 — 들어오는 쪽도 같이 벌려야 합니다.
세 가지가 더 붙습니다. 면의 양 끝에는 16px 여백을 남기고(모서리에 붙으면 어느 면 소속인지 흐려짐),
간격은 14px과 (남는 폭 ÷ n−1) 중 작은 쪽이라 선이 많아지면 알아서 좁아집니다. 그리고
via·채널을 사람이 직접 지정한 관계는 자동 분산에서 빼야 해요 — 손으로 정한 좌표를 라우터가 다시 밀면 안 되니까.
벌린 결과가 서로 몇 px 차이로 어긋나면 ⑳ 경로 예산의 계단 문제로 넘어갑니다.
여러 연결이 같은 구간을 지날 때, 겹쳐 그리지 말고 6px씩 어긋난 가닥으로 나란히 보냅니다. 복도로 들어올 때와 나갈 때만 45°로 붙였다 떨어지고, 그 사이는 완전 평행. 지하철 노선도의 핵심 문법이에요.
지금 ① 포트 분산이 있지만 그건 노드 한 개의 접점을 벌리는 규칙이고, 이건 구간 전체를 벌리는 규칙입니다. 노드에서만 벌리면 중간에서 다시 겹쳐요. 핵심 제약 두 가지 — 복도 안에서 순서를 바꾸지 말 것(꼬면 추적 불가), 간격은 선 굵기의 약 2배로 고정할 것. 앞의 45° 사선 카드와 짝으로 씁니다.
한 단계를 건너뛰는 선(1→3)은 본류에 두면 반드시 노드 위를 지납니다. 위쪽에 전용 레인을 파고, 그런 선이 여러 개면 36px씩 층을 쌓아 서로도 안 겹치게. 레인 높이는 가장 높은 노드보다 위에서 시작.
되돌아가는 선(재시도·피드백)을 본류 사이로 통과시키면 방향을 잃습니다. 도형 바깥 아래(세로 배치면 옆)로 빼고, 점선으로 "역방향"임을 표시하고, 여러 개면 역시 층을 쌓습니다. 회귀선이 본류를 한 번도 안 건너는 게 목표.
노드가 자기 자신으로 돌아오는 선. 재시도·폴링·재귀·상태 유지처럼 흔한데 지금 컬렉션에 없습니다. ④ 루프백 레인이 있지만 그건 다른 노드로 되돌아가는 경로예요 — 출발=도착인 경우는 기하가 아예 다릅니다.
파라미터가 둘입니다 — 방향(루프가 어느 쪽으로 튀어나갈지)과 벌림(나가는 점과 들어오는 점 사이 각).
그래프 라이브러리들이 loop-direction / loop-sweep으로 정확히 이 둘을 노출합니다(기본 −45° / −90°).
루프가 2개 이상이면 반드시 방향을 갈라야 하고, 라벨은 루프 바깥쪽에 둡니다.
같은 두 노드 사이 왕복은 한 줄에 화살촉만 둘 달면 "한 관계"로 읽혀 요청·응답을 못 나눕니다. ±7px씩 벌려 두 줄로 만들고 각자 라벨·스타일을 주면 왕복임이 보입니다. 응답 쪽을 점선으로 낮추면 주·부도 같이 표현됩니다.
같은 두 노드 사이에 관계가 여러 개일 때. 지금 ⑥ 왕복 쌍이 있지만 그건 A→B와 B→A 딱 2개인 특수 경우고, 같은 방향으로 3개 이상이면 규칙이 없습니다. 그냥 그리면 선도 라벨도 완전히 포개져서 관계 하나로 보여요.
한 그래프 엔진이 이걸 두 숫자로 정리해 뒀습니다 — curveOffset(현에서 제어점이 떨어진 거리 = 휘는 정도)과
curvePosition(0~1, 제어점이 현의 어디쯤인지). k번째 엣지에 offset = (k − (n−1)/2) × 28을 주면 끝.
포트 분산과 공식이 똑같은데 적용 축이 다릅니다 — 포트 분산은 노드 접점을 벌리고, 이건 선의 배를 벌려요.
덤으로 각 호의 꼭대기가 라벨 자리가 되어서, 관계 이름을 겹치지 않게 놓을 수 있습니다.
그룹 박스가 있으면 선이 테두리를 어디로 뚫는지가 새 변수가 됩니다. 비스듬히 관통하면 그룹이 껍데기로 보여요. 교차는 그룹 밖에서 끝내고, 경계는 수직으로 넘고, 통과 지점(빨간 눈금)을 맞추면 그룹이 경계로 읽힙니다.
①이 "같은 면에서 몇 개를 어떻게 벌리나"였다면, 이건 어느 면으로 드나드나입니다. 주로 위아래로 가는 선이 옆면으로 들어오면 노드 얼굴을 찌르는 것처럼 보입니다. 수직 이동은 top/bottom, 수평 이동만 left/right — 좌우 포트를 기본값으로 두지 마세요.
한 발 더 나가면 면을 고르는 것과 그 면으로 수직으로 드나드는 것은 다른 문제입니다. top으로 들어오기로 정해놓고 마지막 구간이 비스듬히 들어오거나 테두리에 접하면서 들어오면, 선언한 면이 아무 뜻이 없어져요. 규칙은 한 줄 — 첫 구간과 마지막 구간은 정한 면에 수직. 끝점은 상자 테두리 위의 점이 아니라 방향 약속입니다. 자동 라우터라면 이 조건을 통과 못 하는 후보를 아예 버리는 게 낫습니다. 이상한 화살표가 나중에 사람 눈에 발견되는 것보다 싸요.
①가 얼마나 벌리나, ⑨가 어느 면인가였다면 이건 그 면에서 위아래 순서입니다 — 셋 중 제일 안 챙기는 축이고, 공짜로 교차가 사라지는 축이에요. 포트를 코드에 적은 순서대로 붙이면 상대 노드의 위치와 어긋나 선끼리 꼬입니다. 상대 노드의 y로 정렬해서 붙이면 두 판 모두 선 개수도 경로 모양도 그대로인데 교차만 0이 됩니다. 층별 배치의 교차 최소화(barycenter)를 한 면 안으로 줄인 것이고, 상대가 여럿이면 y의 평균을 쓰면 됩니다. 가로로 드나드는 면이면 x로 정렬합니다.
앞의 일곱 규칙이 다루지 못한 마지막 경우 — 레인으로도 정리가 안 되는 다대다. 중간 허리로 당겼다 풀면 선 개수가 아니라 흐름의 부피가 보입니다. 대가가 분명한 기법이라 같이 적어야 해요: 개별 경로 추적은 포기하는 대신 전체 구조를 얻습니다. 한 선을 끝까지 따라가야 하는 그림이면 쓰면 안 됩니다.
지금 ⑩ 다발 묶기는 켜고 끄는 스위치인데, 실제로는 0에서 1까지 도는 다이얼입니다.
표준 시각화 라이브러리의 curveBundle.beta(β)가 정확히 이 값이에요 — β=0이면 완전 직선, β=1이면 제어점이 허리에 완전히 붙습니다.
권장 기본값은 0.85이고, 이유가 명확합니다 — β=1은 예뻐 보이지만 허리에서 네 선이 한 덩어리가 돼서
어느 선이 어디로 가는지 추적이 안 됩니다. 0.85는 묶임을 보여주면서 각 선의 출입 각도를 남겨둬요.
두 번째로 필요한 게 "어떤 선끼리 묶어도 되나"인데, 그래프 엔진의 edge-bundling 플러그인이 이걸
bundleThreshold(기본 0.6)로 노출합니다 — 방향·길이·위치가 비슷한 호환 가능한 선끼리만 묶고,
값을 올릴수록(0.8) 까다로워져 묶이는 선이 줄어요. 나머지 파라미터도 뜻이 있습니다 —
K(0.1, 당기는 힘) · cycles(6, 반복 라운드) · divisions(3, 선을 나눈 마디 수).
엉뚱한 선까지 묶이면 β가 아니라 임계값을 올릴 것.
서로 다 연결된 노드 뭉치를 선 하나(백본) + 짧은 지선(스텁)으로 바꿉니다.
n개 노드가 완전연결이면 선이 n(n−1)/2개인데, 버스는 항상 n+1개. 6개면 15 → 7.
상용 레이아웃 엔진이 BusRouter라는 이름으로, 회로도가 데이터 버스로 100년 쓰던 방식입니다.
지금 있는 ⑩ 다발 묶기와 다릅니다. 다발은 가는 길이 같은 선들을 시각적으로 모을 뿐 선 개수는 그대로인데, 버스는 "이 집합 안에서는 다 통한다"를 한 줄로 선언해서 선을 실제로 없앱니다. 쓸 수 있는 조건이 명확해요 — 관계가 완전연결(또는 브로드캐스트)일 때만. 개별 쌍을 강조해야 하면 못 씁니다.
선을 끝까지 안 그립니다. 양쪽 끝에서 각각 25%만 그리고 가운데는 비워요. 그러면 교차가 구조적으로 0이 됩니다. 사람은 남은 두 토막의 각도를 연장해서 짝을 찾습니다 — 실제로 됩니다.
실험에서 나온 조건이 하나 있는데 중요합니다 — "균질성이 교차 수나 잉크 양보다 중요하다". 즉 모든 선이 똑같은 비율(25%면 전부 25%)로 잘려야 하고, 선마다 다른 길이로 자르면 오히려 전체 그리기보다 나빠집니다. 밀집 그래프에서 다발 묶기가 실패하는 경우(묶을 공통 경로가 없는 랜덤 그래프)에 남는 마지막 카드예요. 스텁 끝에 앞의 테이퍼를 붙이면 방향까지 살아납니다.
같은 두 잭을 잇는 케이블이 여럿이면 처짐 깊이를 7씩 달리합니다. 옆으로 벌리지 않고 아래로 벌려요. ⑦ 다중 엣지(curveOffset)는 끝점부터 좌우로 갈라 놓는데, 이건 끝점을 한 점에 모아 둔 채 중간만 갈라집니다 — "한 잭에 여러 개 꽂힘"이 정보일 때 쓰는 분리법이라 축이 다릅니다. 행잉 케이블(경로 모양)과 짝이고, 보통 곡선에도 제어점 y 만 바꾸면 그대로 됩니다.
레인으로 다 뺐는데도 남는 교차는 덜 중요한 선만 반원으로 넘깁니다(a 8 8 0 0 1 0 16).
둘 다 넘기면 의미가 사라지니 한쪽만 — 점선·가는 선·되돌아가는 선이 넘어가는 쪽입니다.
교차는 홉, 연결은 점 — 이 둘을 구분해야 T자 지점에서 오독이 없습니다.
같은 선을 두 번 그립니다 — 먼저 배경색으로 7px, 그 위에 본색으로 1.8px. 아래 선이 자동으로 끊겨 보여서 위/아래가 생깁니다. 지도에서 도로 입체교차를 그리는 표준 방식.
솔직히 말하면 ⑭ 교차 홉과 같은 문제를 푸는 다른 답입니다. 차이는 이래요 — 홉은 넘어가는 선이 튀어서 SVG 경로가 복잡해지고 교차점 좌표를 미리 계산해야 하는데, 케이싱은 경로를 안 건드리고 렌더 순서만으로 해결됩니다(교차점 계산 0). 대신 배경이 단색일 때만 통해요. 노드 밑을 지나가는 선에도 그대로 씁니다.
얼마나 두껍게 깔 것인가는 두 갈래입니다. 배경색으로 불투명하게 3~4배(여기 예시는 1.8px 위에 7px)를 깔면 아래 선이 확실히 끊기지만 다발 안쪽에도 흰 틈이 생겨요. 반대로 1.4배에 불투명도 0.7만 주면 아래 선이 비쳐 보이면서도 위/아래는 읽힙니다 — 아이소메트릭 도구들이 쓰는 튜닝이고, 선이 촘촘할수록 이쪽이 낫습니다.
교차를 없애는 규칙은 이미 여러 개 있는데(우회 채널·홉·다발), 피할 수 없는 교차를 어떻게 교차시킬지는 없습니다. 답은 각도예요 — 교차각이 30°보다 작아지면 읽는 시간이 지수적으로 늘어납니다(흐름도 설계 연구가 정리한 실험 결과). 직각에 가까울수록 눈이 두 선을 자동으로 분리합니다.
실무 규칙 세 줄. ① 교차를 못 없애면 둘 중 하나를 더 휘어서 각을 벌린다(선을 줄이는 것보다 각을 키우는 게 싸다). ② 노드에서 나가는 선들도 방사형으로 고르게 벌린다 — 좁은 각으로 몰리면 노드 근처에서 같은 문제가 생김. ③ 연결되지 않은 노드 밑으로는 선을 통과시키지 않는다. 이 셋은 앞 카드들과 달리 그림이 아니라 검사 항목입니다.
⑫ 버스 백본은 여러 선을 일부러 한 줄기로 모으는 기법이었죠. 그런데 끝점을 공유하지 않는 두 선이 우연히 같은 복도를 8px 이상 나란히 달리면, 그림은 똑같은데 뜻이 거짓말이 됩니다 — A→D와 B→C가 "A·B가 합쳐졌다 갈라진다"로 읽혀요. 기법과 버그를 가르는 건 그림이 아니라 끝점을 공유하느냐 하나입니다.
둘째는 컨테이너 테두리를 복도로 빌려 쓰는 것. 선이 그룹 상자의 변을 따라 붙어 달리면 테두리가 굵어진 건지 선이 지나가는 건지 못 가립니다. 테두리를 가로지르는 건 괜찮아요 — 따라 달리는 게 문제고, 같은 방향으로 겹치는 길이가 조금이라도 있으면 실패로 봅니다. 고치는 값은 쌉니다 — 8px만 비켜 놓으면 됩니다.
이 둘은 앞 카드들과 성격이 달라서 그리는 법이 아니라 확인하는 법입니다. 노드를 뚫고 지나가는 선은 바로 눈에 띄는데, 이 둘은 그럴듯해 보여서 그냥 넘어갑니다.
경로가 "좋다"를 숫자로 두면 자동 라우팅을 기계가 검사할 수 있습니다. 네 개면 충분해요 — 꺾임은 관계당 2번까지, 스트레치(실제 경로 길이 ÷ 직선 맨해튼 거리)는 1.35배까지, 모든 구간은 8px 이상, 끝점 스텁을 뺀 안쪽 구간은 16px 이상.
뒤의 두 개가 실무에서 더 자주 걸립니다. 포트를 벌리다 보면(① 포트 분산) 두 포트가 몇 px 차이로 어긋나고, 그러면 라우터가 7px짜리 계단을 하나 만듭니다. 이건 디자인이 아니라 렌더링 사고처럼 보여요. 답은 두 갈래 — 어긋남이 16px 안쪽이고 한쪽 끝만 벌어진 상태면 두 포트를 한 축에 맞춰 직선으로 만들고, 양쪽 다 벌어져서 못 맞추면 작은 계단 대신 24px 스텁 → 바깥 채널로 크게 돌아갑니다. 어느 쪽이든 작은 계단은 안 만든다가 규칙이에요.
스트레치 1.35라는 값이 의외로 관대합니다 — 되돌아가지 않는 직각 경로는 몇 번을 꺾어도 맨해튼 길이가 그대로라 대개 1.0배입니다. 1.35를 넘긴다는 건 경로 안에 되돌아가는 구간이 있다는 뜻이고, 실제로 꺾임 수보다 이쪽이 더 아프게 읽힙니다.
반대 방향 검사도 하나 있습니다 — 곧게 갈 수 있었는데 굽힌 경우. 두 노드의 포트 구간이 32px 이상 겹치고 그 사이에 다른 노드가 없으면, 꺾임은 몇 번이든 오류입니다. ⑲의 나머지가 "너무 돌아갔다"를 잡는다면 이건 "돌아갈 이유가 없었다"를 잡아요.
포트를 옮겨서 피할 때는 최소 변위가 원칙입니다 —
8px짜리 방해에는 8px로 답하고, 넓은 구간의 한가운데로 점프하지 않습니다.
몇 px을 수십 px의 이동으로 갚으면 "치운 선"이 아니라 "잘못 그린 선"으로 읽히거든요.
바라던 좌표에서 +12, −12, +24, −24 …로 바깥으로 훑어 나가면 결과도 결정론적입니다.
출처가 아니라 무엇이 변하는가로 묶었습니다. 다섯 축(굵기 · 통과 · 생성 · 상태 · 빛)이 서로 겹치지 않아, 한 다이어그램에서 고를 때는 축을 먼저 정하고 그 안에서 하나만 쓰면 됩니다.
선 전체가 부풀었다 줄어듦. "살아있다"는 신호만 주고 방향은 못 보여줌. 비용 0 — CSS 한 줄.
볼록한 덩어리가 관을 타고 이동 — 방향과 속도가 다 보임. 엘보든 곡선이든 경로를 그대로 따라감. 구현도 쌈.
여러 덩이가 줄지어 흐름 — 펌프질, 대량·연속 전송 느낌. 여러 선에 걸면 금방 시끄러워지니 1~2개까지만.
선 자체가 물결치며 부풀었다 좁아짐 — 가장 문자 그대로의 꿀렁꿀렁. 매 프레임 다시 그리는 JS라, 강조할 선 몇 개에만.
점만 지나가고 선은 안 부풂 — 꿀렁이 아니라 "패킷 하나"의 느낌. 개별 이벤트를 보여줄 때는 이쪽이 맞음.
머리 하나 + 뒤로 갈수록 작아지고 옅어지는 꼬리. ⑤ 알갱이의 상위 버전으로, 방향감이 훨씬 셈. 원본의 물리 spark(튀는 불똥)는 Canvas 전용이라 뺐습니다.
반투명 몸통 대시(16 25) 위에 진한 머리 대시(6 35)를 위상차 10으로 겹침 — "머리 달린 패킷"이 흐름. 원본은 어두운 배경에 밝은 머리, 밝은 배경에선 반대로 머리를 진하게. 굵기는 고정.
흐르는 게 무엇인지를 패킷에 적어서 보냄 — JWT, Request ID, INSERT, 이벤트명. 선 종류를 늘리지 않고 정보를 더하는 유일한 방법이라, 규칙 개수 예산을 안 씁니다.
선 위에 라벨을 얹는 게 아니라 선을 따라 흐르게 함. SVG 네이티브 <textPath>라 Canvas보다 오히려 쉬움.
긴 페이로드·프로토콜 표기에 적합.
대시를 움직이는 게 아니라 그라데이션 좌표 자체를 이동시킵니다. 대시는 패턴이 반복돼 여러 개가 보이지만 이건 펄스가 언제나 정확히 하나고, 머리·꼬리에 다른 색을 줄 수 있어 색만으로 방향이 읽혀요. 좌표 시작·끝을 도형 바깥까지 빼는 게 요령 — 그래야 되감기는 순간이 화면에 안 걸립니다.
두 가지가 겹쳐 있습니다. 하나는 분기마다 고유색 — 쉬는 동안에도 옅게 깔려 있어 어느 갈래가 뭔지 색만으로 압니다. 다른 하나는 한 번에 하나만 켜고 돌아가기 — 셋 다 동시에 움직이면 예산을 세 배 쓰지만, 번갈아 켜면 같은 예산으로 "셋 다 살아 있다"를 말할 수 있어요. 켜짐 구간은 주기의 1/n 남짓(원본은 분기당 2.2초). 여기 색은 강조가 아니라 이름이라 3색이 규칙 위반이 아닙니다 — 아래 참조.
⑤⑥⑧처럼 경로를 따라 물체를 보내는 걸 CSS 한 줄로 합니다 — offset-path:path("…") +
offset-distance 애니메이션. 이 컬렉션의 다른 이동 계열은 라이브는 JS, 내보낼 땐 SMIL로 변환하는데
이건 둘 다 필요 없습니다. 갈림길은 offset-rotate 하나 — 화살촉·차량은 auto(경로 기울기를 따라 눕고),
라벨은 0deg(안 그러면 뒤집혀서 못 읽습니다).
점 하나가 나갔다가 같은 선으로 돌아옵니다. 돌아오는 점의 색이 결과이고, 출발점 표시등이 그 색으로 남아요. ⑤ 알갱이 · ⑥ 혜성은 끝없이 흐르는 양이고 라우팅의 ⑥ 왕복 쌍은 선 두 개인데, 이건 한 번 일어난 사건을 선 하나로 — 방향이 뒤집히고, 색이 결과를 싣고, 흔적(표시등)이 남습니다. 요청·응답 · 헬스체크 · 지연 시간. 여러 홉이면 0 · 240 · 500 · 740ms 시차로 이어 붙입니다. 라이브는 JS, 내보낸 SVG·HTML은 SMIL 로 같은 3주기(OK · OK · 오류)를 돕니다.
dasharray를 "경로길이 경로길이"로 두고 offset을 줄이면 선이 그려짐. 순서대로 등장 → 도착 시 화살표 → 그 위로 패킷 스트림 → 리셋. 내보낸 SVG/HTML은 한 번 그려진 뒤 계속 흐릅니다.
지나간 자리에 색이 남으면서 경로를 따라 색상이 변함 — 흐름의 "단계"를 색으로 표시.
원본은 Canvas 누적 페인트, 여기선 pathLength="100" + 그라데이션 stroke로 동일한 인상.
1:N 분산을 동시가 아니라 아주 짧은 시차로 뻗게 하면 "퍼져나간다"로 읽힘. 로드밸런서·브로드캐스트·팬아웃 큐. 시차 0이면 그냥 세 선이라 효과가 죽습니다.
모션그래픽의 값 그래프가 CSS의 cubic-bezier()와 같은 것입니다 — 곡선의 기울기가 곧 속도라,
펜 툴로 패스 만지듯 감가속을 직접 빚습니다(속도 그래프는 속도를 y축에 직접 그리는 방식이고, CSS로 흉내 내려면 linear()에 점을 찍어야 합니다).
규칙은 둘로 갈립니다 — 끝없이 도는 흐름은 반드시 등속(이징을 걸면 루프 이음매에서 눈에 띄게 튐),
한 번 일어나는 전이는 이징. 그리고 전이에서는 끝이 단조롭지 않게 — 대칭 ease-in-out보다
빨리 튀어나가 길게 안착하는 곡선이 무게가 느껴집니다.
선이 그려지는 게 아니라 두 덩어리가 액체처럼 이어붙는 연결. 흐림(feGaussianBlur)을 준 뒤
feColorMatrix로 알파 대비를 극단으로 올려 경계를 되살리면, 가까워질 때만 목이 생깁니다.
화살표가 어울리지 않는 관계 — 병합 · 흡수 · 같은 그룹이 됨에 씁니다. 방향이 없는 게 특징이자 한계. 선이 아니라 노드가 통째로 움직여서 이 컬렉션에서 가장 비싼 효과(4.9%)입니다 — 정말 중요한 관계 하나에만.
두 숫자는 따로 놀면 안 됩니다. 알파 대비의 기울기(22)를 올리면 절편도 같이 내려야 임계값이 제자리에 있습니다 —
절편 = 0.5 − 기울기 × 5/12. 고전적인 18 −7 짝이 이 식에서 나오고, 기울기 22면 −8.67이에요.
임의로 −10을 쓰면 임계 알파가 0.42에서 0.48로 올라가 목이 얇아집니다 — 간격 6px에서 재 보니 14px → 9px로 35% 얇아졌습니다.
마지막 feComposite operator="atop"이 원본을 흐린 실루엣 위에 다시 얹습니다.
이게 없으면 필터 안의 글자·아이콘이 전부 뭉개져서, 구이 효과는 장식용 원에만 쓸 수 있게 돼요.
하나 더 — 필터는 SVG 콘텐츠에 걸어야 합니다. HTML 요소에 CSS filter:url()로 거는 방식은 WebKit이 다르게 그립니다.
반대편 근거도 적어 둡니다. 같은 구이 효과를 만들어 놓고 필터를 버린 사례가 있습니다 — 이유는 크롬·사파리·파이어폭스에서 같은 픽셀이 나온다고 보장할 수 없다는 것이고, 그래서 흐림+임계값 대신 그냥 원을 그렸습니다. 기하는 그대로 두고 경계만 안티에일리어싱에 맡기는 방식이에요. 거기 달린 주석이 이 카드에서 제일 쓸모 있습니다 — "그렇다고 반지름을 줄여서 보정하지 마라. 그러면 마크가 진짜로 작아진다." 필터가 만들어 주던 단단한 경계가 사라지면 조금 물러 보이는데, 그걸 크기로 벌충하면 튜닝해 둔 값이 통째로 무너집니다. 내보내기를 여러 브라우저에서 열게 할 거라면 필터 없는 쪽을 고르고, 모양을 정확히 지켜야 하면 필터를 고르세요.
죽은 경로는 지우지 말고 빨갛게 흐려서 남기고, 대체 경로를 새로 그림 — "무엇이 죽었는지"가 같이 보여야 장애도가 읽힘. 장애 시나리오·재해복구 문서에서 가장 값어치 있는 한 장.
선이 목적지까지 못 가고 경계에서 끝남 — 우회로가 있는 ⑬과 달리 여기서 끝. 뒷구간은 옅은 점선으로 남겨 "원래 가려던 곳"을 보여줍니다. WAF·정책 거부·권한 차단·rate limit.
같은 요청이 조건에 따라 두 갈래 — 짧은 길과 긴 길이 번갈아 활성. ⑬처럼 하나가 죽는 게 아니라 둘 다 정상 경로라 쓰지 않는 쪽을 지우지 않고 흐리게 둡니다. 캐시·샤딩·A/B·폴백.
상태 계열이 지금 장애 · 차단 · 조건 분기 셋인데 전부 사고입니다. "정상인데 지금은 조용함"이 없어요. 흐름 애니메이션은 "지금 트래픽이 있다"는 뜻이라 유휴 상태에 쓰면 거짓말이 됩니다. 하트비트는 연결이 살아 있다만 말합니다. 주기 2.3초, 밝아지는 데 0.6초 · 어두워지는 데 0.5초로 살짝 비대칭 — 심장 박동처럼 빨리 차고 천천히 빠지는 게 아니라 그 반대여야 기계 신호로 읽힙니다.
여기서 진짜 배울 건 비용입니다. 넷 다 "살아 있음"이라는 같은 뜻인데 변화율이 25.07% / 2.57% / 2.45% / 0.33%로 76배 벌어집니다. 호흡시키는 게 면적이냐 점이냐의 차이예요. 그리고 하트비트는 흐름 애니메이션과 결정적으로 다릅니다 — 흐름은 강조하고 싶은 한 경로에만 걸지만, 하트비트는 살아 있는 링크 전부에 걸립니다. 링크가 20개면 예산을 20으로 나눠야 하니, 실질적으로 쓸 수 있는 건 점 박동뿐입니다.
이 섹션의 나머지가 전부 무한 반복입니다. 그런데 흐름을 대시로 표현하는 계열(⑦ 대시 패킷 같은)은 흐르는 동안 선이 원래 갖고 있던 점선의 뜻을 덮어씁니다 — 5 5가 "보안 채널"이었다면 흐르는 내내 그 정보가 사라져요. 이 컬렉션의 기존 답은 "그러면 대시 대신 굵기 계열을 써라"였는데, 다른 답이 있습니다.
한 번만 흐르고 원래 획으로 돌려주는 것. 페이지가 열리면 대시가 한 바퀴 지나가고, 끝나면서 빌려 썼던 10 8을 놓고 원래의 5 5로 복귀합니다. 흐름도 보여주고 뜻도 안 지우고, 예산은 한 번만 씁니다. 무한 반복이 기본값이어야 할 이유는 사실 없어요 — 계속 도는 건 "지금 이게 살아 움직인다"를 말할 때만 필요합니다.
여기서 규칙이 하나 더 나옵니다 — 멈출 때 무엇이 남는가를 기법마다 따로 정해야 해요. 혜성은 대시를 없애 실선 강조로 굳히고(경로가 그대로 남아서 쓸모가 있음), 선을 타고 가던 토큰은 아예 지웁니다(경로 중간에 멈춰 선 패킷은 아무 뜻도 없으니까). 그냥 일시정지시키면 둘 다 "고장 난 화면"이 됩니다.
움직임 최소화 설정에도 같은 질문이 그대로 옵니다. 흔한 처리는 "그 자리에서 멈춤"인데,
그러면 사용자마다 다른 순간의 그림을 보게 돼요. 더 나은 답은 대표 프레임 하나를 미리 고르는 것 —
시각 t를 상수로 박아 그 한 장을 그립니다. 크기·속도·상태에 상관없이 같은 순간이라 결과가 예측 가능해져요.
다만 정직하게 말하면 고른 순간이 모든 기법에 좋을 수는 없습니다 — 어떤 기법은 그 시각에 하필 "가장 흐트러진"
상태일 수 있어서, 기법마다 대표 시각을 따로 두거나 최소한 한 번은 확인해 보고 정해야 합니다.
두 노드의 순서를 뒤집을 때 연결선이 끊겼다가 새 위치에서 다시 이어집니다. Orbit · Morph · Slide 세 연출. 이 컬렉션의 주제가 선이니 볼 것은 카드의 움직임이 아니라 선이 없는 그 0.5초예요 — 자리가 확정되기 전에 선을 남겨 두면 존재하지 않는 연결을 그리는 셈이라, 끊는 게 정직한 처리입니다.
세 연출의 차이는 겹치는 순간 누가 앞인가에서 갈립니다.
Orbit은 앞으로 나오는 카드를 위쪽 호로(scale 1.05), 뒤로 가는 카드를 아래쪽 호로(.94 · 투명도 .8) 보내
깊이로 답합니다. Slide는 수평 교차라 가장 싸지만 겹치는 프레임에서 앞뒤가 모호해요.
Morph는 아예 자리를 안 옮깁니다 — blur 6px · 투명도 .35로 가장 흐려진 정점에서 내용만 맞바꿉니다.
그래서 Morph만 선을 끊지 않고 .35로 흐리기만 해요. 위치가 안 변하니 끊을 이유가 없습니다.
구현에서 걸리는 건 하나입니다 — 이동량을 transform에 누적하면 세 번째 스왑부터 자리가 어긋납니다.
애니메이션이 끝나면 transform을 0으로 되돌리고 자리 자체(x)를 맞바꿔 확정해야 해요.
실측으로 세 모드 각각 5회 연속 스왑 뒤에도 슬롯 좌표가 12 / 278 그대로였습니다.
전환 중의 두 번째 클릭은 무시합니다 — 안 막으면 애니메이션 두 개가 같은 카드에 겹쳐 위치가 꼬입니다.
움직임 최소화 설정에서는 연출 없이 즉시 교환합니다.
— 원본은 DOM·CSS 컴포넌트였고, 이 컬렉션은 모든 항목이 SVG라 내보내기가 SVG를 복제·래스터합니다. 그대로 넣으면 이 항목만 SVG·PNG·GIF로 못 나가서, 같은 인터랙션을 순수 SVG로 다시 구현했습니다. 데모용 껍데기(테마 버튼 · 색 스와치 · 전체화면 무대)는 컬렉션에 이미 있어 가져오지 않았습니다.
같은 경로를 넓게(16~18px) · 낮은 불투명도(.16~.26)로 한 번 더 깔고 위에 또렷한 심선을 얹으면 끝 — 원본 Canvas도 정확히 이 방식입니다. 핵심은 어두운 배경에서만 값어치가 있다는 것. 밝은 배경에선 그냥 선이 두꺼워 보일 뿐입니다.
패킷이 닿는 순간 링이 퍼지고 노드 테두리가 한 번 번쩍입니다. 흐름 애니메이션은 "가는 중"만 보여주는데, 이건 "도착했다"는 사건을 찍어줘서 인과가 읽힙니다. 계속 깜빡이면 경고등이 되니 사건에만.
⑦ 대시 패킷과 같은 기법인데 넓은 쪽을 흐리게(feGaussianBlur) 처리하면 "패킷"이 아니라 "빛이 훑고 지나감"으로 읽힙니다. 점검·스캔·인덱싱처럼 대상이 아니라 행위가 지나가는 흐름에.
제대로 하려면 두 가지가 더 필요합니다. 하나는 가장자리를 딱 끊지 않는 것 —
띠의 밝기를 계단이 아니라 가우시안으로 떨어뜨리면(반폭 25° 남짓) 훑는 느낌이 나고,
각도로 도는 스캔이라면 차이를 atan2(sin Δ, cos Δ)로 계산해야 한 바퀴 도는 자리에 이음매가 안 생깁니다.
다른 하나는 훑지 않은 부분을 죽이는 것 — 훑는 쪽만 밝히면 대비가 부족해서, 나머지를 45% 정도로
내려야 띠가 확실히 읽힙니다. 05 포커스 디밍의 사다리와 정확히 같은 원리예요 —
강조는 밝히는 게 아니라 나머지를 죽이는 것입니다.
prefers-reduced-motion) — 이건 취향이 아니라 접근성 요건이라 내보낸 SVG에도 같이 실립니다. 여러 선을 동시에 움직일 때는 선마다 위상 오프셋을 다르게 주고 루프 길이를 정수배로 맞추세요 — 전부 같은 박자로 뛰면 다이어그램이 아니라 전광판이 되고, 루프가 정수가 아니면 GIF 이음매가 튑니다. 반복은 등속, 일회성 전이만 이징 — 도는 흐름에 이징을 걸면 매 바퀴 이음매에서 튀어 보입니다.
animation-play-state:paused로
멈추는 이 페이지도 그래요 — 재 보니 370ms 멈춘 선이 안 멈춘 선보다 정확히 그만큼 뒤처집니다.
답은 위상을 경과가 아니라 시계에서 읽는 것: t = performance.now()/1000 × 속도로
매 프레임 위치를 계산하면, 멈췄다 켜도 시계가 있는 자리로 바로 복귀합니다.
화면 밖에서 애니메이션을 멈추는 최적화와 위상 규칙을 둘 다 지키려면 이 방법밖에 없어요.
floor(t)를 시드로 쓰고 frac(t)를 경로 위 위치로 쓰면
상태를 하나도 안 들고도 순회마다 경로가 바뀝니다. 이렇게 두면
되감기 · 정지 · 재개 · GIF 인코딩 · 렌더러 간 비교가 전부 공짜로 따라와요 —
어떤 시각 t의 그림이 항상 같으니까요.
feGaussianBlur가 있습니다. 그래서 E 계열로 회수했습니다.
필요하면 SVG는 좌표 엔진, Canvas는 렌더러로 나누는 구조가 맞습니다.
반대로 사전 샘플링(매 프레임 getPointAtLength()를 부르지 않고 미리 계산)과
시맨틱 메타데이터(선에 role / stage / kind를 붙여두면 연출이 데이터가 됨)는
렌더러와 무관한 원칙이라 이 파일도 같은 방식(data-xa-*)을 씁니다.
지금까지는 스스로 움직이는 선이었고, 이건 사람이 건드리면 반응하는 선입니다. 정보를 처음부터 다 보여주지 않고 물어볼 때 꺼내 놓을 수 있어서, 밀도 문제를 근본에서 줄입니다. 아래 카드의 선 위에 마우스를 올려보세요 — 내보낸 SVG를 브라우저에서 열어도 그대로 반응합니다.
기본 1.5px 옅은 선 → 호버 시 강조색 3px → 선택 시 고정 강조. 전환은 150ms, stroke와 stroke-width에만.
핵심은 보이지 않는 굵은 히트 패스(16px, stroke:transparent)를 밑에 깔아두는 것 — 안 깔면 1.5px 선을 마우스로 맞춰야 해서 고장 난 것처럼 느껴집니다. 단 보이는 선이 히트 패스를 덮으므로 형제 선택자 대신 둘을 <g>로 묶어 그룹 호버로 받아야 정중앙에서도 잡힙니다.
히트 패스는 마우스 얘기고, 키보드는 답이 따로 필요합니다. 사각형이면 브라우저 기본 포커스 링이 그려지는데
선에는 그릴 테두리가 없어요. 방법은 포커스 레일 — 히트 패스와 같은 경로에 6px 점선(2 4)을
34% 투명도로 깔아두고 :focus-visible에서만 켭니다. 선을 따라가는 점선 띠라 어느 선이 잡혔는지 바로 보이고,
vector-effect:non-scaling-stroke를 주면 확대해도 두께가 안 변합니다.
위 네 번째 줄에 Tab으로 들어가 보세요 — 내보낸 SVG에서도 그대로 동작합니다.
두 가지가 더 붙습니다. 터치·굵은 포인터에서는 히트 패스를 24px까지 넓히고
(@media (pointer: coarse)), 렌즈나 포커스 모드가 켜져 있는 동안에는 히트 패스를
pointer-events:none으로 꺼야 합니다 — 안 끄면 모드끼리 서로 클릭을 뺏습니다.
라벨을 평소엔 35%로 죽여두고 선에 마우스를 올릴 때만 살립니다. 선이 20개인 다이어그램에서 라벨 20개를 항상 띄우면 못 읽는데, 이러면 밀도를 안 올리고 정보를 다 담을 수 있습니다. 종이로 낼 거면 못 쓰는 기법.
연결 가능한 지점을 평소엔 완전히 숨기고(opacity 0) 노드에 올렸을 때만 띄웁니다. 안 그러면 노드마다 점 네 개가 상시로 붙어 다이어그램이 회로도처럼 보여요. 편집 가능한 캔버스의 기본 예의.
한 선에 올리면 나머지를 16%까지 죽여 그 경로만 남깁니다. 선이 많을 때 "이 요청이 어디로 가는가"를 레이아웃을 안 바꾸고 답해줘요. — 원본에는 없고, 위 셋과 같은 축이라 채워 넣은 확장입니다.
죽이는 정도를 한 값으로 두지 않고 질문이 좁을수록 더 깊게 가져가면 한 단계 좋아집니다. 실제로 쓰이는 사다리는 대략 이래요 — 아직 목적지를 고르는 중이면 24%, 그냥 지나가는 호버는 20%, 선 하나를 집었으면 16%, 노드 하나에 집중이면 13%, 종류로 걸러낸 상태면 11%, 상·하류 전체를 물었으면 9%. 답이 정확할수록 배경을 더 지운다는 한 줄이 전부입니다.
커서 주변의 선만 밀어내고 노드는 안 움직입니다. 레이아웃(=사용자가 외운 위치)을 안 건드리면서 가려진 것만 드러내는 게 핵심 — 노드를 같이 밀면 매번 지도가 달라져서 오히려 헤맵니다. 시각화 연구 그룹에서 나온 기법이고, 여러 렌즈를 동시에 쓸 수 있습니다.
구현이 생각보다 쌉니다. 직선도 큐빅으로 미리 써두면(C 명령 개수만 같으면) CSS d 속성이
그대로 트랜지션됩니다 — JS 없이 :has(.hit:hover) 한 줄. 지금 포커스 디밍이 "관계없는 걸 흐리게"라면,
이건 "가리는 걸 치운다"라 반대쪽 답입니다. 흐리면 정보가 없어지는데, 밀면 다 남아요.
지난번에 넣은 엣지 렌즈와 이름이 비슷한데 반대입니다 — 그건 선을 밀어내서 가린 걸 드러냈고, 이건 선을 지워서 남길 걸 고릅니다. 밀어내기는 밀도가 낮을 때, 지우기는 밀도가 아주 높을 때 씁니다.
진짜 배울 점은 nodeType 옵션 하나예요 — 무엇을 "안에 있다"고 볼 것인가를 네 가지로 나눕니다.
both(양 끝이 다 안) · either(한쪽만 안이어도) · source(출발점이 안) · target(도착점이 안).
같은 원을 올려도 묻는 질문이 완전히 달라져요 — both는 "이 동네 안에서 무슨 일이 있나",
either는 "이 동네가 바깥과 어떻게 엮이나", source는 "여기서 나가는 게 어디로 가나".
렌즈를 만들 때 반경보다 이 선택을 먼저 정해야 합니다.
앞의 포커스 디밍이 "이 선 하나"를 남긴다면, 이건 미리 정해둔 여러 홉을 한 덩어리로 켭니다. 질문이 "이 선이 어디로 가나"가 아니라 "이 요청은 어떤 길로 지나가나"일 때 쓰는 쪽이에요. 경로는 정의해 두는 것이지 계산하는 게 아닙니다 — 아무 노드나 골라 최단경로를 찾아주는 건 이 컬렉션 밖(자동 경로탐색)입니다.
배울 점은 죽이는 깊이를 하나로 두지 않는다는 겁니다. 실측된 배분은 선 13% · 노드 30% · 영역 45%. 노드를 선만큼 죽이면 경로가 어디를 지나는지가 같이 사라지고, 배경 경계까지 죽이면 사용자가 외운 지도가 없어져서 켤 때마다 위치를 다시 찾습니다. 포커스 디밍 카드의 사다리가 "질문이 좁을수록 깊게"라면, 이건 같은 상태 안에서 역할별로 나누는 축입니다.
그리고 켜는 것만으로는 순서가 안 나옵니다. 원본 아티팩트는 순서를 그림 밖 단계 목록으로 뺐는데 (재보면 그림 안 번호 배지용 CSS는 만들어져 있고 끝내 쓰이지 않았습니다), 그러면 눈이 그림과 목록을 왕복해요. 홉마다 번호를 선 위 중간에 얹으면 한 번에 읽히고, 정지 이미지에도 순서가 남습니다 — 행진 대시로 방향을 주는 방식은 PNG로 내보내는 순간 사라지니까요.
마지막 함정. 켤 때 stroke만 바꾸면 화살촉은 원래 색으로 남습니다.
원본은 마커를 두 벌 만들어 marker-end를 갈아끼웠고, 이 카드는 화살촉 색 상속 카드의 값 하나로 처리했습니다 —
지금 이 그림에서 켜질 때 촉이 같이 물드는 게 그겁니다.
항목마다 "왜 넣었나"는 카드에 적혀 있습니다. 여기는 그 반대 — 봤는데 안 넣은 것과 그 이유입니다. 고르는 목록이 쓸모 있으려면 고르지 않은 이유도 같이 있어야 하니까요.
conic-gradient+마스크, radial-gradient 여러 겹으로 쌓는 글로우. SVG 도형을 채우거나 칠하지 못합니다.feMorphology로 다발 전체를 감싸는 테두리)은 두 번 만들었는데
데모에서 개별 케이싱과 차이가 안 보여서 뺐습니다 — 선 간격이 팽창 반경의 2배 안일 때만 달라지는데, 그 조건이 실제 도면에서 드뭅니다.
보이지 않는 이점은 카드로 만들지 않았습니다.22 −10이었는데, 절편이 기울기를 따라가야 한다는 걸 나중에 알고
22 −8.67로 고쳤습니다(목 두께 9px → 14px 실측). 다발 묶기도 사실상 β=1로 그려져 있던 걸 뒤늦게 확인해
강도 다이얼 카드를 따로 세웠습니다. 발광은 한때 "Canvas 전용"으로 잘못 분류했다가 회수했습니다.5 4 하나로 고정인데,
이 컬렉션의 굵기 상대 대시(대시 = 굵기×2)와 충돌하고 고정값은 획이 2~3px일 때만 맞는 값이거든요.
라벨 처리도 "선 옆에 두고 24px 띄운다"는 회피 규칙만 있고 마스크·헤일로 규격이 없어서,
기존 라벨 마스킹 카드 쪽이 낫다고 판단했습니다.pathLength="1" + 분수 대시로 길이를 정규화하는 방식도
좋았지만 이미 있는 pathLength="100"과 같은 아이디어라 카드를 세우지 않았습니다.marker-end를 두 번째 마커로 갈아끼워 화살촉 색을 맞추고 있었는데,
같은 문제를 마커 하나로 푸는 값(context-stroke)이 있어서 화살촉 색 상속을 세웠고 —
복제는 이 컬렉션의 공용 defs에도 여섯 벌 있던 문제라 남 얘기가 아니었습니다 —
경로별 하이라이트에서 죽이는 깊이를 요소 역할별로 나눈 배분(선 13 / 노드 30 / 영역 45)을 읽어 경로 켜기를 세웠습니다.
거기서 순서 배지용 CSS가 만들어져 있는데 그림에는 한 번도 안 쓰이고 순서가 그림 밖 목록으로 나가 있는 것도 확인해서,
카드에서는 번호를 그림 안으로 넣었습니다.<g> 하나만 변환 · 커서 밑의 점 고정 ·
드래그 임계값으로 클릭 억제 · grab 커서 · 배율 표시와 리셋)은 잘 정리돼 있지만 연결선이 아니라 씬 조작 UI라 범위 밖입니다.
질문 → 다이어그램 문법 매핑표(토폴로지 · 시퀀스 · 프로세스 · 상태 · 계층 · 타임라인 · 매트릭스)와
매체 고르기(HTML/CSS · SVG · Canvas · WebGL)도 좋은 지침이지만 다이어그램 종류·설계 쪽이라,
예전에 9종 타입팩을 뺀 것과 같은 이유로 안 가져왔습니다.
"똑같은 라운드 박스 벽지를 피하라"와 존·경계 박스는 노드 표현이고, 코디네이티드 뷰·레이어 토글은 화면 구성입니다.[data-pos=b])로 잡아서 morph 가
속성 하나만 바꾸면 자리도 바뀌었는데, SVG 에서는 자리가 좌표(x)라 같은 코드가 흐려지기만 하고 안 바뀝니다.
"위치는 건드리지 않는다"를 문자 그대로 옮기면 깨지는 자리라 카드에 함께 적었습니다.data-cat과 data-export(고유 ID)를 달고,
④ 움직이면 data-anim + sampleCycle()에 한 사이클 길이를 등록하고,
⑤ 06에 버린 것까지 적습니다.
기존 항목과 기법이 같고 상황만 다르면 추가하지 않습니다 — 컬렉션이 커지는 게 목적이 아니라 고를 수 있는 게 목적입니다.