# ThreadAuto 영상 엔진 통합 설계 미팅

**일시**: 2026-03-17
**안건**: 에반 스타일 + 파이프라인 연결 + Remotion 렌더링 + TTS 싱크 통합 설계
**미팅 모드**: hybrid
**토론 깊이**: thorough
**소집 페르소나**: 이리스(프론트), 불칸(백엔드), 야누스(DevOps), 아르고스(테스터), 로키(DA)

---

## Cycle 1 — Independent Round

### 아누 분석

3개 엔진 파편화 문제:
- `video/evan_dynamic.py` (688줄): 에반 6원칙 O, 콘텐츠 하드코딩 + MoviePy
- `renderer/shortform_renderer.py`: 파이프라인 연결 O, 에반 스타일 X (이미지 슬라이드쇼)
- `remotion/`: ShortForm 컴포지션 + 5개 장면 + 애니메이션 존재, 미완성

제이회장님 확정 방향: Remotion 엔진 + 에반 스타일 + 파이프라인 연결 + TTS-영상 싱크

### 페르소나 의견

**이리스(프론트)**:
- Remotion ShortForm 컴포지션 구조 양호하나 에반 원칙 충족 미흡
  - 타이핑 효과: HookScene만 적용, 나머지 4개 장면 미적용
  - DataScene: body 타입 입력 시 counter가 없어 정적 화면
  - InfoScene: 연속 동일 레이아웃 반복 위험
- TTS 싱크 권장: Python에서 장면별 TTS 생성 → duration 측정 → Remotion props 주입 (접근 B)
- **SCENE_GAP(0.4초) ↔ transitionDuration(0.5초=15프레임) 불일치** → 5장면 기준 0.5초 누적 오차
- Root.tsx에 `calculateMetadata` 추가 필수 (동적 durationInFrames, 없으면 영상 잘림)
- slides_to_scenes vs compose_scenes 이중 구조 통합 필요
- render-server 상주 방식이 CLI 반복 호출보다 2~5배 빠름

**불칸(백엔드)**:
- 데이터 플로우 설계:
  ```
  slides → extract_narration_texts() → TTS 생성 → measure_durations()
  → compose_scenes_with_durations() → concat_audio() → render_shortform()
  ```
- `pipeline_orchestrator.py` 신규 작성 필요 (통합 진입점)
- `extract_narration_texts(slides)` 함수 필요 (슬라이드 타입별 TTS 텍스트 추출)
- 엣지 케이스: 빈 텍스트(silence 대체), TTS>장면(타이핑 하한선), 체크리스트 순차 등장
- 대안: Remotion `<Audio>` 대신 FFmpeg mux 유지가 더 안정적
- verify_av_sync() 검증 함수 필요
- slides_to_scenes() deprecated → compose_scenes() 단일화

**야누스(DevOps)**:
- 7.7GB RAM 현실성: 가능하지만 안전 마진 얇음 (여유 약 2~3GB)
- **즉시 조치**: Swap 4GB + `--gl=swangle` + `--disable-dev-shm-usage` + concurrency=1
- render-server 상주 방식으로 Chromium 재기동 오버헤드 제거
- /tmp 파티션 공간 확인 필수 (Chromium 임시 파일)
- 대안: Hetzner CX21 (€4.5/월, 4GB), 16GB 업그레이드, Remotion Lambda

**아르고스(테스터)**:
- **Python↔TypeScript 인터페이스 계약 테스트 최우선** (스키마 드리프트 방지)
- **TransitionSeries duration overlap 버그 발견**: totalFrames 계산이 transition 오버랩 무시 → 2초 싱크 오차
- 에반 6원칙 자동 검증: 데이터 레벨(typing/delay/x좌표) + 픽셀 레벨(배경 어둡기, 프레임 변화량)
- 파이프라인 분기 테스트로 카드뉴스/텍스트 리그레션 방지

### Devil's Advocate (Cycle 1)

**지정**: 로키 (필수 참석)

1. **실패 시나리오**: 서버 OOM → Chromium crash → 절반만 렌더링된 영상이 SNS에 업로드될 위험
   - **반박**: swap 4GB + concurrency=1 + swangle로 메모리 피크 완화. 렌더링 실패 시 부분 파일 정리 로직 추가. 업로드 전 파일 크기/무결성 검증. **봇 프로세스 OOM 보호(oom_score_adj)** 설정.
   - **판정**: 반박 수용. 인프라 조치 후 리스크 관리 가능.

2. **후회 이유**: Remotion 라이선스 (InsuRo SaaS 전환 시 상업적 배포), Python+Node.js 이중 스택 부담
   - **반박**: InsuRo SaaS 전환은 아직 먼 미래. 그 시점에 라이선스 재검토. 이중 스택은 render_bridge.py가 캡슐화하므로 Node.js 직접 접촉 최소화.
   - **판정**: 일부 수용. 라이선스 모니터링 + SaaS 전환 시점 재검토 조건 부여.

