Communitygithub.com

bitovi/ai-enablement-prompts

기준 URL과 개발(dev) 또는 Storybook URL을 Playwright 스크린샷으로 여러 브레이크포인트에서 비교하고, 픽셀 수준의 이미지 차이(diff)를 분석하여 스타일 및 HTML 수정을 안내합니다. 기존 페이지나 컴포넌트를 복제하거나, 시각적 정확성을 확인하거나, 반응형 충실도를 검증할 때 사용하세요.

ai-enablement-prompts란 무엇인가요?

이 스킬은 두 URL(기준: 프로덕션 사이트, 현재: 로컬 개발 서버나 Storybook 스토리)을 시각적으로 비교하여 픽셀 단위의 차이를 찾고, 이를 바탕으로 CSS/HTML 수정을 안내하는 Visual Diff 기능입니다. Playwright MCP를 사용해 여러 브레이크포인트(모바일, 태블릿, 데스크톱)에서 스크린샷을 캡처하고, pixelmatch와 pngjs 라이브러리를 이용해 이미지 차이를 분석합니다. 컴포넌트 범위(component-scoped)와 전체 페이지(full-page) 비교를 모두 지원하며, 스토리북 iframe URL을 사용한 깔끔한 스크린샷, 자동 크롭 및 패딩 처리, diff 임계값 조정 등의 고급 기능을 제공합니다. 에이전트는 시각적 diff 이미지를 직접 보고 빨간색(차이), 노란색(안티앨리어싱) 영역을 해석하여 수정할 부분을 정확히 파악할 수 있습니다. 주로 VS Code에서 Claude Code, Cursor, Codex 등과 함께 사용되며, `.vscode/mcp.json`에 Playwright MCP 설정과 `scripts/visual-diff.mjs` 스크립트가 필요합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/bitovi/ai-enablement-prompts/tree/HEAD/plugins/playwright/skills/visual-diff

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

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

문서

스킬: Visual Diff

두 URL(기준, 예: 프로덕션 사이트와 현재, 예: 로컬 개발 서버 또는 Storybook 스토리)을 시각적으로 비교하여 픽셀 수준의 차이를 찾고 수정을 안내합니다.


사용 시기

  • 기존 페이지나 섹션을 Storybook 또는 Astro 사이트에서 복제할 때
  • 컴포넌트의 시각적 정확성을 참조 디자인과 비교하여 확인할 때
  • 스타일 변경 후 브레이크포인트별 반응형 충실도를 검사할 때
  • CSS/HTML을 반복적으로 수정하여 컴포넌트가 참조와 일치할 때까지 개선할 때

사용하지 말아야 할 경우

  • 콘텐츠만 변경된 경우(텍스트 업데이트, 카피 편집)로 레이아웃이 변경되지 않은 경우
  • Figma 디자인과 비교하는 경우 - Figma MCP 스킬을 사용하세요.
  • 성능 또는 접근성 감사 - Lighthouse나 a11y addon을 사용하세요.

사전 준비 사항

시작하기 전에 다음을 확인하세요:

  1. Playwright MCP가 .vscode/mcp.json에 --caps=vision 옵션으로 구성되어 있어야 합니다.
  2. pixelmatch와 pngjs가 설치되어 있어야 합니다. package.json devDependencies를 확인하세요.
  3. 기준 URL에 접근 가능해야 합니다(외부 사이트 또는 실행 중인 서버).
  4. 현재 URL에 접근 가능해야 합니다(:4321의 개발 서버, :6006의 Storybook 등).
  5. 워크스페이스 루트에 temp/ 디렉터리가 존재해야 합니다(.gitignore에 포함됨).

개발 서버 또는 Storybook이 실행 중이지 않은 경우 VS Code 작업을 사용하여 시작하세요:

  • 개발 서버: Dev Server 작업
  • Storybook: Storybook 작업

워크플로우

1단계: 입력 받기

에이전트는 다음 입력이 필요합니다:

입력필수예시
기준 URL예https://www.bitovi.com/
현재 URL예http://localhost:4321/ 또는 http://localhost:6006/?path=/story/card--default
CSS 선택자아니오#hero-section 또는 .card-grid (요소 범위 비교용)
브레이크포인트아니오기본값: 모바일(375×667), 태블릿(768×1024), 데스크톱(1280×720)

2단계: 기준 스크린샷 촬영

컴포넌트 범위 비교 (단일 컴포넌트 비교 시 권장)

특정 컴포넌트를 비교할 때 – 특히 Storybook 스토리가 해당 컴포넌트만 렌더링하지만 기준 페이지는 전체 페이지를 포함하는 경우 – 컴포넌트의 경계 상자(bounding box)로 크롭하여 관련 없는 페이지 섹션(네비게이션 바, 고정 오버레이, 위/아래 모듈 등)이 diff 결과를 오염시키지 않도록 합니다.

