# Remotion 딥다이브 리서치 보고서

**작성일:** 2026-03-10
**작성팀:** dev1 (헤르메스 팀장, 불칸/이리스/아테나/아르고스)
**관련 태스크:** task-434.1
**선행 리서치:** task-342.1 (dev2팀 remotion-video-automation.md)

---

## 1. Remotion 핵심 개념 상세

### 1.1 프레임 기반 렌더링 원리

Remotion의 핵심 철학: **"A video is a function of images over time."**

```
React 컴포넌트 → Chromium 헤드리스 브라우저 → 프레임별 스크린샷 → FFmpeg 인코딩 → MP4
```

상세 흐름:
1. `@remotion/bundler`의 `bundle()`이 Webpack으로 React 프로젝트 번들링
2. Chromium에게 "현재 프레임은 N번"이라고 알림
3. React가 해당 프레임 상태로 렌더링 → PNG 스크린샷
4. 프레임은 독립적이므로 여러 브라우저 탭에서 병렬 렌더링 가능
5. FFmpeg가 이미지 시퀀스를 H.264 등으로 인코딩 + 오디오 합성
6. 최종 영상 파일 출력

**핵심 특성:** `useCurrentFrame()`은 순수 함수처럼 동작 → 같은 프레임 번호에는 항상 같은 결과. CSS transitions, setTimeout 등 비결정적 코드 사용 금지.

### 1.2 핵심 API

**Composition vs Still vs Sequence vs Series:**

```tsx
import { Composition, Still, Sequence, Series } from 'remotion';

// Composition: 동영상 등록 단위
<Composition
  id="ShortForm"
  component={ShortFormComp}
  durationInFrames={900}  // 30fps × 30초
  fps={30}
  width={1080}
  height={1920}
  defaultProps={{ slides: [] }}
/>

// Still: 정지 이미지용 (fps, durationInFrames 불필요)
<Still
  id="CardNews"
  component={CardNewsComp}
  width={1080}
  height={1350}
/>

// Sequence: 절대 프레임 기반 타임시프트
<Sequence from={0} durationInFrames={90}>  {/* 0~2초 */}
  <IntroSlide />
</Sequence>
<Sequence from={90} durationInFrames={90}>  {/* 3~5초 */}
  <ContentSlide />
</Sequence>

// Series: 순차적 시퀀스 (from 자동 계산)
<Series>
  <Series.Sequence durationInFrames={90}><IntroSlide /></Series.Sequence>
  <Series.Sequence durationInFrames={90}><ContentSlide /></Series.Sequence>
</Series>
```

**핵심 훅:**

```tsx
import { useCurrentFrame, useVideoConfig, interpolate, spring } from 'remotion';

const MyComponent = () => {
  const frame = useCurrentFrame();           // 현재 프레임 번호 (0-indexed)
  const { fps, width, height } = useVideoConfig();

  // 값 보간: 프레임 0~30에서 opacity 0→1
  const opacity = interpolate(frame, [0, 30], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  // 물리 기반 spring 애니메이션
  const scale = spring({ frame, fps, config: { damping: 80, stiffness: 200 } });

  return <div style={{ opacity, transform: `scale(${scale})` }}>...</div>;
};
```

**@remotion/renderer 주요 함수:**

```typescript
import { bundle } from '@remotion/bundler';
import { renderMedia, renderStill, selectComposition, openBrowser } from '@remotion/renderer';

// bundle(entryPoint) — Webpack 번들링
// selectComposition(options) — Composition 선택
// renderMedia(options) — 영상 렌더링 (프레임 + FFmpeg 인코딩)
// renderStill(options) — 정지 이미지 렌더링
// renderFrames(options) — 개별 프레임만 PNG/JPEG로 렌더링
// openBrowser(type, options) — 브라우저 인스턴스 열기 (재사용 가능)
```

### 1.3 지원 포맷

