# task-436.4 보고서: ThreadAuto Remotion Phase 4 — 최적화 + 고급 효과

**팀**: dev1 (헤르메스 팀장, 이리스/불칸/아르고스)
**일시**: 2026-03-10
**레벨**: Lv.2

---

## 작업 내용

### 4-1. 렌더링 성능 최적화

- **concurrency 옵션**: `RemotionBridge.__init__`에 `concurrency: int = 4` 파라미터 추가. `_build_render_command`, `_build_still_command` 양쪽에 `--concurrency` 플래그 반영.
- **번들 캐시 활용**: `_bundle_cache_dir` 속성 추가. 설정 시 `--bundle-cache-dir` 플래그로 명시적 캐시 경로 지정 가능. stdout에서 "Cached bundle" 감지 시 로깅.
- **벤치마크 유틸**: `benchmark_render()` 메서드 추가 — `time.perf_counter()`로 렌더링 시간 측정, `avg_time/min_time/max_time/iterations` 반환.

### 4-2. 고급 애니메이션 효과

- **CountUp 컴포넌트** (`src/components/CountUp.tsx`): DataScene에서 분리 독립. Easing 커브(linear/easeOut/easeInOut/easeOutExpo), 포맷팅(number/percent/currency), suffix/prefix 크기 자동 계산(메인의 46%).
- **BarChart 컴포넌트** (`src/components/BarChart.tsx`): CSS 기반 수평 바 차트. stagger 방식 순차 등장, interpolate로 바 너비 0→100% 애니메이션.
- **DonutChart 컴포넌트** (`src/components/DonutChart.tsx`): CSS conic-gradient 기반 도넛 차트. segments별 누적 각도 계산, 중앙 합계/라벨 표시.
- **Sparkle 효과** (`src/components/Sparkle.tsx`): CSS 기반 반짝이 효과. `Math.sin(seed)` 결정론적 pseudo-random으로 위치/지연 생성, children wrapper 형태.

### 4-3. Scene 컴포넌트 개선

- **Ken Burns 효과** (`src/animations/kenBurns.ts`): `useKenBurns()` 훅 — scale 1.0→1.15 + translate, `Easing.inOut(Easing.ease)` 적용.
- **타이핑 효과** (`src/animations/typing.ts`): `useTypingEffect()` 훅 — `text.slice(0, Math.floor(...))` 문자열 슬라이싱, `isComplete` 상태 반환.
- **하이라이트 효과** (`src/animations/highlight.tsx`): `HighlightText()` — 텍스트를 키워드 기준으로 세그먼트 분리, 키워드에 accent 색상 + 밑줄 width 0→100% 애니메이션.
- **SceneData 옵션 확장**: `kenBurns?`, `typingEffect?`, `highlightKeywords?` 필드 추가 (모두 optional, 기존 동작 유지).
- **HookScene 통합**: `kenBurns` → AbsoluteFill에 Ken Burns transform, `typingEffect` → 타이틀 타이핑 효과.
- **InfoScene 통합**: `highlightKeywords` → InfoCard body 텍스트에 하이라이트 적용.

### 4-4. 오디오 동기화 개선

- **get_audio_duration()**: ffprobe 기반 오디오 파일 길이(초) 측정. `ffprobe -v quiet -show_entries format=duration -of csv=p=0` 사용. ffprobe 미설치 시 RuntimeError.
- **auto_duration 옵션**: `render_shortform(auto_duration=True)`이고 `audio_path` 지정 시 → MP3 길이를 scene 수로 균등 분배하여 각 scene duration 자동 조정. 기본값 False (기존 동작 유지).

### 4-5. 프로덕션 안정화

- **Chromium 크래시 자동 재시도**: `render_video(max_retries=2)` — stderr에 "Chromium/chrome/SIGKILL/OOM" 키워드 감지 시 `time.sleep(2)` 후 재시도. 일반 에러는 즉시 RuntimeError. 최대 재시도 초과 시 RuntimeError.
- **GL 렌더러 옵션**: `RemotionBridge(gl="swangle")` — `--gl` 플래그로 GPU 없는 서버 환경 지원 (angle/swangle/swiftshader).

### 4-6. 테스트

- **Python pytest**: 69/69 PASSED (기존 34 + Phase 4 신규 35)
  - Phase 4 테스트: concurrency, bundle cache, benchmark, get_audio_duration, auto_duration, Chromium retry, GL 옵션 등