기준 페이지에서 크롭하는 방법:

  1. 기준 URL로 이동합니다.
  2. 뷰포트를 { width: 1280, height: 720 }으로 설정합니다.
  3. 대상 요소가 보이도록 스크롤합니다:
    () => document.querySelector('<selector>').scrollIntoView({ behavior: 'instant', block: 'start' })
    
  4. 컴포넌트의 렌더링된 높이를 측정합니다:
    () => document.querySelector('<selector>').getBoundingClientRect().height
    
  5. 뷰포트 높이를 측정된 높이와 정확히 일치하도록 설정합니다(너비는 유지):
    mcp_playwright_browser_resize → { width: 1280, height: <측정된 높이> }
    
  6. 스크린샷을 촬영합니다 – 이제 컴포넌트 영역만 캡처됩니다.

배경 샘(bleed) 경고: 컴포넌트 뒤에 보이는 내용은 Storybook(빈 배경)과 기준 페이지(다른 모듈)에서 다릅니다. 일반적인 경우: sticky/local 네비게이션 바가 상단을 침범하거나, 모달/오버레이 뒤에 다른 페이지 콘텐츠가 보이거나, 부분적으로 투명한 컴포넌트입니다. 이러한 잔여 diff는 컴포넌트 자체 콘텐츠 외부에 클러스터링되며 수정 대상이 아닙니다 – 무시하세요.

Storybook 스토리에서 크롭하는 방법:

Storybook iframe.html은 주변 크롬 없이 컴포넌트만 렌더링합니다 – 크기 조정이 필요 없습니다. 그냥 이동하여 스크린샷을 촬영하세요:

mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=<story-id>&viewMode=story" }
mcp_playwright_browser_resize → { width: 1280, height: <기준 크롭과 같은 높이> }
mcp_playwright_browser_take_screenshot → { filename: "temp/vdiff-current-desktop.png" }

전체 페이지 뷰포트 스크린샷 (전체 페이지를 비교할 때)

1. mcp_playwright_browser_navigate → { url: "<baseline-url>" }
2. mcp_playwright_browser_resize → { width: 1280, height: 720 }
3. mcp_playwright_browser_wait_for → { time: 2 }       # 페이지가 완전히 렌더링될 때까지 대기
4. mcp_playwright_browser_take_screenshot → {
     type: "png",
     filename: "temp/vdiff-baseline-desktop.png"
   }

각 브레이크포인트에 대해 반복:

브레이크포인트너비높이파일명 접미사
모바일375667-mobile
태블릿7681024-tablet
데스크톱1280720-desktop

3단계: 현재 스크린샷 촬영

현재 URL에 대해 정확히 동일한 과정을 반복하고 temp/vdiff-current-{breakpoint}.png 파일명을 사용합니다.

중요: 양쪽에서 동일한 뷰포트 크기와 동일한 크롭 전략(컴포넌트 범위 또는 전체 페이지)을 사용하여 비교가 동등하도록 합니다.

4단계: diff 스크립트 실행

각 브레이크포인트에 대해 터미널에서 visual-diff 스크립트를 실행합니다:

node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-desktop.png \
  --current temp/vdiff-current-desktop.png \
  --output temp/vdiff-diff-desktop.png

스크립트는 JSON을 stdout으로 출력합니다:

{
  "totalPixels": 921600,
  "diffPixels": 4521,
  "diffPercent": 0.49,
  "width": 1280,
  "height": 720,
  "baselineDimensions": { "width": 1280, "height": 720 },
  "currentDimensions": { "width": 1280, "height": 720 },
  "dimensionsMismatch": false,
  "diffImagePath": "/absolute/path/to/temp/vdiff-diff-desktop.png"
}

dimensionsMismatch가 true인 경우, 이미지 크기가 서로 다릅니다. 스크립트는 비교 전에 작은 이미지에 투명 픽셀을 패딩합니다. 이는 종종 레이아웃 문제를 나타냅니다.

5단계: diff 이미지 보기

임시 HTTP 서버를 시작하여 temp/ 디렉터리를 제공하고, Playwright를 diff 이미지로 이동합니다:

# 서버를 백그라운드에서 시작 (세션당 한 번 실행)
npx -y http-server temp/ -p 8787 --cors -c-1 &

그런 다음 Playwright를 diff 이미지로 이동합니다:

mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-desktop.png" }
mcp_playwright_browser_take_screenshot → {}   # vision 트리거 – 에이전트가 diff를 볼 수 있음

Playwright MCP의 --caps=vision 플래그 덕분에 에이전트가 스크린샷 출력에서 이미지를 직접 볼 수 있습니다.

참고: file:// URL은 Playwright MCP에서 차단되므로 HTTP 서버가 필요합니다. 세션 동안 한 번 시작하고 모든 diff 이미지에 재사용하세요.

diff 이미지 읽기:

  • 빨간색 픽셀 = 기준과 현재의 차이
  • 노란색 픽셀 = 안티앨리어싱 차이(보통 무시 가능)
  • 어두운 원본 = 일치하는 영역(컨텍스트를 위해 30% 불투명도로 표시)
  • 큰 빨간색 클러스터 = 구조적 문제(누락된 요소, 레이아웃 변경, 잘못된 크기)
  • 흩어진 빨간색 점 = 서브픽셀 렌더링, 글꼴 스무딩 또는 안티앨리어싱(보통 허용 가능)
  • 가장자리의 빨간색 띠 = 크기 불일치 또는 패딩/마진 차이