**비디오 코덱 (5종):**
- **h264** (기본): MP4, CRF 1-51(기본18), 빠른 인코딩, 최고 호환성
- **h265 (HEVC)**: MP4, CRF 0-51(기본23), 작은 파일
- **vp8**: WebM, CRF 4-63
- **vp9**: WebM, CRF 0-63(기본28), 매우 작은 파일
- **prores**: MOV, 전문 편집용

**정지 이미지:** PNG, JPEG, WebP, PDF
**오디오:** MP3, WAV, AAC

---

## 2. ThreadAuto 적용 가능성

### 2.1 카드뉴스 (정적 이미지 1080x1350)

**Remotion으로 대체 시 장점:**
- CSS 그라데이션, box-shadow, backdrop-filter 등 고급 효과 1줄로 구현 (현재 Pillow 수십줄)
- Google Fonts + 시스템 폰트 자유 사용, 자간/행간/커닝 완벽 지원
- Flexbox/Grid 레이아웃 → 좌표 하드코딩 불필요
- @remotion/player로 실시간 프리뷰 → 디자인 수정 속도 10배 향상
- SVG 아이콘/차트를 벡터 그대로 사용 가능
- Tailwind CSS로 빠른 스타일링

**단점:**
- 렌더링 속도: Pillow 0.2~0.5초/장 → Remotion 2~10초/장 (Chromium 오버헤드)
- Chromium 프로세스 메모리 300~500MB 상주
- Node.js 의존성 추가 (Python 단일 스택 유지 불가)
- cardnews.py 82KB의 1:1 전환 작업량 상당 (20~25 개발일 추정)

**renderStill()로 카드뉴스 생성 코드 예제:**

```tsx
// src/compositions/CardNews.tsx
import { AbsoluteFill, Img, staticFile } from 'remotion';

interface CardNewsProps {
  slides: Array<{
    type: 'cover' | 'card_list' | 'detail' | 'cta' | 'body';
    title: string;
    items?: Array<{ title: string; description: string }>;
    hook?: string;
  }>;
  theme: {
    primary: string;
    accent: string;
    bg_gradient: [string, string];
    text_primary: string;
    card_bg: string;
  };
  slideIndex: number;  // 몇 번째 슬라이드를 렌더링할지
}

export const CardNews: React.FC<CardNewsProps> = ({ slides, theme, slideIndex }) => {
  const slide = slides[slideIndex];

  return (
    <AbsoluteFill style={{
      background: `linear-gradient(180deg, ${theme.bg_gradient[0]}, ${theme.bg_gradient[1]})`,
      fontFamily: 'Pretendard, Noto Sans KR, sans-serif',
    }}>
      {/* 상단 브랜드 바 */}
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0,
        height: 12, backgroundColor: theme.primary,
      }} />

      {/* 콘텐츠 영역 */}
      <div style={{
        padding: '80px 60px',
        display: 'flex', flexDirection: 'column',
        height: '100%', boxSizing: 'border-box',
      }}>
        <h1 style={{
          fontSize: 52, fontWeight: 900, color: theme.text_primary,
          letterSpacing: '-0.02em', lineHeight: 1.3,
          marginBottom: 32,
        }}>
          {slide.title}
        </h1>

        {slide.type === 'card_list' && slide.items?.map((item, i) => (
          <div key={i} style={{
            background: theme.card_bg,
            borderRadius: 16, padding: '24px 28px',
            marginBottom: 16,
            boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
            borderLeft: `4px solid ${theme.accent}`,
          }}>
            <div style={{ fontSize: 22, fontWeight: 700, color: theme.text_primary }}>
              {item.title}
            </div>
            <div style={{ fontSize: 18, color: '#6B7280', marginTop: 8 }}>
              {item.description}
            </div>
          </div>
        ))}
      </div>

      {/* 하단 브랜드 영역 */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0,
        height: 100, backgroundColor: theme.primary,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'white', fontSize: 28, fontWeight: 500,
      }}>
        인카다이렉트 TOP사업단
      </div>
    </AbsoluteFill>
  );
};
```