- **TypeScript tsc --noEmit (skipLibCheck)**: 사용자 코드 에러 0건
- **TypeScript 타입 테스트**: Phase4Components.test.tsx에 36개 케이스 (CountUp/BarChart/DonutChart/Sparkle/kenBurns/typing/highlight/SceneData 옵션/HookScene·InfoScene 통합)
- **pyright**: render_bridge.py — 0 errors, 0 warnings

---

## 생성/수정 파일 목록

### 신규 생성 (10개)

**컴포넌트**
- `remotion/src/components/CountUp.tsx` — CountUp 분리 컴포넌트 (이징, 포맷팅)
- `remotion/src/components/BarChart.tsx` — CSS 바 차트 애니메이션
- `remotion/src/components/DonutChart.tsx` — CSS 도넛 차트 (conic-gradient)
- `remotion/src/components/Sparkle.tsx` — 파티클/반짝이 효과

**애니메이션 유틸**
- `remotion/src/animations/kenBurns.ts` — Ken Burns 효과 훅
- `remotion/src/animations/typing.ts` — 타이핑 효과 훅
- `remotion/src/animations/highlight.tsx` — 하이라이트 효과 유틸

**테스트**
- `remotion/src/__tests__/Phase4Components.test.tsx` — 프론트엔드 타입 테스트 36건

### 수정 (5개)

- `remotion/render_bridge.py` — concurrency/gl/bundle_cache/benchmark/get_audio_duration/auto_duration/retry 추가
- `remotion/tests/test_render_bridge.py` — Phase 4 테스트 35건 추가
- `remotion/remotion.config.ts` — 설정 업데이트
- `remotion/src/types/shortform.ts` — SceneData에 kenBurns/typingEffect/highlightKeywords 추가
- `remotion/src/scenes/HookScene.tsx` — Ken Burns + 타이핑 효과 옵션 통합
- `remotion/src/scenes/InfoScene.tsx` — 하이라이트 효과 옵션 통합

---

## 머지 판단

- **머지 필요**: Yes
- **브랜치**: task/task-436.4-dev1
- **워크트리 경로**: /home/jay/projects/ThreadAuto/.worktrees/task-436.4-dev1
- **머지 의견**: remotion/ 디렉토리 내부만 변경. 기존 video/ 코드 미수정 (병행 운영 유지). 모든 신규 옵션은 optional로 기존 동작에 영향 없음. pytest 69/69 통과, tsc 에러 0건, pyright 0 errors. 충돌 가능성 없음.

---

## 셀프 QC

- [x] 1. 이 변경이 다른 파일에 영향을 미치는가? → remotion/ 내부만 변경. 기존 video/ 미수정. 새 옵션은 모두 optional.
- [x] 2. 이 로직의 엣지 케이스는 무엇인가? → ffprobe 미설치 시 RuntimeError (auto_duration=False면 영향 없음), scenes 빈 배열 시 auto_duration 분배 처리, highlightKeywords 빈 배열/미설정 시 기본 동작
- [x] 3. 이 구현이 작업 지시와 정확히 일치하는가? → 4-1~4-6 전 항목 구현 완료 (render-server Express는 선택적으로 표기됨, 미구현)
- [x] 4. 에러 처리와 보안은 확인했는가? → Chromium 크래시 자동 재시도, ffprobe 에러 핸들링, max_retries 초과 시 명시적 에러
- [x] 5. 테스트가 모든 경로를 커버하는가? → Python 69건 (성공/실패/재시도/엣지케이스), TS 타입 테스트 36건

---

## 버그/이슈

- render-server Express.js 세팅은 "(선택적)" 표기로 Phase 4에서 미구현. 필요 시 별도 태스크 가능.
- Audio 컴포넌트 deprecated 경고 (Remotion v4.0.434) 지속: 향후 OffthreadAudio 전환 예정.
- @types/jest 설치 미완료 상태에서 Phase4Components.test.tsx의 describe/it/expect가 IDE에서 경고 발생하나, skipLibCheck으로 빌드 영향 없음.

## 비고

- 기존 Scene 컴포넌트의 기본 동작 100% 유지 (새 옵션 미사용 시)
- render_shortform, render_video 기존 시그니처 완전 호환 (신규 파라미터는 모두 기본값 보유)
- ffprobe 없는 환경에서도 auto_duration=False(기본값)이면 정상 동작
- CountUp 컴포넌트는 DataScene에서 직접 사용 가능하나, Phase 4에서는 독립 컴포넌트로만 생성 (DataScene 내장 CounterDisplay 유지하여 하위 호환)
