Communitygithub.com

lyonsv/demo-video-skill

스크립트 기반 데모 비디오를 1440×900 MP4 형식으로 녹화합니다. Playwright로 헤드리스 Chrome을 구동하고 ffmpeg로 인코딩하며, 일관된 하우스 포맷(흰색 타이틀 카드, 필 캡션, 클릭 리플, 빨리 감기)을 사용합니다.

demo-video-skill란 무엇인가요?

이 스킬은 실제 실행 중인 웹 애플리케이션의 스크립트 데모 비디오를 제작합니다. Playwright를 통해 헤드리스 Chrome을 제어하여 모든 UI 상호작용을 녹화하고, ffmpeg로 인코딩하여 1440×900 해상도, 30fps, H.264 코덱의 MP4 파일을 생성합니다. 데모 비디오는 일관된 '하우스 포맷'을 따릅니다. 흰색 타이틀 카드와 엔드 카드, 하단 중앙의 어두운 필 캡션, 클릭 전 보라색 리플 애니메이션, 느린 부분(모델 응답, 작업 실행 등)은 약 2초로 압축됩니다. 각 비디오는 전용 데모 저장소(demos repo)의 폴더에 저장되며, `demo.json` 매니페스트로 세그먼트를 관리합니다. 이 스킬은 Claude Code, Cursor, Codex 등 AI 에이전트와 함께 사용하도록 설계되어, 사용자가 '데모 비디오 제작', '제품 워크스루', '기능 소개 영상', '아키텍처 다이어그램 애니메이션' 등을 요청할 때 활용됩니다. 주요 스크립트로는 `find-demos.js`(비디오 목록 조회), `record.js`(전체 녹화 및 인코딩), `demo-kit.js`(워크스루 스크립트 라이브러리), `review.js`(결과 검토) 등이 있습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lyonsv/demo-video-skill/tree/HEAD

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

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

문서

데모 비디오

실제 실행 중인 앱에서 녹화한 스크립트 기반 워크스루입니다. 시청자는 제품이 실제 작업을 수행하는 모습을 보게 됩니다. 느린 부분(모델 응답, 작업)은 빨리 감기 처리되고, 각 장면에는 주목할 점을 설명하는 짧은 캡션이 표시됩니다. 모든 비디오는 동일한 포맷을 사용하므로 여러 세그먼트를 하나의 비디오로 연결할 수 있습니다.

하우스 포맷

새로운 세그먼트가 이전 세그먼트와 일관되도록 다음 사항을 정확히 준수하세요.

  • 비디오: 1440×900, 30fps, H.264 (libx264 -preset slow -crf 18), yuv420p, +faststart, 오디오 없음.
  • 타이틀 및 엔드 카드: 전체 흰색 화면, 중앙 정렬: 옵션으로 작은 대문자 회색 브랜드 라인(kit.start에서 brand 설정, 기본값 없음), 46px 굵은 제목, 20px 회색 부제목. 약 2.8초 유지 후 페이드 아웃. 세그먼트는 타이틀 카드로 시작하며, 마지막 세그먼트는 한 줄 요약 카드(예: "아이디어 → 평가 → 라이브 테스트 → 출시" / "중간에 앱 배포 없음")로 끝납니다.
  • 캡션: 어두운 필(rgba(17,17,17,.88)), 흰색 17px 세미볼드 Lato, 하단 중앙. 캡션당 하나의 아이디어, 약 60자 이하, 문장체, 마침표 없음, 현재형, 시청자 관점에서 작성("결과가 채팅 옆에 아티팩트로 열립니다"). 장면이 바뀔 때 캡션을 변경하고, 각각 약 2.5~4초 유지합니다.
  • 클릭: 보라색 리플(rgba(122,71,255,…))이 클릭 직전 대상에 나타납니다.
  • 타이핑: 키별로 표시됨(키당 약 25ms, 긴 텍스트의 경우 14ms).
  • 대기: 느린 작업은 ff() 내부에서 실행되며 약 2초 안에 재생됩니다. 나머지는 실시간입니다.
  • 아키텍처 장면: 고정된 1440×900 흰색 페이지(타이틀 → 이전 → 문제 강조 → 이후 → 메트릭 카운트업 → 기능 목록), Lato, 잉크 #111, 보라색 강조 #6f43d6; "이전" 태그는 회색, "이후" 태그는 보라색. assets/architecture-example.html이 참조 파일입니다. 해당 CSS와 구조를 재사용하고 내용만 변경하세요.

비디오 저장 위치: 데모 저장소