3. **더 단순한 대안**: MoviePy easing 교체(pytweening, ~50줄) + FFmpeg 인코딩 최적화
   - **반박**: 제이회장님이 Remotion을 best로 확정. 에반 6원칙 중 타이핑/시간차/레이아웃 변주는 React 컴포넌트(spring(), CSS, Sequence)가 구조적으로 우위. pytweening으로는 Remotion의 spring() 물리 애니메이션, TransitionSeries 전환 효과를 흉내내기 어려움.
   - **판정**: 기각. 제이회장님 비즈니스 결정 + 기술적 우위 근거 충분.

### 비관습적 대안

**제시자**: 불칸

**대안: Remotion `<Audio>` 대신 FFmpeg post-mux**
- Remotion은 무음 MP4만 렌더링 → Python에서 FFmpeg로 오디오 mux
- 최강 지지: 오디오 경로 문제 없음, Remotion 렌더링 단순화, tts_sync.py의 기존 mux 함수 재사용
- 최강 반론: 렌더링 후 추가 단계, Remotion의 Audio startFrom 정밀 제어 포기
- 이상적 시나리오: audioSrc 경로/프로토콜 문제가 반복될 때
- 노력: 낮음 (기존 mux 함수 있음)
- 리스크: 낮음
- **판정**: 채택. Python에서 TTS concat → FFmpeg mux가 더 안정적. Remotion audioSrc는 사용하지 않음.

---

## 최종 합의

### 1. 아키텍처
- `scene_composer.py`를 정규 변환 함수로 사용 (`slides_to_scenes` deprecated)
- `pipeline_orchestrator.py` 신규 작성 (통합 진입점)
- Remotion `<Audio>` 미사용 → Python FFmpeg mux 유지

### 2. 데이터 플로우
```
5단계 파이프라인 → slides JSON
  ↓
extract_narration_texts(slides) → 장면별 텍스트
  ↓
edge_tts 생성 → WAV/MP3 파일들
  ↓
measure_durations() → 장면별 오디오 길이
  ↓
compose_scenes_with_durations(slides, durations) → Remotion scenes
  ↓
concat_audio() → combined.mp3
  ↓
render_bridge.render_video() → 무음 MP4
  ↓
mux_audio_video() → 최종 MP4
```

### 3. Remotion 수정 사항
- Root.tsx: `calculateMetadata` 추가 (동적 durationInFrames)
- SCENE_GAP ↔ transitionDuration 통일 (0.5초)
- ChecklistScene/InfoScene/CtaScene에 useTypingEffect 적용
- DataScene body 타입 fallback 애니메이션 추가
- ShortFormVideo에서 `<Audio>` 컴포넌트 제거 (FFmpeg mux로 대체)

### 4. 인프라
- 즉시: Swap 4GB + `--gl=swangle` + concurrency=1
- 봇 프로세스 OOM 보호 설정
- 렌더링 전 메모리 여유 3GB 체크
- 장기: VPS 분리 또는 RAM 업그레이드 검토

### 5. 테스트
- P0: Python↔TypeScript 인터페이스 계약 테스트
- P0: TransitionSeries duration overlap 수정
- P1: 에반 6원칙 자동 검증 (데이터 레벨)
- P2: TTS 싱크 엣지 케이스 테스트

### 6. 보안
- subprocess.run(shell=False) 유지
- props는 JSON 파일로 전달 (shell injection 방지)
- InsuRo SaaS 전환 시 Remotion 라이선스 재검토

---

## Temporal Interrogation

### [HOUR 1] 작업 시작
- [RESOLVED] Remotion 패키지 이미 설치 완료 (remotion/node_modules)
- [RESOLVED] FFmpeg 7.0.2 설치 확인, edge_tts Python 패키지 확인
- [OPEN] Swap 4GB 설정 필요 (야누스 권고)
- [OPEN] --gl=swangle 동작 확인 필요 (npx remotion render 테스트)

### [HOUR 2-3] 핵심 구현
- [RESOLVED] 데이터 플로우: slides → TTS → durations → scenes → render → mux
- [RESOLVED] FFmpeg mux 방식 유지 (Remotion Audio 미사용)
- [OPEN] extract_narration_texts() 슬라이드 타입별 텍스트 추출 규칙
- [OPEN] SCENE_GAP 값 통일: 0.5초 (= 15frames/30fps)
- [OPEN] Root.tsx calculateMetadata 구현

### [HOUR 4-5] 구현 후반
- [OPEN] TransitionSeries duration overlap 수정
- [OPEN] 에반 6원칙 검증 테스트 작성
- [OPEN] 인터페이스 계약 테스트 작성

### [HOUR 6+] 마무리
- [OPEN] 실제 렌더링 테스트 (OOM 모니터링)
- [OPEN] 에반 레퍼런스 vs 우리 영상 비교
