모든 글
뉴스 해석2026-09-179 min read

다이어그램 스킬에서 가져올 것은 19KB짜리 검증 스크립트다

별 4만 개짜리 diagram-design 스킬을 뜯어보니, 검증기가 지키는 건 4px 격자가 아니라 접근성과 외부 참조 화이트리스트였다. 에이전트 산출물에 붙일 검사를 고르는 기준.

에이전트 스킬 저장소 하나가 다섯 달 만에 별 4만 개를 받았다. 다이어그램을 그려주는 스킬이다. 그런데 거기서 내 저장소에 복사할 만한 파일은 SKILL.md도, 40개의 타입 레퍼런스도 아니다. scripts/self_check.py 하나다.

에이전트가 뱉어낸 HTML을 열어보기 전에 무엇을 떨어뜨릴지 코드로 적어둔 파일이고, 그 검사 목록이 예상과 다르기 때문이다.

출처: github.com/cathrynlavery/diagram-design (MIT)

확인된 것

저장소를 직접 읽고 확인한 것만 적는다.

  • 2026-04-16 생성, 2026-09-15 마지막 푸시. 확인 시점 별 40,648개, 포크 2,592개.

  • Claude Code / Codex / Factory Droid / Pi에 각각의 플러그인 매니페스트로 설치된다. Claude Code는 /plugin marketplace add cathrynlavery/diagram-design.

  • skills/diagram-design/references/에 레퍼런스 56개, 그중 type-*.md가 40개. 평상시 요청 하나에 로드되는 건 SKILL.md + 타입 파일 1개다. README가 그 계약을 이렇게 쓴다.

    No matter how many types exist, the agent only reads the one you need.

  • 디자인 규칙은 산문으로 명시돼 있다. 강조도 세다.

    Every coord, width, and gap divisible by 4 — non-negotiable, it's what keeps the diagrams from feeling AI-generated.

  • 그런데 self_check.py의 검사 함수는 넷뿐이다: check_css_references, check_svgs, check_scripts, check_motion. 격자·강조색·라운드·그림자를 검사하는 코드는 없다.

  • 실제로 검사하는 것: SVG의 role="img", <title>이 첫 자식인지, aria-labelledby가 title→desc 순서인지, ID가 다이어그램별로 접두됐는지. <script>는 최대 1개이며 정본 컨트롤러와 정확히 일치해야 한다. 모션은 스텝 2~8개, 아이템 12개 상한, 한 스텝에 의미 있는 아이템 2개까지.

  • 원격 참조는 화이트리스트 하나뿐이다.

    parsed.hostname.casefold() == "fonts.googleapis.com"
    and parsed.port is None
    and parsed.path == "/css2"
    and not parsed.fragment
    

    그 밖의 원격 참조는 전부 에러. javascript:와 data:text/html은 차단, data:는 data:image/만 통과.

판단

1. 검증기에는 "틀려도 눈에 안 띄는 것"만 넣는다

README는 4px 격자를 "non-negotiable"이라고 쓰지만, 기계가 지키는 건 그게 아니다. 격자가 어긋난 다이어그램은 사람이 보면 5초 만에 안다. 하지만 스크린리더에서 이름이 없는 SVG, 다른 다이어그램과 ID가 충돌하는 aria-labelledby, 슬쩍 끼어든 외부 스크립트는 아무도 모르는 채로 배포된다.

이 저장소의 분업은 그 기준대로 갈려 있다. 미학은 프롬프트에 남기고, 조용히 새는 것만 스크립트로 막는다. 내 스킬에 검증 단계를 붙일 때 쓸 규칙이 여기서 나온다 — 눈에 보이는 실패를 검사기에 넣는 건 중복이다. 검사할 가치가 있는 건 성공한 것처럼 보이는 실패뿐이다.

2. 이건 다이어그램 얘기가 아니라 에이전트 산출물의 CSP다