모든 비디오는 팀의 데모 저장소(기본 경로 ~/src/demos, 또는 $DEMOS_REPO)에 폴더를 가지며, 녹화 스크립트와 demo.json 매니페스트를 보관합니다. 제품 저장소에는 데모 코드가 포함되지 않습니다. 녹화 파일은 커밋되지 않습니다. 매니페스트의 output은 MP4 파일 위치를 지정합니다.

~/src/demos/
  package.json            playwright-core + ffmpeg-static (npm install 한 번 실행)
  <video>/
    demo.json             매니페스트
    architecture.html     "scenes" 세그먼트 (선택 사항)
    record-<part>.js      "walkthrough" 세그먼트, demo-kit으로 작성
    .build/               세그먼트별 MP4 및 프레임 (gitignored)

매니페스트 (demo.json):

{
  "name": "<video-name>",
  "title": "한 줄: 비디오가 보여주는 내용",
  "description": "대상 시청자와 전달하려는 요점",
  "owner": "이메일",
  "repos": ["녹화된 기능이 포함된 저장소, 브랜치 포함"],
  "needs": [{ "what": "서비스", "check": "2xx/3xx 응답을 반환하는 URL", "start": "시작 방법" }],
  "reset": "깨끗한 테이크를 얻는 방법과 녹화 후 남는 상태",
  "segments": [
    { "name": "architecture", "kind": "scenes", "page": "architecture.html", "timeline": "0:3000,1:4500" },
    { "name": "walkthrough", "kind": "walkthrough", "script": "record-walkthrough.js" },
    { "name": "intro", "kind": "video", "file": "~/path/to/existing.mp4" }
  ],
  "output": "~/Downloads/<name>.mp4"
}

이 스킬의 examples/sift/ 폴더는 참조 폴더입니다. 동일한 폴더에 포함된 작은 예제 앱의 두 세그먼트 비디오(장면 페이지와 실제 워크스루)가 포함되어 있습니다.

데모 저장소가 아직 클론되지 않은 경우, 생성하고(git init, 위 package.json, node_modules/, .build/, *.mp4, *.jpg, *.png, *-frames/, *-timeline.json을 포함하는 .gitignore 파일) 사용자에게 리모트가 아직 없다고 알리세요.

도구 (scripts/, 이 스킬 기준 상대 경로)

스크립트설명
find-demos.jsnode find-demos.js [root…] [--json]: 데모 저장소의 모든 비디오를 세그먼트, 저장소, 출력, 마지막 녹화 및 커밋 시간과 함께 나열합니다.
record.jsnode record.js <video-dir> [--only <segment>]: needs를 확인하고 각 세그먼트를 녹화 및 인코딩하여 .build/에 저장한 후 output으로 연결합니다(이전 비디오는 *.previous.mp4로 유지). 데모 저장소 루트에서 실행하세요.
demo-kit.js워크스루 스크립트용 라이브러리: kit.start()는 page, title/untitle, caption, tap, type, ff, scroll/scrollTop, ripple, navigate, startCast, finish를 제공합니다. 프레임은 디스크로 스트리밍됩니다.
encode.jsnode encode.js <frames-dir> <timeline.json> <out.mp4>: MP4를 빌드하고 ff() 윈도우를 압축합니다.
record-scenes.jsnode record-scenes.js <page.html> <out.mp4> "0:3000,1:4500,…": window.step(n)을 정의하는 장면 페이지를 녹화합니다.
concat.jsnode concat.js <out.mp4> <a.mp4> <b.mp4> …: 세그먼트를 연결하고 하우스 포맷으로 다시 인코딩합니다.
review.jsnode review.js <video.mp4> <out-dir> [stills] [at=s,s,…]: 스틸 이미지의 2×2 콘택트 시트를 생성하여 검토합니다.

assets/example-walkthrough.js는 키트를 사용한 완전한 예제입니다(examples/sift/record-walkthrough.js는 실제 예제). 새 워크스루를 작성하기 전에 이 파일을 읽으세요. 비디오 중간에 앱 전환, 빨리 감기 작업, 엔드 카드 등의 구조를 보여줍니다.

워크플로우

0. 기존 항목 확인

먼저 find-demos.js를 실행하세요. 사용자가 이미 폴더가 있는 비디오를 다시 녹화하거나 확장하려면 해당 폴더에서 작업하세요. UI 변경 후에는 record.js <folder>(또는 --only <segment>)로 다시 녹화하거나 매니페스트에 세그먼트를 추가하세요. 같은 비디오에 대해 두 번째 폴더를 만들지 마세요. 새로운 비디오에 대해서만 새 폴더를 만들고, 비디오 이름(케밥 케이스)으로 지정하세요.