6단계: 해석 및 조치

수치적 JSON 데이터와 시각적 diff 검사를 결합합니다:

diffPercent해석조치
< 1%시각적으로 동일미미한 서브픽셀 차이만 있음. 조치 불필요.
1–5%거의 일치간격, 글꼴 두께 또는 테두리 차이에 대해 diff 이미지 검사. 작은 CSS 수정이 필요할 수 있음.
5–15%주목할 만한 차이레이아웃 변경, 색상 불일치 또는 누락된 요소. diff에서 빨간색 클러스터를 검토하여 작업이 필요한 섹션 식별.
> 15%큰 차이주요 구조적 또는 스타일링 차이. 가장 큰 빨간색 영역부터 집중 – 이는 가장 큰 레이아웃 불일치를 나타냄.

diff 이미지를 해석할 때 다음을 주목하세요:

  • 빨간색 클러스터가 페이지의 어디에 있는가? (상단, 중간, 하단, 왼쪽, 오른쪽)
  • 특정 요소와 연관되어 있는가? (네비게이션, 히어로, 카드, 푸터)
  • 클러스터가 간격 문제, 색상 차이 또는 누락된 콘텐츠를 나타내는가?

7단계: 수정 및 재-diff

  1. diff 분석에 따라 CSS/HTML 수정
  2. 3~6단계를 반복(현재 URL만 다시 스크린샷 촬영)
  3. diffPercent가 허용 가능한 수준에 도달할 때까지 계속

일반적인 목표:

  • 정확한 복제: < 2%
  • 합리적 일치: < 5%
  • 구조적 일치(다른 콘텐츠): < 15%

전체 페이지 스크린샷

기본적으로 스크린샷은 보이는 뷰포트만 캡처합니다. 긴 페이지의 경우 fullPage 옵션을 사용하세요:

mcp_playwright_browser_take_screenshot → {
  type: "png",
  fullPage: true,
  filename: "temp/vdiff-baseline-desktop-full.png"
}

경고: 전체 페이지 스크린샷은 큰 이미지를 생성하고 diff 속도가 느려집니다. 전체 페이지 레이아웃을 비교할 때만 사용하세요.


Storybook 특화 팁

Storybook 스토리를 스크린샷할 때:

  1. iframe URL을 사용하여 깔끔한 스크린샷 확보(Storybook 크롬 없음):

    http://localhost:6006/iframe.html?id=components-card--default&viewMode=story
    
  2. 렌더링 대기 – Storybook 스토리는 하이드레이션에 시간이 걸릴 수 있음:

    mcp_playwright_browser_wait_for → { time: 3 }
    
  3. 요소 스크린샷은 Storybook 패딩에서 스토리 콘텐츠를 분리하는 데 효과적입니다.


임계값 조정

--threshold 플래그(0~1)는 pixelmatch 감도를 제어합니다:

값감도사용 사례
0.05매우 엄격정확한 픽셀 일치, 모든 것을 포착
0.1기본좋은 균형, 대부분의 안티앨리어싱 무시
0.2관대시스템 간 글꼴 렌더링 차이를 허용
0.3매우 관대주요 색상/레이아웃 차이만 포착
node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-desktop.png \
  --current temp/vdiff-current-desktop.png \
  --output temp/vdiff-diff-desktop.png \
  --threshold 0.2

예시: 히어로 섹션 비교하기

# 1. 기준 스크린샷 (프로덕션 사이트 히어로)
mcp_playwright_browser_navigate → { url: "https://www.bitovi.com/" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 2 }
mcp_playwright_browser_snapshot → {}
# 히어로 섹션의 ref 찾기, 예: ref="hero-[1]"
mcp_playwright_browser_take_screenshot → {
  type: "png",
  element: "hero section",
  ref: "hero-[1]",
  filename: "temp/vdiff-baseline-hero-desktop.png"
}

# 2. 현재 스크린샷 (Storybook 스토리)
mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=components-hero--default" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 3 }
mcp_playwright_browser_take_screenshot → {
  type: "png",
  filename: "temp/vdiff-current-hero-desktop.png"
}

# 3. diff 실행 (터미널에서)
node scripts/visual-diff.mjs \
  --baseline temp/vdiff-baseline-hero-desktop.png \
  --current temp/vdiff-current-hero-desktop.png \
  --output temp/vdiff-diff-hero-desktop.png

# 4. 임시 서버 시작 (세션당 한 번, 백그라운드 터미널에서)
npx -y http-server temp/ -p 8787 --cors -c-1 &

# 5. Playwright를 통해 diff 이미지 보기
mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-hero-desktop.png" }
mcp_playwright_browser_take_screenshot → {}   # vision 트리거

# 6. 해석: JSON 출력 읽기 + 시각적으로 diff 검사
# 7. 스타일 수정, 현재 스크린샷 재촬영, 다시 diff

정리

모든 출력 파일은 temp/ 디렉터리에 저장되며, 이는 .gitignore에 포함됩니다. 세션 후 정리하려면:

rm -f temp/vdiff-*.png

관련 스킬