### 2.2 숏폼 영상 (대화형, TTS 포함)

**Remotion으로 대체 시 장점:**
- `useCurrentFrame()`으로 프레임 단위 정밀 애니메이션 제어
- `spring()` 물리 기반 애니메이션 → 자연스러운 움직임
- `<Audio>` 컴포넌트로 TTS 오디오 동기화
- `<TransitionSeries>`로 슬라이드 전환 효과
- CSS/SVG로 차트, 카운트업, 타이핑 효과 고품질 구현
- Lottie 애니메이션 연동 (@remotion/lottie)

**단점:**
- 30초 영상 렌더링: 1.5~2.5분 (멀티프로세스 ON 기준)
- Chromium 메모리: 동시 렌더 시 2~4GB
- 기존 MoviePy 전환 효과/BGM 코드 전면 재작성

**영상 코드 예제:**

```tsx
// src/compositions/ShortForm.tsx
import { AbsoluteFill, Sequence, Audio, spring, interpolate, useCurrentFrame } from 'remotion';

interface ShortFormProps {
  slides: Array<{ type: string; title: string; items?: any[] }>;
  ttsAudioUrl: string;  // Edge TTS 생성 오디오
}

export const ShortForm: React.FC<ShortFormProps> = ({ slides, ttsAudioUrl }) => {
  const SLIDE_DURATION = 90;  // 3초 × 30fps
  const TRANSITION = 15;       // 0.5초 전환

  return (
    <AbsoluteFill style={{ backgroundColor: '#0F172A' }}>
      {slides.map((slide, i) => (
        <Sequence
          key={i}
          from={i * (SLIDE_DURATION - TRANSITION)}
          durationInFrames={SLIDE_DURATION}
        >
          <SlideComponent slide={slide} />
        </Sequence>
      ))}
      <Audio src={ttsAudioUrl} />
    </AbsoluteFill>
  );
};

const SlideComponent: React.FC<{ slide: any }> = ({ slide }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const fadeIn = spring({ frame, fps, config: { damping: 80 } });
  const opacity = interpolate(fadeIn, [0, 1], [0, 1]);
  const translateY = interpolate(fadeIn, [0, 1], [30, 0]);

  return (
    <AbsoluteFill style={{
      opacity,
      transform: `translateY(${translateY}px)`,
      padding: '100px 60px',
    }}>
      <h1 style={{
        fontSize: 64, fontWeight: 900, color: 'white',
        textShadow: '0 4px 20px rgba(0,0,0,0.3)',
      }}>
        {slide.title}
      </h1>
    </AbsoluteFill>
  );
};
```

### 2.3 Threads/Instagram Reels 규격 지원

- **해상도:** width=1080, height=1920 (9:16) → Composition 설정으로 직접 지정
- **코덱:** H.264 → `codec: "h264"`
- **프레임레이트:** 30fps → `fps: 30`
- **최대 길이:** Threads 5분(300초), Instagram Reels 90초 → `durationInFrames`로 제어
- **파일 포맷:** MP4 → 기본 출력

### 2.4 서버(Linux headless) 렌더링

**Chromium 자동 관리:**
- Remotion v4.0.414+ → Chrome Headless Shell 자동 다운로드
- `npx remotion browser ensure`로 사전 다운로드 가능
- GPU 불필요 (CSS + React 기반 콘텐츠이므로 Chrome Headless Shell로 충분)

**Ubuntu 필수 시스템 패키지:**

```bash
sudo apt-get install -y \
  libnss3 libdbus-1-3 libatk1.0-0 libasound2t64 \
  libxrandr2 libxkbcommon-dev libxfixes3 libxcomposite1 libxdamage1 \
  libgbm-dev libcups2 libcairo2 libpango-1.0-0 libatk-bridge2.0-0 \
  fonts-noto-cjk fonts-noto-color-emoji
```