1. 먼저 스토리보드 작성

코드 작성 전에, 각 섹션을 번호로 나눈 스토리보드를 작성하세요. 각 섹션에는 화면에서 일어나는 일과 캡션을 포함하세요. 다음을 미리 결정하세요.

  • 실제여야 하는 것과 시뮬레이션할 수 있는 것. 실제(실제 모델 호출, 실제 검색)를 선호하세요. 데이터를 시뮬레이션해야 한다면(예: 몇 주 분량의 실시간 트래픽), 캡션과 앱 자체(레이블이 가능한 경우)에 화면에 표시하세요. 시청자는 데모를 있는 그대로 받아들이므로, 실제 결과로 제시된 시뮬레이션 결과는 오해를 불러일으킬 수 있습니다.
  • 클릭하지 말아야 할 것. 공유 환경에 쓰기 작업(보드 저장, 이메일 전송, 구매)을 수행하는 모든 것. 버튼을 보여주되 누르지 마세요.
  • 길이: 세그먼트당 약 60~90초. 3분짜리 연결 비디오도 괜찮습니다. 각 세그먼트는 한 가지 요점을 전달해야 합니다.

대화에서 사용자의 목표가 명확하다면, 작업하면서 업데이트에 스토리보드를 공유하세요. 비디오의 요점이 불분명할 때만 질문하세요.

2. 비디오 폴더 설정

<demos repo>/<video>/ 폴더를 만들고 demo.json을 작성하세요(비디오가 사용하는 모든 서비스에 대해 needs에 check URL을 채우고 reset을 기록). 데모 저장소 루트에서 node_modules가 없으면 npm install을 실행하세요. 키트와 스크립트는 작업 디렉터리에서 playwright-core와 ffmpeg-static을 확인하므로 모든 작업을 저장소 루트에서 실행하세요. Chrome이 설치되어 있어야 합니다(channel: 'chrome', Playwright 브라우저 다운로드는 필요 없음).

3. 앱 사전 점검

  • 비디오가 사용하는 모든 서비스가 작동 중인지 확인하고(각 URL을 curl로 확인), 로컬 HTTPS가 작동하는지 확인하세요(키트는 자체 서명 인증서를 무시함).
  • 상태 초기화하여 깨끗한 테이크로 시작하도록 하고(새로운 DB, localStorage 초기화), 상태가 더티면 스크립트가 즉시 예외를 발생시키도록 만드세요. 3분 후에 실패하는 테이크는 실제 모델 호출과 시간을 낭비합니다.
  • 비디오가 폴링하는 느린 엔드포인트의 시간을 측정하세요. 4초 걸리는 결과 쿼리는 카메라에 로딩 스켈레톤으로 보입니다. 쿼리를 수정하거나 기다리세요.
  • 작은 Playwright 스크립트로 각 주요 상태의 스크린샷을 찍고 확인하세요. 레이아웃, 줄 바꿈, 깨진 이미지, 1440×900에서 접히는 부분 위의 콘텐츠 등. 이는 녹화보다 저렴합니다.

4. 녹화 스크립트 작성

assets/example-walkthrough.js의 구조를 복사하세요.

const kit = require(process.env.DEMO_KIT || `${process.env.HOME}/.claude/skills/demo-video/scripts/demo-kit`);
const demo = await kit.start();                             // record.js가 프레임 및 타임라인 경로를 제공
await demo.page.goto(URL, { waitUntil: 'networkidle' });
await demo.title('기능 이름', '사용자가 할 수 있는 일');
await demo.startCast();                                     // 타이틀이 페이지를 덮은 후 시작
await demo.sleep(2800); await demo.untitle(); await demo.sleep(900);
await demo.caption('…'); await demo.tap(locator); await demo.type(locator, '텍스트');
await demo.ff(() => page.waitForFunction(() => /* 응답 완료 */ true, null, { timeout: 180000 }));
await demo.finish();
  • 안정적인 선택자(aria-label, id, data-*)를 사용하고 실제 DOM과 일치하는지 확인하세요.
  • ff() 내부에서는 고정된 sleep 대신 실제 완료 조건을 기다리고, 충분한 타임아웃을 설정하세요.
  • 캡션이 어떤 것을 언급할 때, 먼저 화면에 스크롤하세요(내용이 요소 내부에서 스크롤되는 앱의 경우 demo.scroll(px, 'main') 사용).
  • demo.navigate(url)는 페이지나 앱을 전환하며, 새 페이지에서 스크린캐스트를 다시 시작합니다.
  • 아키텍처 세그먼트의 경우, 예제에서 장면 페이지를 빌드하고 record-scenes.js로 녹화하세요. 또한 화살표 키와 스페이스 내비게이션(예제에 있음)을 추가하여 라이브 프레젠테이션에서도 동일한 페이지를 사용할 수 있게 하세요.

