Remotion 모션 그래픽 — 프레임 기반 2D 비디오
Remotion은 React를 비디오로 렌더링합니다. 모든 프레임은 프레임 인덱스의 순수 함수이므로 출력은 결정적(deterministic)이며, diff가 가능하고 리뷰할 수 있습니다. 텍스트 중심 및 데이터 기반 비디오에 가장 적합한 기본 선택이며, Blender 또는 Manim 패스를 타이포그래피, 캡션, 전환 효과와 함께 조립하는 최고의 "컴포지터"입니다.
관례는 references/REMOTION-PLAYBOOK.md에서, 바로 사용 가능한 컴포넌트는 references/REMOTION-RECIPES.md에서 확인하세요. 최신 API는 공식 문서와 설치된 CLI에서 확인하십시오: https://www.remotion.dev/docs/
1. 설정
node --version # 18+ 필요, 20/22 LTS 권장
npx create-video@latest my-video # blank 템플릿 선택
cd my-video
npx skills add remotion-dev/skills # 공식 Remotion 에이전트 스킬 (권장)
npm run dev # Remotion Studio: http://localhost:3000
렌더링:
npx remotion render <entry> <CompositionId> out/video.mp4 --codec=h264
npx remotion render <entry> <CompositionId> out/preview.mp4 --scale=0.5
npx remotion render <entry> <CompositionId> out/frames --sequence --image-format=png
정확한 플래그는 버전에 따라 다릅니다. npx remotion render --help로 확인하세요.
라이선스: Remotion은 공개 소스(Source-available)이며 OSI 오픈 소스가 아닙니다. 개인 및 적격 소규모 조직은 현재 조건에 따라 무료로 사용할 수 있습니다. 대규모 또는 상업/자동화 사용의 경우 유료 라이선스가 필요할 수 있습니다. 실제 사용 사례에 대한 가격은 https://www.remotion.dev/docs/license/pricing 에서 확인하세요. "MIT" 또는 "모든 회사에 무료"라고 설명하지 마십시오.
2. 프로젝트 구조
스타터 템플릿에 맞추고, 템플릿에 대항하지 마십시오:
src/
Root.tsx # 컴포지션 등록
compositions/
MainVideo.tsx # 장면을 순서대로 배열
scenes/
Intro.tsx Feature.tsx Outro.tsx
components/
AnimatedTitle.tsx Background.tsx Grid.tsx Caption.tsx Transition.tsx
animations/
easing.ts timing.ts reveal.ts
data/
content.ts # 복사 및 숫자, 레이아웃과 분리
styles/
tokens.ts # 팔레트, 타입 스케일, 간격
public/
audio/ fonts/ images/ video/
규칙:
- 장면당 하나의 파일;
MainVideo는 장면을 순서대로 배열하기만 합니다. - 복사 및 데이터는
data/에 위치시키고, 다섯 개의 장면에 인라인으로 넣지 마십시오. - 디자인 토큰은
styles/tokens.ts에; 마법의 헥스 값들을 여기저기 흩어 놓지 마십시오. - 재사용 가능한 기본 요소는
components/에; 장면별로 중복되지 않아야 합니다. public/아래의 모든 것은staticFile("...")을 사용합니다.
3. 결정성 (필수 규칙)
모든 시각적 상태는 현재 프레임에서 파생됩니다:
import { useCurrentFrame, useVideoConfig, interpolate, spring } from "remotion";
const frame = useCurrentFrame();
const { fps, durationInFrames, width, height } = useVideoConfig();
애니메이션 드라이버로 금지된 것: Date.now(), setTimeout, setInterval, requestAnimationFrame, CSS 키프레임, 시드되지 않은 Math.random(), 렌더링 사이에 변경되는 비동기 데이터.
변형이 필요할 때 시드된 난수:
export const rand = (seed: number) => {
const x = Math.sin(seed * 12.9898) * 43758.5453;
return x - Math.floor(x);
};
비동기 자산은 프레임이 렌더링되기 전에 해결되어야 합니다. delayRender/continueRender를 올바르게 사용하고, 항상 catch에서 계속하거나 프레임 외부에서 자산을 미리 로드하는 것이 좋습니다.
4. 타이밍
durationInFrames는 명시적입니다. 30FPS에서 10초 = 300프레임, 인덱스 0–299.- 한 번 변환:
const s = (n: number) => Math.round(n * fps). - 장면 경계는 누적 타임라인에서 가져오며, 장면별 반올림을 사용하지 않습니다.
<Sequence from={start} durationInFrames={len}>를 사용하십시오; 컴포지션을 넘어서는지 확인하십시오.- 오버랩을 의도적으로 만드십시오; 한 프레임의 간격은 눈에 띄는 깜박임입니다.
- 지속 시간이나 크기가 props에서 오는 경우
calculateMetadata를 사용하십시오.
5. 애니메이션 제작
| 모션 | 기본 요소 |
|---|---|
| 진입/안정화 | spring 또는 Easing.out(Easing.cubic)이 있는 interpolate |
| 종료 | Easing.in(Easing.cubic) |
| 대칭 이동 | Easing.inOut(Easing.cubic) |
| 기계적/연속적 | Easing.linear |
| 숫자/색상/회전 | 클램프된 외삽과 함께 interpolate |
| 간격(Stagger) | 요소당 from 오프셋 (30–80 ms) |
const enter = spring({ frame, fps, config: { damping: 18, stiffness: 120, mass: 0.8 } });
const opacity = interpolate(frame, [0, 12], [0, 1], {
extrapolateLeft: "clamp", extrapolateRight: "clamp",
});
모든 것을 스프링으로 만들지 마십시오. 모든 속성에 애니메이션을 적용하지 마십시오. 레이아웃 속성보다 변형(transform)과 불투명도(GPU 친화적)를 선호하십시오. 짧은 헤드라인의 경우 최소 약 0.8–1.2초의 체류 시간을 확보하고, 본문 텍스트는 더 길게 확보하십시오.
6. 타이포그래피 및 디자인
- 타입 스케일을 설정하고(예: 1080p에서 96/64/40/28/20px) 모든 곳에서 사용하십시오.
@remotion/fonts또는 스타터의 문서화된 패턴을 사용하여public/fonts에서 폰트를 로드하고, 렌더링 전에 준비가 완료될 때까지 기다리십시오. 시스템 폰트에 의존하지 마십시오.- 줄 길이(약 45–75자), 줄 높이(디스플레이 타입의 경우 1.1–1.4), 자간을 확인하십시오.
- 5–8%의 안전 여백을 유지하십시오; 세로/소셜 형식의 경우 플랫폼 UI 오버레이를 확인하십시오.
- 페르시아어/아랍어: 올바른
dir="rtl", 모양을 구현하는 폰트, 혼합된 숫자/URL 확인, 렌더링된 프레임 검사 — 헤드리스 Chrome은 일부 디자인 도구와 다르게 텍스트를 렌더링합니다. - 최종 복사를 문자 그대로 확인하십시오. 비디오의 오타는 출시 버그입니다.
7. 구성 및 전환
- 비트(beat)당 하나의 초점; 눈을 움직이게 하고, 화면 전체를 움직이지 마십시오.
- 전환 어휘를 사용하십시오: 컷, 6–10프레임 크로스페이드, 마스크 와이프, 스케일 스루, 블러 스루. 의도적으로 재사용하십시오.
- 모든 것을 디졸브하는 것보다 비트에서 컷하는 것을 선호하십시오.
- 긴 비디오의 경우 먼저 타이밍 차트(초 → 프레임 → 장면)를 만들고 하나의 파일에 인코딩하십시오.
8. 오디오 및 캡션
import { Audio, staticFile } from "remotion";
<Audio src={staticFile("audio/music.mp3")} volume={0.6} />
- 사운드가 시작되어야 하는 시퀀스에만 가져오십시오(Import only into a sequence that starts where the sound should start).
volume에 대한interpolate로 페이드 인/아웃; 클릭을 피하십시오.- 음악을 보이스오버 아래로 덕킹하십시오; 대화가 명료하게 유지되도록 하십시오.
- 캡션: 소셜용으로
<Caption>으로 굽거나(burn in), 플랫폼용으로.srt를 내보내십시오. 안전 영역과 읽기 속도(약 15–20자/초)를 고려하십시오. - 프리뷰뿐만 아니라 인코딩된 파일에서 믹스를 확인하십시오.
9. 성능
- 메모리 제한이 있는 머신에서 동시성 줄이기:
--concurrency=4. - 거대한 블러, 필터 체인, 거대한 SVG, 수천 개의 DOM 노드를 피하십시오; 밀도가 높은 그래픽에는 Canvas를 사용하십시오.
- 정적 배경을 캐시하십시오(
<Freeze>또는 미리 렌더링된 이미지). - 이미지 최적화(올바른 크기, 지원되는 경우 WebP/AVIF) 및 비디오 자산 다듬기.
- 먼저
--scale=0.5와 짧은 프레임 범위로 미리보기를 렌더링하십시오.
10. 일괄 및 데이터 기반 비디오
- props 스키마를 정의하고 렌더링 전에 유효성을 검사하십시오.
- 데이터는 JSON/CSV에 유지하고, 레이아웃은 컴포넌트에 유지하십시오.
- 텍스트 맞춤: 최대 줄 수, 폴백 글꼴 크기, 자르기 규칙, 오버플로 테스트.
- 동일한 컴포지션에서 다른
--props로 각 변형을 렌더링하십시오. - 매니페스트를 작성하고 모든 출력의 지속 시간과 비어 있지 않은 크기를 확인하십시오.
npx remotion render src/index.ts Main out/acme.mp4 --props='{"brand":"Acme","tagline":"Build faster"}'
11. Blender와 하이브리드
- Blender 샷을 Remotion 컴포지션과 동일한 FPS, 해상도, 프레임 수로 렌더링하십시오.
- 프로젝트에서 오프셋을 정의하지 않는 한, Blender 프레임 1 → Remotion 프레임 인덱스 0에 매핑하십시오.
- 투명도의 경우 PNG 시퀀스 또는 ProRes 4444를 내보내고, 체커보드 위에서 알파를 확인하십시오; MP4에는 알파가 없습니다.
- 색상 파이프라인 일치: 브라우저 출력과 동일한 전송/뷰 변환 가정으로 Blender 패스를 인코딩한 다음 합성을 검사하십시오.
- 패스를
<Video>가 아닌<OffthreadVideo>로<Sequence>안에 배치하고, 프레임 맵에서startFrom/프레임 오프셋을 설정하십시오.
12. 전달 전 검증
npx remotion compositions src/index.ts # ID와 크기 나열
ffprobe -v error -show_entries format=duration,size \
-show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt,channels \
-of json out/video.mp4
그런 다음 프레임 0, 모든 장면 경계, 모든 전환, 모든 텍스트가 많은 프레임, 마지막 프레임을 검사하십시오. 잘린 텍스트, 누락된 폰트, 검은 프레임, 오디오 드리프트를 확인하십시오. 인코딩/정규화 레시피는 video-composition을, 전체 게이트는 motion-studio/references/QUALITY-CHECKLIST.md를 참조하십시오.
13. 일반적인 실패 모드
- 컴포지션을 찾을 수 없음 → 엔트리 파일 또는 ID가 잘못됨;
npx remotion compositions실행. - 빈 렌더링이지만 프리뷰는 정상 → Studio에서는 에셋 경로가 확인되지만 헤드리스에서는 그렇지 않음;
staticFile사용,public/대소문자 확인. - 폰트가 Times로 대체됨 → 렌더링 전에 폰트가 로드되지 않음; 준비 완료를 기다리십시오.
- 렌더링 간 출력이 다름 → 벽시계 시간 또는 시드되지 않은 난수.
- OOM 또는 매우 느림 →
--concurrency낮추기, 블러/스케일 줄이기, 렌더링 분할. - 오디오 동기화 불일치 → 잘못된
startFrom, FPS 불일치, 또는 가변 프레임 속도 소스.