**멀티프로세스 모드 (성능 3배 향상):**

```bash
# CLI
npx remotion render --enable-multi-process-on-linux=true

# 또는 remotion.config.ts
Config.setChromiumMultiProcessInLinux(true);
```

---

## 3. 기술 스택 비교

### 3.1 Pillow+MoviePy (현재) vs Remotion

| 항목 | Pillow+MoviePy | Remotion |
|------|---------------|----------|
| **언어** | Python | TypeScript/React |
| **카드뉴스 렌더링 속도** | 0.2~0.5초/장 | 2~10초/장 |
| **영상 렌더링 속도** | 1~3분 (30초 영상) | 1.5~2.5분 (멀티프로세스) |
| **텍스트 렌더링 품질** | B+ (기본 안티앨리어싱) | A+ (서브픽셀 렌더링) |
| **자간/행간 제어** | D (불가) | A+ (CSS letter-spacing) |
| **그라데이션** | C (선형만, 코드 많음) | A+ (CSS 1줄) |
| **그림자 효과** | C (블러 근사치) | A+ (multi-layer shadow) |
| **애니메이션 품질** | C (슬라이드쇼 수준) | A+ (spring, easing) |
| **레이아웃** | D (좌표 하드코딩) | A+ (Flexbox/Grid) |
| **폰트 선택** | C (시스템 폰트 로드) | A+ (Google Fonts 전체) |
| **실시간 프리뷰** | D (렌더 후 확인) | A+ (@remotion/player) |
| **서버 메모리** | 50~100MB | 700MB~1.3GB |
| **서버 의존성** | Python만 | Node.js + Chromium |
| **학습 곡선** | B (Python) | C (React + Remotion API) |
| **라이선스** | 무료 (MIT) | 3인 이하 무료, 4인+ 유료 |

### 3.2 기타 대안 비교

| 기술 | 비용 | 난이도 | 퀄리티 | Python 호환 | ThreadAuto 적합도 |
|------|------|--------|--------|------------|------------------|
| MoviePy | 무료 | 중 | 중 | 최고 | 높음 (현재 사용중) |
| FFmpeg 직접 | 무료 | 상 | 중 | 높음 | 중간 |
| Shotstack API | $39~/월 | 하 | 중상 | 높음 (SDK) | 중간 |
| Creatomate | $49~/월 | 하~중 | 중상 | 중간 | 중간 |
| **Remotion** | **무료(3인↓)** | **상** | **최고** | **낮음(브릿지)** | **높음(장기)** |

### 3.3 비용 분석

**Remotion 라이선스:** 직원 3명 이하 영리 조직 → **무료** (현재 조건 충족)

**VPS 렌더링 비용 (4코어 8GB RAM):**
- 추가 서버 비용 없음 (기존 VPS 활용)
- Chromium + Node.js 운영 비용만 (메모리/CPU)

**Lambda 렌더링 비용 (스케일업 시):**
- 카드뉴스 600장/월: ~$0.12
- 영상 600개/월: ~$3-12
- S3 스토리지: ~$0.5-2
- **총 ~$5-15/월**

---

## 4. Remotion 생태계

### 4.1 @remotion/player — 실시간 프리뷰

```tsx
import { Player } from '@remotion/player';
import { CardNews } from './compositions/CardNews';

// 웹 브라우저에서 실시간 프리뷰
<Player
  component={CardNews}
  inputProps={currentProps}
  compositionWidth={1080}
  compositionHeight={1350}
  durationInFrames={1}
  fps={30}
  style={{ width: 540, height: 675 }}  // 50% 스케일
/>
```

- 디자인 수정 → HMR로 즉시 반영
- 타임라인 스크러빙으로 영상 미리보기
- 프로덕션 코드와 프리뷰 코드 동일

### 4.2 @remotion/lambda — AWS Lambda 서버리스