5. 녹화

데모 저장소 루트에서 전체 비디오(또는 한 세그먼트)를 포그라운드에서 긴 타임아웃으로 실행하고 로그를 출력하세요.

set -o pipefail; node ~/.claude/skills/demo-video/scripts/record.js <video> [--only <segment>] 2>&1 | tee <video>/.build/record.log

tail로 파이프하지 마세요. 레코더의 종료 코드가 숨겨지고 실패한 테이크가 성공처럼 보일 수 있습니다. 백그라운드로 실행해야 한다면, "완료" 알림을 신뢰하지 말고 실제 PID를 기다리세요(while kill -0 $PID; do sleep 5; done). 알림은 프로세스가 끝나기 전에 도착할 수 있습니다.

6. 검토

record.js는 이미 인코딩과 연결을 수행했으며, 검토 명령어를 출력합니다.

node ~/.claude/skills/demo-video/scripts/review.js <output.mp4> <video>/.build/review 16

한 세그먼트만 보려면 <video>/.build/<segment>.mp4를 검토하세요.

비디오가 완료되었다고 판단하기 전에 모든 콘택트 시트를 읽으세요. 다음을 확인하세요.

  • 각 캡션이 해당 순간의 화면 내용과 일치하는지
  • 캡션이 언급하는 내용이 화면에 보이고 접히는 부분 아래에 있지 않은지
  • 깨진 이미지, 대체 텍스트, 멈춘 스켈레톤, 오류 토스트, 줄 바꿈되거나 잘린 헤더, 디버그 UI가 없는지
  • 빨리 감기 대기가 정지된 프레임이 아닌 진행 상황의 빠른 블러처럼 보이는지
  • 타이틀 및 엔드 카드가 있는지

스크립트나 앱을 수정하고(예: 로드 실패한 이미지에 대한 플레이스홀더, 더 빠른 쿼리), 상태를 초기화하고 다시 녹화하세요. 보통 두세 번의 테이크가 필요합니다.

7. 전달

  • 연결 지점 검토: 각 세그먼트 경계에서 review.js <output> <dir> 0 at=<t-1>,<t+1> 실행.
  • record.js는 이전 버전을 *.previous.mp4로 유지합니다. 이 폴더 자체의 output이 아닌 비디오는 절대 덮어쓰지 마세요. 다른 사람의 비디오에 추가하려면 새 폴더의 video 세그먼트로 추가하세요.
  • 사용자에게 비디오 폴더가 데모 저장소에 커밋할 준비가 되었음을 알리세요. 요청할 때만 커밋하세요.
  • 사용자에게 보고: 파일 경로 및 길이, 각 세그먼트가 보여주는 내용, 실제였던 것과 시뮬레이션된 것, 녹화가 남긴 상태(실행 중인 서비스, 생성한 레코드, 켜져 있는 모든 것)와 이를 되돌리는 방법.

이전에 겪었던 함정

  • 메모리: CDP 스크린캐스트는 애니메이션이 있는 동안 초당 최대 60프레임을 보냅니다. 여러 분짜리 테이크 동안 프레임을 메모리에 버퍼링하면 레코더가 종료되어 출력이 없습니다. 키트는 프레임을 디스크로 스트리밍하고 ff() 동안 프레임을 줄입니다. 이 방식을 유지하세요.
  • 클록: ff() 윈도우와 프레임 타임스탬프는 모두 벽시계 초를 사용하므로, 하나의 ff() 호출 내에서 전체 대기(폴링 포함) 시간을 측정하세요.
  • 블록킹 작업(예: 데이터 스크립트를 위한 execFileSync)은 ff() 내부에서 사용 가능합니다. 이후 프레임이 재개되고 윈도우는 어차피 압축됩니다.
  • 포트: 데모를 위해 서비스를 시작하기 전에 포트가 비어 있는지 확인하세요. 직접 시작하지 않은 프로세스를 종료하지 마세요.
  • 잔여 상태: 출시 또는 활성화로 끝나는 데모는 해당 기능을 켜진 상태로 둡니다. 이후에 끄거나 사용자에게 알리세요.

관련 스킬