위 화이트리스트를 다시 보자. 허용 호스트 하나, 경로 하나, 포트 없음, 프래그먼트 없음. 스크립트는 정본과 바이트 단위로 같아야 하고, 이미지가 아닌 data URL은 못 들어온다. 이건 디자인 규칙이 아니라 콘텐츠 보안 정책이다.

에이전트에게 HTML·대시보드·리포트·설정 파일을 만들게 하고 그걸 브라우저에서 열거나 사용자에게 보내고 있다면, 검토할 질문은 "예쁜가"가 아니다. 이 파일이 어디에 접속할 수 있고 무엇을 실행할 수 있나. 모델은 잘 만들라고 하면 잘 만들려고 하지, 못 하게 막아달라고 부탁하지 않는다. 그 답을 19KB 스크립트로 적어둔 예시가 지금 MIT로 공개돼 있다.

덧붙이면, "오프라인으로 열린다"와 "Google Fonts를 부른다"는 같이 성립하지 않는다. README 스스로 no network requests beyond Google Fonts라고 단서를 단다. 사내망이나 에어갭 환경에 넣을 생각이면 폰트를 로컬로 내리는 작업이 별도로 필요하다.

3. 스킬을 만들 때 먼저 쓸 것은 "무엇이 언제 로드되는가" 표다

레퍼런스 56개짜리 스킬인데 일상 요청에서 로드되는 건 두 파일이다. 주목할 건 README가 성공 기준을 로드되지 않은 것으로 적었다는 점이다 — "정확히 하나의 타입 레퍼런스, 그 외 아무것도".

스킬을 쓰는 순서를 뒤집을 근거가 된다. 레퍼런스 파일부터 쓰지 말고 이 표부터 쓴다. 표의 한 행을 채우는 데 결국 전부 필요하다면, 그 스킬은 쪼개진 척하는 한 파일이다. 파일을 나눠도 컨텍스트에 통째로 올라가면 나눈 의미가 없다.

4. 반대로 — 40개 타입은 그 스킬 자신의 원칙과 충돌한다

README의 첫 번째 원칙은 "The highest-quality move is usually deletion." 인데 타입은 40개다. 점진적 공개 덕에 토큰 비용은 안 늘지만 선택 비용은 는다. 40개 중에 고르는 주체는 에이전트고, 잘못 고른 타입은 self_check.py가 못 잡는다. 구조와 접근성만 보기 때문이다.

즉 실패 지점이 렌더링에서 라우팅으로 옮겨갔다. 타입이 늘수록 잘못 그린 다이어그램은 줄고 잘못 고른 다이어그램은 는다. 도입한다면 초반에는 "플로우차트로 그려줘"처럼 타입을 명시해서 부르고, 에이전트가 알아서 고르게 두는 건 몇 번 결과를 본 뒤가 낫다. (이건 저장소에 없는 내 추론이다.)

모르는 것

  • 직접 설치해서 돌려보지 않았다. self_check.py는 읽었지만 생성물에 실행해보지 않았다. 위 판단은 전부 코드와 README를 읽은 결과다.
  • 타입 선택 정확도에 대한 수치가 저장소에 없다. 4번은 구조에서 나온 추론이지 측정된 문제가 아니다.
  • 별 40,648개는 설치 수가 아니다. 저장소에 Trendshift 배지가 달려 있어 노출 경로도 따로 있다. 인기와 실사용을 같은 숫자로 쓰면 안 된다.
  • 타입 개수가 세 군데서 다르다. 저장소 description은 38, README 본문은 39, references/의 type-*.md는 40개다. 어느 것이 맞는지 확인하지 못했다. 중요한 숫자는 아니지만, 한 저장소 안에서도 숫자가 안 맞는다는 건 기억해둘 만하다.
  • 브랜드 온보딩의 WCAG 대비 검사와 폰트 대조는 README 서술이고, 실행해 확인하지 않았다.

댓글

불러오는 중...