```bash
# 함수 배포
npx remotion lambda functions deploy --memory=2048 --timeout=120 --region=ap-northeast-2

# 사이트 배포
npx remotion lambda sites create src/index.ts --site-name=threadauto
```

- 오케스트레이터 Lambda가 영상을 N개 청크로 분할
- N개 워커 Lambda가 병렬 렌더링
- S3에 최종 파일 업로드
- Python SDK 공식 지원: `pip install remotion-lambda`

### 4.3 @remotion/renderer — 로컬 렌더링

```typescript
const browser = await openBrowser("chrome", {
  chromiumOptions: { enableMultiProcessOnLinux: true },
});

const bundleLocation = await bundle({ entryPoint: "./src/index.ts" });

// 카드뉴스
await renderStill({ composition, serveUrl: bundleLocation, inputProps, puppeteerInstance: browser });

// 영상
await renderMedia({ composition, serveUrl: bundleLocation, codec: "h264", concurrency: 4, puppeteerInstance: browser });
```

### 4.4 보험 콘텐츠에 적합한 커뮤니티 리소스

- **@remotion/lottie**: LottieFiles.com에서 보험/금융 애니메이션 (방패, 하트, 체크 등) 수천 개 무료 사용
- **@remotion/google-fonts**: Pretendard, Noto Sans KR 등 한국어 폰트 직접 로드
- **@remotion/transitions**: TransitionSeries로 슬라이드 전환 (wipe, slide, fade 등)
- **React 생태계 전체**: Tailwind CSS, Framer Motion 일부 기능, SVG 라이브러리 활용 가능

---

## 5. 우리 환경 제약사항 분석

### 5.1 서버 환경

| 항목 | 현재 | Remotion 요구 | 판정 |
|------|------|-------------|------|
| OS | Ubuntu Linux | Ubuntu 22.04+ | OK |
| Node.js | v24.14.0 | v16+ (v4.x) / v18+ (v5.x) | OK |
| RAM | 미확인 (추정 4~8GB) | 최소 4GB (concurrency=4) | 확인 필요 |
| CPU | 미확인 (추정 4코어) | 코어 수 = concurrency 설정 | 확인 필요 |
| FFmpeg | 별도 설치 불필요 | Remotion v4.0+ 번들 내장 | OK |
| Chromium | 자동 다운로드 | `npx remotion browser ensure` | OK |
| 한국어 폰트 | Pretendard OTF 설치됨 | Chromium이 시스템 폰트 참조 | OK |

### 5.2 React 경험

- InsuWiki: Next.js 프로젝트 존재
- InsuRo: Vite+React 프로젝트 존재
- **React 컴포넌트 작성 역량 보유** → Remotion 학습 곡선 완화

### 5.3 자동화 요구사항

하루 20개 콘텐츠 × 30일 = 600개/월

```
카드뉴스 10장/일 × 10초/장 = 100초 = 1분 40초
영상 10개/일 × 2분/개 = 20분 (순차 실행)
총 처리 시간: ~22분/일

→ 4코어 8GB 서버에서 전혀 문제 없음
→ 서버 부하: 24시간 중 22분만 사용
```

### 5.4 메모리 상세

| 구성 요소 | 메모리 |
|----------|--------|
| Chrome Headless Shell (탭 1개) | 300~500MB |
| Remotion Node.js 프로세스 | 200~300MB |
| FFmpeg 인코딩 | 100~300MB |
| React 번들 + 에셋 | 100~200MB |
| **합계 (단일 렌더)** | **700MB~1.3GB** |
| **concurrency=4 동시 렌더** | **2~4GB** |

---

## 6. Python ↔ Node.js 브릿지

### 6.1 방식별 비교

