🧩 Design System8. Pipeline & DistributionAdoption Metrics — 디자인 시스템 사용률을 측정하는 법

Adoption Metrics — 디자인 시스템 사용률을 측정하는 법

이 문서가 답하는 질문: 우리 디자인 시스템이 실제로 쓰이고 있는지 어떻게 알 수 있는가? 한 줄 답 (Pyramid Top): adoption은 다운로드 수가 아니라 raw 값 vs 토큰 비율컴포넌트 import 비율 로 측정한다. 토큰을 우회한 raw #3b82f6이나 inline style={{}}이 1%만 되어도 디자인 시스템 contract는 깨진 것이다.


Why — 왜 존재하는가

디자인 시스템 팀이 가장 흔히 답할 수 없는 질문 3개:

  1. “우리 시스템을 얼마나 쓰는가?”
  2. “어느 이 가장 잘 쓰고 가장 못 쓰는가?”
  3. “어느 컴포넌트가 외면받는가, 왜인가?”

PM과 임원은 ROI를 묻는다. 디자이너는 일관성이 깨지는 곳을 묻는다. 엔지니어는 이전 시스템에서 마이그레이션이 완료됐는지 묻는다. 이 모든 질문에 데이터로 답할 수 있어야 디자인 시스템 팀의 존재 가치가 증명된다.

지표답하는 질문측정법
Token coverage”raw hex 색을 토큰으로 얼마나 대체했나”grep #[0-9a-fA-F]{6} 비율
Component import ratio”Button을 @org/ds에서 import vs 직접 만든 비율”AST 분석
Variant usage distribution”어느 variant가 안 쓰여 죽었나”빌드 시점 정적 분석
Visual diff frequency”Chromatic에서 unintended diff 비율”Chromatic API
npm downloads”내부 npm 다운로드 추이”npm registry
Onboarding time”신규 멤버가 첫 컴포넌트 PR 내는 시간”git log 분석

How — 어떻게 동작하는가

원칙:

  • 정적 분석 우선 (CI에서 실행, 사용자 코드 접근 가능).
  • 런타임 telemetry는 opt-in (privacy 이슈).
  • 임계치는 경고이지 차단이 아니다 (디자인 시스템은 enabler지 gatekeeper가 아니다 — 7장 7-3 governance).

What — 구체 사양·수치·예시

1) Raw hex 색 찾기 (Token coverage)

# 모든 raw hex 색 (3자리·6자리·8자리)
rg --json -t typescript -t css '#[0-9a-fA-F]{3,8}\b' src/ \
  | jq '.data.path.text' \
  | sort -u > raw-hex.txt
 
wc -l raw-hex.txt  # 위반 건수
# 토큰 사용 vs raw 사용 비율
TOKEN_REFS=$(rg -t typescript 'var\(--colors-[a-z-]+\)|colors\.[a-z]+\.[0-9]+' src/ | wc -l)
RAW_HEX=$(rg -t typescript '#[0-9a-fA-F]{6}\b' src/ | wc -l)
 
echo "Token coverage: $TOKEN_REFS / $((TOKEN_REFS + RAW_HEX))"

목표: 95% 이상. raw가 신규 PR에서 늘어나지 않는 것이 핵심.

2) Inline style 검출

# style={{ color: '#...' }} 같은 inline 스타일
rg -t tsx -t jsx 'style=\{\{' src/ --count-matches

inline style은 토큰 우회의 가장 흔한 경로. 컴포넌트 props로 우회 가능 (color="primary").

3) Component import ratio

// scripts/analyze-imports.ts
import { Project } from 'ts-morph'
 
const project = new Project({ tsConfigFilePath: './tsconfig.json' })
const counts: Record<string, number> = {}
 
project.getSourceFiles('src/**/*.{ts,tsx}').forEach((file) => {
  file.getImportDeclarations().forEach((imp) => {
    const mod = imp.getModuleSpecifierValue()
    if (mod === '@org/design-system') {
      imp.getNamedImports().forEach((named) => {
        const name = named.getName()
        counts[name] = (counts[name] || 0) + 1
      })
    }
  })
})
 
console.table(counts)
// { Button: 234, Input: 187, Modal: 12, DataTable: 3 }

DataTable: 3 같은 외면받는 컴포넌트가 보임 → 인터뷰 → 사용성 개선.

4) Variant 사용 분포

// JSX prop 분석
project.getSourceFiles().forEach((file) => {
  file.forEachDescendant((node) => {
    if (node.getKind() === SyntaxKind.JsxOpeningElement) {
      const tagName = node.getTagNameNode().getText()
      if (tagName === 'Button') {
        const variantAttr = node.getAttribute('variant')
        const variant = variantAttr?.getInitializer()?.getText() || 'default'
        // 카운트
      }
    }
  })
})

