video-motion — 비디오 배경에 모션 효과 오버레이 (Remotion)
설계가 곧 코드: 좌표 / 시간 / 텍스트는 전부 src/cues.ts (데이터 계약, 필드는 변경 불가)에 선언되며, fx 컴포넌트는 하드코딩이 없습니다. cues를 변경하고 단일 명령을 다시 실행하면 전체 비디오가 재생성됩니다. 세 가지 모션 효과 레이어: 자막 SubtitleTrack / 데이터 막대 DataBars / 원형 강조 표시 Spotlight (circle / arrow / box).
기술 다이어그램, 프로세스/데이터 화면, Lottie, 3D, 지도 또는 Studio 파라미터 편집은 전문 시각 제작에 따라 관련 브랜치에서 참조하세요. 감독이 콘텐츠와 시각적 방향을 제공하면, 제작 모듈이 실제 프로젝트 자산/코드 및 렌더링 증거를 제공합니다. 현재 컴포넌트, 타입 및 잠금 엔진 인터페이스가 우선시됩니다.
시리즈 비디오는 시리즈 일관성에 따라 섹션 규칙을 프로젝트의 토큰, 폰트, 장면, 자막, 썸네일 및 실제 오디오 트랙에 적용합니다. 실제 입력 버전을 재사용하고 이번 콘텐츠로 렌더링하여 대조하며, 단일 테마 값을 변경하는 것을 전체 비디오가 통일되었다고 간주하지 않습니다. 제품 데모는 실제 녹화/캡처 자료에 주석을 오버레이하며, 이후의 마우스 또는 원형 표시는 실제 동작과 결과와 일치해야 합니다.
Step 1 · 스크립트 디자인 (자료를 보고, 화면을 추측하지 마세요)
- 프레임을 추출하여 자료 보기:
npx remotion still remotion/index.ts FootageOverlay <출력>/f-<n>.png --frame=<n>, 샷과 주요 변화에 따라 프레임 선택; 실제 생성된 이미지의 절대 경로를 사용하여 이미지 읽기 src/cues.ts모션 효과 스크립트 작성:- 자막은 화면을 설명: 시청자가 현재 보고 있는 것을 말하고, 의미와 읽기 속도에 따라 분할
- 원형 표시는 실제 대상을 가리킴: circle / arrow / box가 화면에 실제로 존재하고 주목할 가치가 있는 요소를 가리키며, 좌표는 먼저 프레임 추출을 통해 착지점 확인
- 숫자는 출처가 명확해야 함: 데이터 막대 값은 실제 출처(화면 HUD / 결제 패널 / 백엔드 데이터)에서 가져오고, 가져올 수 없으면 자막에 "데모 데이터" 명시
- 해석을 대상과 관련된 세부 사항에 연결: HUD, 채팅, 프롬프트 및 작업은 증거로 사용될 수 있지만, 설명에 필요한 내용만 표시; 큐 밀도를 위해 전체 화면을 채우지 않음
Step 2 · 엔지니어링 및 사양 (자료에 따라 적응)
templates/remotion-app/를 작업 디렉토리에 복사하고npm install실행 (Remotion 서브 패키지는 동시에 설치해야 하며, 버전이 엄격하게 연동됨); 설치 후npm run typecheck실행 (esbuild 관용이 타입 클린을 의미하지 않음, tsc 통과해야 함; 템플릿에 typescript devDependency 포함되어 있어 설치 후 바로 실행 가능)- 네이티브 사양:
node scripts/probe-footage.mjs <자료 경로>명령어는 ffprobe를 사용하여src/footage-params.ts를 생성합니다. 출력 해상도 / 프레임 속도는 자료에 따라 유동적이며, 하드코딩되지 않습니다. 자료를 변경하면 이 스크립트를 다시 실행하기만 하면 됩니다. - 자료는 skill 디렉토리에 포함되지 않음: 렌더링 시
--public-dir <자료 디렉토리>사용 (staticFile이 프로젝트 자료 라이브러리를 직접 읽음); 인자를 전달하기 어려운 경우에만 자료를public/footage.mp4로 복사 - cues는 1080p 디자인 좌표로 작성하며,
FootageOverlay에는transformOrigin: "top left"가 내장되어 있어 전체 크기가 임의의 캔버스에 맞게 조정됩니다. 해상도를 변경해도 cues를 수정할 필요가 없으며, 모션 효과 위치가 흐트러지지 않습니다.
Step 3 · 생성 (단일 명령)
렌더링 전에 먼저 검증: node scripts/verify-cues.mjs (deck 자막은 src/deck-cues.ts 파라미터 사용), 오류가 0이어야만 렌더링 진행; 총 시간 경고는 알림만 하고 차단하지 않음.
cd <remotion-app>
npx remotion render remotion/index.ts FootageOverlay <저장소>/products/<프로젝트명>/demo.mp4 --crf=16 --public-dir <자료 디렉토리>
결과물은 저장소 products/<프로젝트명>/ 디렉토리에 저장 (디렉토리 규칙: skill은 순수 도구이므로 결과물이 skill 내에 남지 않음; 다른 저장소에서 사용 시 해당 프로젝트 루트 output/<skill 이름>/에 저장); --crf=16은 고품질. 대용량 자료 렌더링은 백그라운드에서 실행 (1440p60 × 30s 약 10분 소요), 종료 코드 + ffprobe로 검증하며, 진행률 표시줄에 의존하지 않음.
Step 4 · 3단계 검증 (필수, 생략 불가)
① 프로그램: 렌더링 종료 코드 0; npx remotion compositions remotion/index.ts가 출력한 durationInFrames / 너비 / 높이 / fps가 자료 메타데이터와 일치; npx remotion still로 주요 자막, 데이터 변화 및 전환에서 대표 프레임 선택 — 각 자막의 start+0.3s / end-0.3s, 데이터 막대 t-0.3s / t+1.5s, 원형 강조 표시 t+0.5s / t+ttl+0.3s (compositions이 출력한 실제 fps로 프레임 번호 변환).
② 이미지 읽기: 실제 PNG를 읽어 프레임별로 확인: 등장/사라짐 시간과 텍스트, 막대 높이 비율과 값 레이블, 원형 강조 표시 유형과 위치, 배경에 깨짐/늘어짐 없음, 전체 프레임에 깨진 문자 없음 (중국어는 Microsoft YaHei 폴백 사용). 문제가 있으면 해당 소스 파일에 기록하고, 재렌더링 후 원래 문제와 인접 연결을 확인; 판단하거나 수정할 수 없는 차단 문제가 있으면 명확히 보고.
③ 연속 재생 및 청취: 프레임 추출만으로는 리듬과 동기화를 증명할 수 없으므로, media-qc의 시청각 방법을 사용하여 실제 클립과 오디오 트랙을 확인하고, 검사 범위를 기록; 사용자가 최종 완성본을 검토.
Step 5 · 전달
보고서: 완성본 경로 및 크기 / 해상도 프레임 속도 (=자료 네이티브) / 3단계 검증 결과 (프레임 추출 확인표 포함) / cues.ts 위치 (재실행 가능). 결과물과 mp4는 절대 git에 포함되지 않음.
PPT 페이지별 비디오 제작 (DeckVideo, 두 번째 비디오 유형)
단일 엔진, 두 가지 비디오 유형: 위의 Steps는 "비디오 배경에 모션 효과 오버레이"이고, 이 섹션은 "PPT 페이지별" — pptx 페이지 이미지 + 페이지별 음성 해설을 직접 비디오로 출력하며, 페이지 시간은 음성 해설의 실제 길이에 따라 결정되고, 자막은 분할 스크립트의 타임라인에 따라 나타납니다.
- pptx 및 분할 스크립트: ppt skill의 결과물 pptx; 내레이션 분할 스크립트
script.json작성,ref는"page N"(N=페이지 번호), 먼저python skills/narration/templates/verify_narration.py <script.json>통과 (모두 통과). 각 세그먼트는 ≤20자 (자막 한 줄 약은 세그먼트 텍스트를 직접 표시), 페이지 내 여러 세그먼트는 마침표로 연결하여 해당 페이지의 내레이션으로 사용 - 페이지 이미지:
python scripts/extract_pages.py <in.pptx> public/deck/pages— COM을 통해 PDF로 변환 후 fitz로 200dpi 렌더링,p-<N>.png출력 (2667×1500, 캔버스 contain으로 채움) - 음성 해설: 테스트는
powershell -File scripts/make_deck_audio.ps1 <script.json> public/deck/audio실행 (SAPI Huihui가 페이지별로page-N.wav출력); 공식 프로세스는 사람이 CapCut에서 페이지별로 음성 해설을 녹음하고, 동일한 이름page-N.wav로 동일 디렉토리에 저장, 다운스트림에 영향 없음 - 연결 및 렌더링:
node scripts/build-deck-params.mjs <script.json> public/deck가src/deck-params.ts+src/deck-cues.ts를 생성 (페이지 시작점 / 총 길이 / 자막 시간은 모두 파생 값, 수동 수정 금지) →npx remotion render remotion/index.ts DeckVideo <저장소>/products/<프로젝트명>/deck.mp4 --crf=16 - 3단계 검증: Step 4의 3단계와 동일 (프로그램 ffprobe / compositions 대조 + 프레임 추출 이미지 확인 + 사용자 시청). L2 프레임 추출 핵심: 각 페이지 첫 프레임+0.3s에서 페이지 순서 확인; 오버랩 중간점은 다음 페이지 start+0.25s (오버랩 끝은 페이지 창 끝, 0.5s 크로스 디졸브); 자막 start+0.3s / end+0.3s에서 텍스트 및 퇴장 확인. 페이지 첫 프레임+0.3s에서 spring 페이드인이 완료되지 않아 전체 프레임이 약간 회색인 것은 전환 중간 상태이지 결함이 아님
페이지 이미지 및 음성 해설은 public/deck/에 저장 (.gitignore가 이미 포함되어 있어 git에 들어가지 않음); 캔버스 1920×1080@30은 build-deck-params.mjs 상단에 상수로 정의되어 있으며, 두 줄만 수정하면 4K가 됨.
장면화 비디오 v2→v3 품질 공정 (scene-kit 직접 렌더링, 세 번째 비디오 유형)
PPT 페이지별 비디오의 업그레이드 라인: 화면 주체가 "PPT 페이지 이미지"에서 Remotion 컴포넌트가 직접 렌더링하는 모션 효과 장면 (src/scene-kit/ 24개 컴포넌트)으로 변경되며, 페이지 시간/오디오/자막 메커니즘은 deck-params 파생 체인을 그대로 사용합니다. "밋밋함" 또는 "저품질" 판단은 docs/2026-08-30-motion-grammar.md를 참조; 이 섹션은 품질 공정의 하드 룰입니다.
품질 기준
docs/2026-08-30-motion-grammar.md의 콘텐츠 유형에 따라 검수 방식을 선택합니다. 토큰 및 브랜드 연속성을 유지; 화면은 실제 주체와 읽을 수 있는 정보를 중심으로 하며, 움직임은 변화를 설명합니다. 여백, 정지 상태 및 각 샷의 지원 정보량은 샷의 목표에 따라 판단하며, 고정 할당량이나 정적 PPT 거부를 적용하지 않습니다.
먼저 content-director가 사실 출처, 스토리보드 및 아트 브리핑을 제공한 다음, 대표 프레임과 짧은 샘플을 확인합니다. 구조 다이어그램은 SVG를 우선, 실제 인터페이스는 스크린샷/녹화 사용, 일러스트레이션이 필요할 때 사용 가능한 imagegen 도구를 연결하고 자산을 등록합니다. 새로운 ArtLayer 및 더 많은 모션 효과가 자동으로 아트 품질 통과를 의미하지는 않습니다.
장면 설계 방법 (일반적, 사전 설정된 답변 없음)
먼저 assets/patterns.md 페이지별 솔루션 라이브러리를 확인합니다. 유사한 상황에 이미 검증된 PAT 항목이 있으면 이를 사용하여 파라미터를 수정 (상황 → 레이아웃 → 컴포넌트 → 함정 → 참조 프레임이 모두 항목에 있음), 없으면 처음부터 설계합니다. 각 페이지의 카피는 먼저 콘텐츠 유형 (내러티브痛点/기능 데모/데이터 설득/프로세스 설명/개념/행동 촉구 6가지 유형)을 결정한 다음, "주체는 카피 콘텐츠에 의해 결정, 동일 유형의 여러 페이지는 동일한 시각적 솔루션을 반복하지 않음, 배경군은 콘텐츠 분위기를 지원"의 세 가지 규칙에 따라 주체 형식과 컴포넌트 조합을 결정합니다. 작업 시작 전에 설계표를 생성 (각 페이지: 카피 요약 → 콘텐츠 유형 → 주체 형식 → 배경군 → 컴포넌트 조합 → 이유 → 화면 정보 패키지 항목별 + 출처 → 주체 주석), 설계표는 사전 검토를 받아야 하며, 통과해야 코드 작성; 지원 정보는 항목별로 출처 표시 (내레이션 스크립트/프로젝트 문서/자료에서 볼 수 있는 내용).
세 가지 하드 레슨 (규칙으로 작성)
- 완료 상태에서 읽기 위한 일시 정지 허용: 채팅, 차트 또는 프로세스가 상태 변화를 표시해야 할 때 기존 컴포넌트 사용; 정지 샷은 정보와 리듬에 따라 판단하며, 검사 결과를 위해 장식 모션 효과를 강요하지 않음.
- 래핑 컴포넌트는 반드시 명시적으로 width를 전달해야 함: 새 컨테이너가 이전 컴포넌트를 감쌀 때 width가 없으면 블록 레벨 요소가 부모 컨테이너를 채움 (일러스트레이션이 제목을 덮음, 휴대폰 케이스가 700px 너비로 늘어남, 두 번 모두 동일한 근본 원인); 래핑 컨테이너는 항상
style={{ width, ...style }}사용. - 긴 스크린샷은 먼저 공백률을 확인한 후 솔루션 결정: 긴 스크린샷을 받으면 먼저 행별로 std/공백 비율 계산 (PIL 스크립트), 지연 로딩으로 스크린샷에 렌더링되지 않은 "빈 긴 이미지"는 스크롤 시 바로 죽음 — 먼저 클로즈업 자르기, 자료 수정 후 다시 스크롤로 전환.
MG 무기고 (S0-S6, 완성본 품질의 주력 탄약)
템플릿에는 전체 무기 세트가 포함되어 있으며, 각 컴포넌트의 구체적인 사용법은 assets/component-catalog.md를 참조 (scene-kit 24개 컴포넌트 / entrance-kit 9개 동사 / vendor 커뮤니티 선정 / 폰트), 여기서는 절대 규칙만 기록:
- 버전 절대 규칙: 모든
@remotion/*은 정확히 동일한 버전이어야 함 (예: 4.0.533,^없음) — 버전 불일치는 Remotion의 하드 오류이며 경고가 아님; 새 패키지를 추가하려면 먼저 기존 버전에 맞춤 (check-docs의 3.7항목이 감시, 어긋나면 빨간불) - 4.0.533 API 사실 (문서/기억과 차이 있음, 실제 테스트 완료; 4.0.518→4.0.533 업그레이드는 still 기준 PSNR 전체 inf 대조, API 형태 변경 없음):
evolvePath는 클리핑되지 않은 path에 대한 dash 속성 반환;interpolatePath(value, from, to)는 세 개의 독립 파라미터;fitText는withinWidth만 받고{fontSize}객체 반환; 중국어 여러 줄은fitTextOnNLines사용 불가 (공백으로 단어 분할);parseSrt는{input}객체를 받고 (원시 문자열은 undefined.split 오류),{captions:[{text,startMs,endMs}]}밀리초 단위 반환 - transform 래핑 함정 (S6 이중 함정 기록): transform이 있는 래핑 동사 (CameraPush/SlideGroup/TextBreath/BlurTrail)는 직접
position:absolute서브트리를 감싸면 안 됨 — transform 요소는 absolute 자손의 containing block이 되어 흐름 내 높이가 0이 되고 내용물이 상단으로 넘침. 컴포넌트는 이미 자체 충분 (CameraPush 루트 inset 0, BlurTrail 메인 레이어 흐름 내), 사용 시에도 크기가 없는 absolute 부모 내에서 직접 래핑하지 않도록 주의 - 공식
<Trail>/TransitionSeries의미: Trail 루트는 AbsoluteFill (페이지 수준 오버레이 의미), 요소 수준 잔상은 scene-kit의 BlurTrail 사용 (이미 relative화됨); TransitionSeries 전환은 인접 페이지 길이를 소비, 완성 엔진은 기본적으로 전환 없음 (Series 하드 컷) + 페이지 내 동사 - vendor 규율: copy-paste로 저장소에 입고, npm 의존성 아님; 업그레이드 = 다시 가져와서 재처리; 4가지 규율은 템플릿
src/vendor/README.md참조 - Lottie 자산 흐름: 무료 상업용 JSON은
assets/lottie/로 이동 (README 3개 필드 등록) → 사용 시 프로젝트public/으로 복사 →LottieLayer src에서 참조; 인테이크 시 표현식이 포함된 JSON은 금지 (렌더링 불확실) - 복잡한 효과 예산: ShutterBlur의 samples 및 BlurTrail의 layers는 렌더링 비용을 증가시킴; ThreeStage는 장면과 시스템 차이에 따라 실제 측정. 먼저 대표 프레임/짧은 클립을 렌더링한 후 규모를 결정하며, 전체 비디오에 고정 샷 할당량을 설정하지 않음.
검수 절차 (4단계)
L1 프로그램 (렌더링 종료 코드, 전체 로그 캡처, 파이프 테일 금지; ffprobe = compositions 프레임 수 대조); L2 프레임 추출 (리스크에 따라 진입/안정된 풀샷/정보 클로즈업/전환 및 자막 대표 프레임 선택, 실제 이미지 확인; 확대는 세부 사항 확인에만 사용, 페이지당 고정 수량 없음); L2.5 무음 테스트 (페이지별 블라인드 답변 주제); L3 보고서 (기술 범위, 키 프레임 증거, 수동 검토 항목 및 필요 시 이전/이후 대조 포함). 일반적인 가치가 있는 컴포넌트, 토큰 및 레이아웃이 확인되면 자산 환류 규칙에 따라 템플릿/등록부에 다시 기록; 프로젝트 전용 콘텐츠는 프로젝트 내에 유지하며, 새로운 경험이 없으면 추가 작업 티켓을 생성하지 않음. 렌더링 명령은 DeckVideo와 동일 (composition을 장면화 컴포넌트로 변경, 예: DeckVideoV2), 썸네일은 npx remotion still로 이미지 생성 (still 프레임 번호는 진입 spring 길이를 고려해야 함).
썸네일 이미지 출력 (Cover, 네 번째 결과물)
비디오를 렌더링하지 않고 한 프레임을 렌더링: 단일 명령으로 1920×1080 썸네일 PNG를 생성하며, 비디오 게시 시 첨부 이미지로 사용합니다.
- props:
products/<프로젝트명>/cover-<이름>.json에{ title, subtitle?, badge?, bg?, preset }작성.preset∈photo(bg 이미지 전체 채움 + 하단 55% 검정 그라데이션 마스크로 대비 보장) /dark(어두운 그라데이션, 기본값) /clean(밝은 배경 + 상단 미세 라인);bg는 remotion-apppublic/디렉토리 내의 상대 경로. subtitle 생략 시 렌더링되지 않음; badge 생략 시 ASTERFORGE 브랜드 각인으로 대체 - 렌더링:
npx remotion still src/cover-index.ts Cover <출력>.png --props=<json 파일 경로>(독립적인 진입점으로 Root.tsx를 거치지 않으며, DeckVideo와 병렬 작업 시 충돌 없음) - 확인: 이미지를 읽어 제목이 완전하고 잘리지 않았는지, photo 마스크 아래 텍스트를 읽을 수 있는지, 깨진 문자 블록이 없는지 확인; 실제 결과물의 절대 경로를 사용하고, 현재 버전 또는 해시를 재확인. 제목 줄 수는 대상 화면 크기와 읽기 필요에 따라 조정.
경계 및 함정
- 실제 결과물의 절대 경로를 사용하여 이미지를 읽고, 현재 파일 버전 또는 해시를 재확인; 특정 도구에 실제로 경로 제한이 있는 경우에만 경로를 변환.
- GBK 콘솔: print는 전부 영어
- Chrome Headless Shell: 첫
npx remotion browser ensure는 Google 소스에서 다운로드 (수백 MB), 중국 네트워크에서는 시간 초과 가능 — 실패 시 즉시 보고, 대용량 파일 다운로드를 반복 재시도하지 않음 - 대용량 미디어, 의존성 및 캐시는 Git에 포함되지 않음; 제작 레시피, 썸네일, 자막, 게시 구성 및 검수 메타데이터는 아카이브 규칙에 따라 보관.
현재 워크플로우 및 기술 참조
node scripts/studio.mjs init --id <이름> --line b로 템플릿을 불러오고, job.json의 deps/typecheck는 재실행 가능; 본 프로젝트의 params/render/QC 명령은 장면과 실제 오디오 트랙이 결정된 후 job.json에 추가됩니다. 렌더링 후 media-qc를 호출하며, 기술 보고서와 사용자 최종 검토는 별도로 기록; 아카이브 검증 통과 후 캐시 정리.
공식 remotion-best-practices는 python scripts/install-upstream-skills.py를 통해 프로젝트 캐시에 고정 설치된 후, setup-skills에 의해 마운트됩니다. 출처는 references/upstream.json 참조; 이는 기술 참조이며, 본 저장소 워크플로우를 대체하지 않습니다. 공식 참조 4.0.538과 현재 엔진 4.0.533은 별도로 관리되며, 새 API를 사용하기 전에 먼저 로컬 타입과 실제 렌더링을 확인하고, 자동 업그레이드하지 않음.