Remotion 주제 설명 동영상 스킬 (50-60초)
이 스킬은 모든 주제에 대해 고품질의 50~60초 짧은 형식의 설명 동영상(TikTok / YouTube Shorts / Reels 형식: 1080x1920 @ 30fps)을 제작하는 방법을 안내합니다.
다음 요소를 결합합니다:
- 시각적 베스트 프랙티스 (
remotion-dev/skills에서 제공: 프레임 정확한 스프링, 보간 클램핑, 시퀀스 구성, 타이포그래피 계층). - AI 음성 합성:
.env에서TTS_PROVIDER를 통해edge(edge-tts-universal, 온라인, 무료) 또는cit(CIT Voice Studio, 오프라인 베트남어 스튜디오 음성 - github.com/quyen2867/cit-voice-studio에서 다운로드, API 문서:docs/cit-voice-studio.md). - 정확한 타이밍 동기화: 오디오 음성 길이를 Remotion 프레임 시퀀스에 직접 매핑합니다.
1. 동영상 구조 및 타이밍 (50-60초 / 30fps 기준 1500-1800프레임)
매력적인 짧은 동영상은 Hook-Problem-Solution-Value-Outro 프레임워크를 따릅니다:
| 장면 | 이름 | 시간 | 프레임 범위 (@ 30fps) | 목적 및 시각 요소 |
|---|---|---|---|---|
| 1 | Hook | 0초 – 5초 | ~150프레임 | 즉시 주목 유도: 큰 제목, 호기심 자극 질문, 떠다니는 배지. |
| 2 | 맥락 / 문제점 | 5초 – 15초 | ~300프레임 | 주제가 중요한 이유나 배경 제시. 경고 아이콘/비교. |
| 3 | 핵심 개념 1 | 15초 – 28초 | ~390프레임 | 첫 번째 핵심 원리 설명: 유리 카드, 설명 아이콘, 강조 키워드. |
| 4 | 핵심 개념 2 | 28초 – 42초 | ~420프레임 | 세부 사항 또는 작동 메커니즘 확장: 3단계 프로세스, 부드러운 흐름도. |
| 5 | 실제 영향 | 42초 – 52초 | ~300프레임 | 실제 적용, 실용적인 조언 또는 인상적인 데이터. |
| 6 | 아웃트로 및 CTA | 52초 – 60초 | ~240프레임 | 강력한 한 문장 요약, Like/Share/Follow 요청 및 개인 브랜드. |
총 길이: 1500 – 1800프레임 (약 50 – 60초).
2. TTS 음성 생성 (edge | cit)
2.1 .env를 통한 음성 구성
.env 파일이 올바르게 구성되었는지 확인하세요 (전체 내용은 .env.example 참조):
TTS_PROVIDER=edge # edge | cit
TTS_OUTPUT_DIR=public/audio
# Edge TTS (TTS_PROVIDER=edge)
EDGE_TTS_VOICE=vi-VN-HoaiMyNeural # 또는 vi-VN-NamMinhNeural, en-US-ChristopherNeural
EDGE_TTS_RATE=+10% # +10% ~ +15%는 짧은 동영상의 빠른 템포에 매력적
# CIT Voice Studio (TTS_PROVIDER=cit)
CIT_TTS_BASE_URL=http://127.0.0.1:8001
CIT_TTS_VOICE=Hải Đăng # 음성 목록: docs/cit-voice-studio.md 또는 GET /voices
CIT_TTS_SPEED=1.1 # 0.5 - 2.0
TTS_PROVIDER=cit인 경우:
- CIT Voice Studio 앱이 실행 중이어야 합니다. 스크립트는 오디오 생성 전에 자동으로
GET /health를 호출하고, 연결할 수 없으면 사용자에게 앱을 열도록 안내합니다 (또는 github.com/quyen2867/cit-voice-studio에서 다운로드). - 대사에는 감정 태그
[cười],[thở dài],[hắng giọng]및 일시 정지 태그[nghỉ 0.5s]를 삽입할 수 있습니다 (소리내어 읽히지 않음). 남용 금지: 장면당 최대 1개 태그. - 오류
400 Voice not found= 잘못된 음성 이름 (악센트 구분).CIT_TTS_VOICE를 확인하세요.
2.2 오디오 파일 생성 프로세스
주제를 받으면 음성 대본 파일을 생성하고 scripts/generate-tts.ts 스크립트를 실행하여 오디오를 생성합니다:
import { generateTopicVoices } from "../../scripts/generate-tts";
const scenes = [
{
id: "scene1_hook",
text: "Bạn có biết Docker thực chất là gì và tại sao mọi lập trình viên đều cần nó?",
},
{
id: "scene2_problem",
text: "Trước đây, câu nói 'trên máy tôi vẫn chạy được' luôn là cơn ác mộng khi bàn giao sản phẩm.",
},
{
id: "scene3_concept1",
text: "Docker giải quyết việc này bằng Container - đóng gói toàn bộ code và môi trường vào một khối duy nhất.",
},
{
id: "scene4_concept2",
text: "Khác với máy ảo nặng nề, Docker chia sẻ chung nhân hệ điều hành, giúp khởi động chỉ trong tích tắc.",
},
{
id: "scene5_impact",
text: "Nhờ đó, bạn deploy ứng dụng mượt mà trên mọi máy chủ từ AWS, GCP cho đến VPS giá rẻ.",
},
{
id: "scene6_outro",
text: "Follow kênh để nắm trọn các kiến thức công nghệ ngắn gọn mỗi ngày nhé!",
},
];
await generateTopicVoices("DockerShort", scenes);
오디오 파일은 public/audio/<TopicName>/<sceneId>.<ext> (edge는 .mp3, cit는 .wav)에 자동 저장되며, 각 장면의 정확한 프레임 수(durationInFrames)와 audioPath를 기록한 manifest.json 파일도 함께 생성됩니다.
- 각 장면에 선택적 속성:
voice(선택한 공급자의 음성),rate/pitch/volume(edge 전용),speed0.5-2.0 (cit 전용). - 이미 존재하는 파일은 재사용됩니다(캐시). 대사나 음성을 변경한 후 다시 생성하려면 해당 오디오 파일을 먼저 삭제하세요.
3. Remotion 코딩 규칙 (remotion-dev/skills에서)
Remotion 소스 코드를 작성할 때는 다음 원칙을 반드시 준수해야 합니다:
3.1 스프링 애니메이션 사용
CSS keyframes/transitions 또는 무미건조한 linear easing을 사용하지 마세요. spring()을 사용하세요:
import { spring, useCurrentFrame, useVideoConfig } from "remotion";
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// 카드/아이콘을 위한 부드러운 바운스 스프링
const scale = spring({
frame,
fps,
config: {
damping: 12, // 15 미만은 자연스러운 탄성 바운스 생성
stiffness: 100, // 강성
mass: 0.8,
},
});
const entrance = spring({
frame: frame - 10, // 10프레임 지연
fps,
from: 50,
to: 0,
});
3.2 안전한 보간 (항상 Clamp)
타임라인이 범위를 벗어날 때 값이 넘치지 않도록 항상 extrapolateLeft: "clamp" 및 extrapolateRight: "clamp"를 제공하세요:
import { interpolate } from "remotion";
const opacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
3.3 오디오 임베드 및 시퀀스 동기화
Composition의 각 장면은 <Sequence> 또는 <Series>를 사용하여 정확한 타이밍을 관리합니다:
import { Audio, staticFile, Sequence } from "remotion";
// Scene 컴포넌트 내부 - manifest에서 경로를 가져오고, .mp3 확장자를 하드코딩하지 마세요 (cit 공급자는 .wav 출력):
<Sequence from={0} durationInFrames={sceneDuration}>
<Audio src={staticFile(audioManifest.scenes[0].audioPath)} />
<SceneContent />
</Sequence>
- 깔끔한 장면 전환 버퍼 (Snappy Transition Buffer):
- 장면 사이의 간격은 음성이 끝난 후 +3 ~ +4프레임 (약 0.1초) 만 남겨야 합니다.
- 버퍼가 너무 길면 (+15 ~ +20프레임) 장면 사이에 정적/데드 비트가 발생하여 짧은 동영상의 빠르고 매력적인 템포를 유지하지 못합니다. 다음 장면은 이전 문장이 끝나자마자 나타나야 합니다.
// Series에서 장면 길이 계산 예시
const d1 = audioManifest.scenes[0].durationInFrames + 3;
const d2 = audioManifest.scenes[1].durationInFrames + 3;
// ...
3.4 시각 디자인 표준 (세로 1080x1920)
- 배경: 고급스러운 다크 모드 (
bg-slate-950또는from-slate-950 via-zinc-900 to-black그래디언트)에 방사형 글로우/그리드 효과 결합. - 글래스모피즘 카드:
backdrop-blur-xl bg-white/10 border border-white/20 rounded-3xl p-8 shadow-2xl
- 채널 브랜드 헤더:
- 항상 상단 중앙(
top: 150px,left: 50%translate-x)에⚡ [CHANNEL_NAME]브랜드 컴포넌트를 표시하며, 환경 변수CHANNEL_NAME(기본값: "mquyen")에서 읽어와 플랫폼(TikTok, Shorts, Reels)의 검색창, 팔로잉/For You 탭에 가려지지 않도록 합니다.
- 항상 상단 중앙(
- 모바일 최적화 타이포그래피 (1080x1920):
- 주요 제목: 72px - 96px, font-black 굵게, 선명한 그래디언트.
- 카드 내용/인용문: 40px - 50px font-extrabold.
- 보조 설명 텍스트: 28px - 36px font-semibold.
- 카테고리 배지: 28px - 32px font-black uppercase.
- 중앙 레이아웃 및 한 줄 자막 (Single-Line Chunked Caption):
- 주요 레이아웃: 모든 중요한 콘텐츠 블록은 화면 정중앙 (세로
justify-center)에 위치해야 합니다. - 자막 위치: 중앙 블록 아래 충분한 간격(
mt-64)을 두어 TikTok/Reels 하단 툴바에 가려지지 않도록 합니다. - 한 줄 형식: 자막은 한 줄(약 5-7단어) 로만 표시되며, 큰 글자
text-4xl font-black로 자동으로 구문 단위로 잘리고 음성 진행(durationInFrames)에 따라 부드럽게 전환됩니다.rounded-full bg-slate-950/95 border-2 border-sky-400/50 px-10 py-5의 알약 카드를 사용하고 키워드를 강조합니다.
- 주요 레이아웃: 모든 중요한 콘텐츠 블록은 화면 정중앙 (세로
4. Antigravity / Codex를 위한 단계별 동영상 제작 프로세스
요청을 받으면: "[주제 X]에 대한 설명 동영상 생성":
단계 1: 스크립트 작성
- 6개 장면(Hook, Problem, Concept 1, Concept 2, Impact, Outro)을 결정합니다.
- 자연스럽고 간결한 베트남어 대사를 작성합니다 (각 장면 15-30단어).
단계 2: 음성 생성 (TTS Generation)
.env에서TTS_PROVIDER를 읽어 edge 또는 cit 중 어떤 것을 사용하는지 확인합니다.generateTopicVoices를 호출하는scripts/generate-<topic>-audio.ts스크립트를 생성하고,npx tsx scripts/generate-<topic>-audio.ts를 실행합니다.manifest.json/src/<TopicName>/audioData.ts에서 실제 프레임 길이 목록을 가져옵니다.
단계 3: 코드 디렉토리 src/<TopicName>/ 생성
표준 디렉토리 구조:
src/<TopicName>/
├── <TopicName>.tsx # 여러 Series.Sequence를 조합하는 메인 Composition
├── types.ts # 스키마 및 속성
├── audioData.ts # 오디오 길이 메타데이터
└── scenes/
├── Scene1Hook.tsx
├── Scene2Problem.tsx
├── Scene3Concept1.tsx
├── Scene4Concept2.tsx
├── Scene5Impact.tsx
└── Scene6Outro.tsx
단계 4: src/Root.tsx에 Composition 등록
src/Root.tsx에 새 Composition을 추가합니다:
<Composition
id="<TopicName>"
component={<TopicName>}
durationInFrames={totalFrames}
fps={30}
width={1080}
height={1920}
defaultProps={{}}
/>
단계 5: 확인 및 보고
npm run lint를 실행하여 TypeScript/ESLint 오류를 확인합니다.- 사용자에게 Remotion Studio에서 Composition을 열 수 있는 링크(
http://localhost:3000) 또는 MP4 렌더링 명령어를 보고합니다:npx remotion render <TopicName> out/<TopicName>.mp4