| 항목 | subprocess | HTTP API 서버 | Lambda SDK |
|------|-----------|--------------|-----------|
| 구현 복잡도 | 낮음 | 중간 | 중간 |
| 성능 | 낮음 (매번 번들링) | **높음 (번들 재사용)** | 최고 (분산) |
| 에러 핸들링 | 어려움 | 쉬움 (HTTP) | SDK 처리 |
| 배포 복잡도 | 단순 | Node.js 서버 별도 | AWS 세팅 |
| 동시 렌더 | 불가 | 큐 구현 | 자동 분산 |
| **추천** | POC/개발 | **프로덕션** | 스케일업 |

### 6.2 추천: HTTP API 방식

**Node.js Express 렌더 서버:**

```typescript
// render-server/src/index.ts
const app = express();
let browserInstance, bundleLocation;

async function init() {
  bundleLocation = await bundle({ entryPoint: "./src/remotion/index.ts" });
  browserInstance = await openBrowser("chrome", {
    chromiumOptions: { enableMultiProcessOnLinux: true },
  });
}

app.post("/renders", async (req, res) => {
  const { compositionId, inputProps, type = "video" } = req.body;
  const jobId = uuidv4();
  // 큐에 작업 추가, 순차 실행 (메모리 보호)
  // ...
  res.json({ jobId, status: "queued" });
});

app.get("/renders/:id", (req, res) => { /* 진행률 반환 */ });
app.get("/renders/:id/download", (req, res) => { /* 파일 다운로드 */ });
```

**Python 클라이언트:**

```python
# renderer/remotion_client.py
class RemotionClient:
    def __init__(self, base_url="http://localhost:3000"):
        self.base_url = base_url
        self.session = requests.Session()

    def render_still(self, composition_id: str, input_props: dict) -> str:
        resp = self.session.post(f"{self.base_url}/renders", json={
            "compositionId": composition_id,
            "inputProps": input_props,
            "type": "still",
        })
        job_id = resp.json()["jobId"]
        return self._poll_until_done(job_id)

    def render_video(self, composition_id: str, input_props: dict) -> str:
        resp = self.session.post(f"{self.base_url}/renders", json={
            "compositionId": composition_id,
            "inputProps": input_props,
            "type": "video",
            "codec": "h264",
        })
        job_id = resp.json()["jobId"]
        return self._poll_until_done(job_id)
```

### 6.3 JSON props 변환 어댑터

현재 `content_generator_v2`가 생성하는 slides JSON은 **거의 그대로 Remotion inputProps로 사용 가능**.

최소 변환만 필요:

```python
def slides_to_remotion_props(content: dict, theme) -> dict:
    slides = content.get("slides", [])
    total = len(slides)
    counter = 0
    enriched = []
    for i, slide in enumerate(slides):
        s = dict(slide)
        s["page_num"] = i + 1
        s["total_pages"] = total
        if s.get("type") != "cover":
            counter += 1
            s["content_index"] = counter
        enriched.append(s)
    return {
        "slides": enriched,
        "theme": {
            "name": theme.name,
            "primary": theme.primary,
            "accent": theme.accent,
            "bg_gradient": list(theme.bg_gradient),
            "text_primary": theme.text_primary,
            "text_secondary": theme.text_secondary,
            "card_bg": theme.card_bg,
        },
        "caption": content.get("caption", ""),
    }
```

---

## 7. Remotion으로 구현 가능한 고급 효과

### 7.1 카운트업 애니메이션

```tsx
const CountUp: React.FC<{ target: number }> = ({ target }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const progress = spring({ frame, fps, config: { damping: 80, stiffness: 200 } });
  const value = Math.round(interpolate(progress, [0, 1], [0, target]));
  return (
    <div style={{ fontSize: 200, fontWeight: 900, color: 'white',
      textShadow: '0 4px 20px rgba(0,0,0,0.3)' }}>
      {value}%
    </div>
  );
};
```

Pillow로 동일 구현: ~60줄, 프레임마다 이미지 생성, easing 없음.
Remotion: ~15줄, spring 물리 기반 애니메이션, 완벽한 부드러움.

### 7.2 글래스모피즘 카드