결과 예시: primary: 412, secondary: 87, danger: 23, ghost: 4 → ghost는 죽은 variant.

5) Chromatic visual diff metric

// scripts/chromatic-stats.ts
import { ChromaticAPI } from '@chromatic-com/sdk'
 
const recentBuilds = await ChromaticAPI.getBuilds({ limit: 30 })
const unintendedDiffs = recentBuilds.filter((b) =>
  b.specs.some((s) => s.status === 'changed' && !s.intentional)
).length
 
console.log(`Unintended visual diff rate: ${unintendedDiffs / 30}`)

5% 이상이면 디자인 시스템의 기반 변경이 너무 잦거나, 컴포넌트 의도가 불명확하다는 신호.

6) Dashboard 예시 (대시보드 코드)

// scripts/ds-metrics-dashboard.ts
import { collectMetrics } from './collectors'
 
const metrics = await collectMetrics()
 
console.log(`
=== Design System Adoption Report ===
📦 Component imports
   ${metrics.componentImports.total} total
   Top: ${metrics.componentImports.top3.join(', ')}
   Dead: ${metrics.componentImports.unused.join(', ')}
 
🎨 Token coverage
   ${metrics.tokenCoverage.percent}% (target: 95%)
   Raw hex: ${metrics.tokenCoverage.rawCount}
   Inline style: ${metrics.inlineStyleCount}
 
🎭 Variant usage (Button)
   ${Object.entries(metrics.variants.Button).map(([k, v]) => `${k}: ${v}`).join('\n   ')}
 
📈 Trend (vs last week)
   Token coverage: ${metrics.trend.coverage > 0 ? '↑' : '↓'} ${Math.abs(metrics.trend.coverage)}pp
`)

CI에 주 1회 실행, Slack에 결과 전송.

7) npm 내부 통계

# 내부 verdaccio·jfrog 등 prviate registry
curl -s 'https://npm.internal/-/api/v1/package/@org/design-system' \
  | jq '.versions | to_entries | map({version: .key, downloads: .value.downloads}) | .[0:5]'

다운로드 추이로 마이그레이션 진행도 측정 가능 (이전 버전 사용 점진 감소 패턴).


What-if — 잘못 쓰면 어떻게 깨지는가

  • 다운로드 수만 본다: CI가 매번 npm install하므로 진짜 사용과 무관. 정적 분석이 우선.
  • 임계치를 PR 차단으로 사용: “raw hex 추가 시 빌드 실패” → 우회 hack 등장(/* eslint-disable */). 경고·리뷰로.
  • 변경 추이를 무시: “현재 90%면 됐다” → 신규 PR에서 증가하는 위반을 못 잡음. 항상 delta를 모니터링.
  • 변동을 너무 자주 모니터링: 매 커밋마다 측정 → 노이즈. 주 1회 trend 추적이 적절.
  • death by metric: 30개 지표 추적 → 아무도 안 봄. 3~5개 핵심 지표에 집중.
  • privacy 위반 telemetry: 컴포넌트가 사용자 클릭을 외부 서버로 전송 → GDPR 위험. 런타임 분석은 opt-in + 익명화만.

Insight — 흥미로운 이야기

Salesforce Lightning Design System(2014~)은 adoption metrics라는 용어를 디자인 시스템 도메인에 처음 들여온 팀 중 하나다. 그들의 첫 dashboard는 단순히 “내부 사이트가 slds- 클래스를 얼마나 쓰는가”였고, 첫 측정에서 30%였다. 5년 후 85%로 올렸는데, 어떻게 올렸느냐가 중요하다 — 경고하지 않고 보여주기만 했다. 팀별 dashboard를 만들어 “당신 팀은 60%, 옆 팀은 80%“라고만 노출. 사회적 압력이 metric을 끌어올렸다.

Airbnb DLS(Design Language System) 팀은 2019년 “Component Usage Metric은 컴포넌트의 발견 가능성을 측정하는 것”이라고 정의했다. 이 정의의 함의: 안 쓰는 컴포넌트가 나쁜 게 아니라 발견되지 않거나 이름이 잘못된 것일 수 있다. DataTable이 안 쓰이면 → 이름을 Table로 바꾸거나, Storybook 검색 우선순위 올리기.

가장 흥미로운 반전: adoption metric은 디자인 시스템 팀의 성공 지표가 아니라 제품 팀의 진단 도구다. 팀이 자기 코드의 일관성을 스스로 측정하게 만들면 디자인 시스템 팀이 강제하지 않아도 된다.


요약

  • adoption은 token coverage + component import ratio + variant 분포로 측정.
  • npm 다운로드 수는 secondary signal.
  • 정적 분석 우선 (ripgrep, ts-morph), 런타임 telemetry는 opt-in.
  • 임계치는 경고차단이 아니다 (자기파괴적 hack 등장 위험).
  • delta(추세)가 절대값보다 중요.