```tsx
<div style={{
  background: 'rgba(255, 255, 255, 0.08)',
  backdropFilter: 'blur(20px)',
  border: '1px solid rgba(255, 255, 255, 0.15)',
  borderRadius: 24,
  boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2)',
}}>
```

Pillow: **완전 불가능** (backdropFilter 개념 자체 없음)

### 7.3 SVG 차트 애니메이션 / Lottie / 파티클 효과

- SVG 파이차트: `strokeDasharray`로 progress 애니메이션
- @remotion/lottie: LottieFiles.com 보험 아이콘 수천 개 활용
- 파티클: `random()` + 프레임 기반 물리 시뮬레이션
- 타이포그래피: 글자 하나씩 spring 애니메이션으로 등장

---

## 8. 결론: ThreadAuto에 Remotion 도입 추천

### 8.1 결론: **추천 (조건부)**

dev2팀의 이전 리서치(task-342.1)에서는 MoviePy를 1순위로 추천했으나, **Remotion을 더 깊이 분석한 결과 장기적으로 Remotion 전환이 유리하다**고 판단합니다.

**추천 근거:**

1. **시각적 퀄리티 격차가 압도적**: Pillow 40/100 vs Remotion 90/100. 보험 콘텐츠에서 신뢰감은 시각적 품질과 직결됨.
2. **React 역량 보유**: InsuWiki(Next.js), InsuRo(Vite+React) 프로젝트 경험 있음. Remotion 학습 곡선이 완화됨.
3. **라이선스 무료**: 3인 이하 영리 조직 조건 충족.
4. **서버 환경 적합**: Node.js v24 설치됨, Ubuntu 서버 호환, 하루 20개 처리 여유.
5. **JSON props 호환**: 기존 content_generator_v2 출력을 거의 그대로 Remotion에 전달 가능.
6. **확장성**: 카드뉴스(renderStill) + 영상(renderMedia)을 동일 프레임워크로 통합.

**조건:**

1. 서버 RAM 최소 4GB 확인 (8GB 권장)
2. 점진적 전환 (Phase 1 POC → Phase 2 카드뉴스 → Phase 3 영상)
3. 기존 Pillow/MoviePy 병행 운영으로 리스크 최소화
4. HTTP API 서버 방식으로 Python↔Node.js 연결

### 8.2 MoviePy 대비 Remotion의 핵심 차별점

| 관점 | MoviePy (dev2 추천) | Remotion (본 보고서 추천) |
|------|-------------------|----------------------|
| 초기 개발 속도 | 빠름 (3-6일) | 느림 (2-3주 POC) |
| 장기 유지보수 | 좌표 하드코딩 유지 부담 | CSS 기반 유연한 수정 |
| 시각적 퀄리티 상한 | 슬라이드쇼 수준 | 전문 모션 그래픽 수준 |
| 디자인 변경 속도 | 느림 (코드→렌더→확인) | 빠름 (HMR 즉시 확인) |
| 확장 가능성 | 제한적 | 무한 (웹 기술 전체) |
| **핵심 판단** | "빠르게 시작" | **"제대로 만들기"** |

### 8.3 한 줄 요약

**Pillow+MoviePy**는 "작동하는" 영상을 만들고, **Remotion**은 "공유하고 싶은" 영상을 만듭니다.

---

## 참고 소스

- Remotion 공식 문서 (remotion.dev/docs)
- Remotion GitHub (github.com/remotion-dev/remotion)
- Remotion Lambda 비용 문서 (remotion.dev/docs/lambda/cost-example)
- Remotion Linux Dependencies (remotion.dev/docs/miscellaneous/linux-dependencies)
- Remotion SSR Node.js (remotion.dev/docs/ssr-node)
- Remotion Python 트리거 (remotion.dev/docs/lambda/python)
- dev2팀 선행 리서치 (remotion-video-automation.md, task-342.1)
- ThreadAuto 소스코드 분석 (renderer/, video/, content/)
