华尔街式解读片 (wallstreet-explainer)은(는) 무엇을 하나요?
큰 장부를 설명한다: 진짜 사람, 진짜 뉴스, 진짜 화면이 증거가 되고, 그래픽 스테이지가 숫자와 관계를 연기한다; 각 컷은 이전 컷의 것을 이어받아 계속 나아가며, 시청자는 항상 '지금 무엇을 보고 있는지, 왜 보고 있는지'를 안다. 움직이는 것은 카메라와 세계이지, 요소 자체가 연기하는 것이 아니다: 카메라가 전후 레이어가 있는 무대 안에서 밀고, 당기고, 흔들며, 지금 말하고 있는 것을 찾아간다; 사물은 제자리에서 상태를 바꾼다. 모든 움직임은 그 문장의 의미에 맞춰진다.
이 스킬이란 무엇인가
-
이것은: 일련의 설계 사고(각 컷에 무엇을 넣을지, 왜 넣는지, 어떻게 다음 컷으로 이어지는지), 일련의 모션 원리(세 종류의 유사 참고 영상을 프레임별로 측정하여 도출: 왜 PPT나 AI 같지 않은지, 기법 목록 및 정량적 목표), 일련의 스타일 언어(무대, 큰 글자, 크로마키, 디스크, 숫자, 차트, 원문 하이라이트의 구체적인 구현 방법, 그리고 시작 훅 메뉴), 그리고 일련의 도구입니다.
-
템플릿이 아닙니다. 복사할 수 있는 화면 엔지니어링이나 고정된 컷 순서가 없습니다. 각 영상의 화면은 해당 스크립트와 소스 자료에서 그때그때 새로 구상해야 합니다.
examples/ai-burn-money/는 하나의 영상이 어떻게 구상되고, 어디서 실수했는지를 기록한 것이지, 그 컷을 그대로 따라 하라는 것이 아닙니다. -
느린 푸시, 숫자 스크롤, 실물 위에 글자 겹치기, 참고 영상 모두가 사용한다. 싸구려가 되는 것은 이런 기법 자체가 아니라, 구현 방식입니다. 판단은 세 문장으로: 그것을 지우면 시청자가 무엇을 덜 이해하게 되는가? 카메라가 움직이고 사물의 상태가 바뀌는가, 아니면 요소 자체가 등장 연기를 하는가? 이 레이어는 진짜이고 구체적인가?
작업을 시작하기 전에 먼저 references/thinking.md와 references/motion.md를 읽고, 필요에 따라 다른 문서들을 읽으십시오.
프로세스: 각 단계마다 사용자에게 보여주고 확인받기
미적 판단은 사용자가 합니다. 한 번에 완성본을 만들지 말고, 각 단계에서 구체적인 결과물을 보여주고 사용자가 승인할 때까지 기다리십시오.
| 단계 | 하는 일 | 사용자에게 보여줄 것 |
|---|---|---|
| 0 주제 선정 | 주제는 '지금 뜨겁다'와 '보고 나서 가져갈 것이 있다'를 동시에 만족해야 함 | 두세 개의 주제, 각각 한 문장 훅 첨부 |
| 1 스크립트 | 구어체, 1인칭, 문장마다 다음으로 이어짐; 원음을 삽입하는 부분에는 먼저 준비 문장을 씀(references/audio.md) | 전체 원고. 승인 전에는 공식 녹음하지 않음 |
| 2 감독표 | 아래 '감독표' 항목 참조 | 요점: 시작을 어떻게 잡을지, 각 부분을 어떻게 연결할지, 준비 문장을 추가할지 여부 |
| 3 소스 자료 | 공식 고화질 영상 → 뉴스 원문 → 당사자 원음 → 당사자 크로마키용 전체 실물 영상(컷 편집 포인트 없음, 대사와 맞는 동작 있음). 먼저 연락처 표를 만들어 확인한 후, 인포인트를 정하고 프레임별로 컷 편집 포인트를 확인(references/sourcing.md) | 주요 소스 자료(선택 사항) |
| 4 음성 및 타임라인 | scripts/temp_voice.py: 먼저 임시 음성으로 타임라인을 구성하고, 모든 화면은 글자에 고정 | — |
| 5 시작 | 먼저 메커니즘이 다른 3개의 훅을 골라, 각각 5–8초 러프 컷을 만들어 사용자에게 보여줌(references/opening.md); 선택된 훅은 0초부터 첫 번째 전환 부분까지의 샘플(15–20초, 사운드 포함)로 제작 | 3개의 훅 러프 컷 → 선택 후 시작 샘플 |
| 6 전체 영상 720p | 감독표에 따라 배치 완료; 2–3컷마다 스틸 프레임을 캡처하여 확인, 중요한 동작은 연속 프레임 확인(strip.py); 제출 전에 motion_check.py 실행 | 720p 전체 영상, 프레임 추출 이미지, 그리고 2–3군데를 지정하여 사용자가 집중해서 보도록 요청 |
| 7 완성본 | 만족할 때까지 수정; 공식 음성으로 교체; 1080p로 한 번만 렌더링 | 1080p |
매번 전달 시 다음을 반드시 수행:
ffprobe를 사용하여 파일이 열리는지 확인.- 스스로 프레임별로 스틸 프레임과 연락처 표를 확인하고, 중요한 동작은 연속 프레임을 확인.
- 고정된 리뷰 폴더에 넣고, 번호와 한글 이름으로 명명하며, 이전 버전에는
旧_접두사를 추가. - 이번 버전에서 무엇을 변경했는지, 어떤 항목을 측정했는지, 사용자가 직접 듣거나 봐야 할 것은 무엇인지 명확히 설명.
감독표
전체 영상의 유일한 계획입니다. 사용자가 이를 통해 화면을 확인하고, 제작 시 이를 따르며, 리뷰 시 이를 대조합니다. 각 컷은 다음 열을 포함:
| 컷 | 시간 | 스크립트 | 화면에 무엇이 있고, 무엇을 하는가 → 멈췄을 때 무엇을 남기는가 | 시청자가 무엇을 더 이해했는가 | 담당 | 이해 시점 + 체류 | 연결 |
|---|
- 시청자가 무엇을 더 이해했는가: 이전 컷과 다른 구체적인 문장 하나. 쓸 수 없다면 이 컷은 삭제하거나 다시 구상해야 함.
- 담당: 이 컷이 무엇으로 이해를 돕는가. 인물 / 실증(실제 소스, 원문) / 구성(디스크, 차트) / 텍스트(큰 글자, 결론) / 혼합.
- 연결: 이전 컷이 남긴 어떤 것이 이 컷에 의해 이어받아지는지, 연결 방식은
이어받기 / 컷 / 지나가기 / 밀어넣기 / 확장 / 장 전환과 함께 그 대상을 씀. 첫 컷은—. - '화면' 열에 카메라가 어떻게 움직이는지를 함께 명확히 씀: 각 이동의 착점이 누구인지(누구에게로 밀고, 누구를 찾아 흔들고, 언제 뒤로 당겨 전체를 보는지), 그리고 몇 개 컷에서 당사자 크로마키를 사용하는지.
- 스크립트는 한 글자도 바꾸지 않음; 한 컷이 여러 문장을 포함할 수 있음; 항상 단어에 고정(''칠천'이라고 말하는 그 순간').
작성 후 '읽기 테스트' 수행: 스크립트와 '더 이해한 것' 열을 가리고, '화면' 열만 읽어도 이해가 되는지 확인. 그리고 연속된 여러 하드 컷이 없는지, 충분히 멈추지 않은 컷이 없는지 확인. 예시는 examples/ai-burn-money/director.md 참조.
어떤 문서를 읽을까
| 무엇을 하고 있는가 | 읽을 문서 |
|---|---|
| 한 컷에 무엇을 넣을지, 왜 넣는지, 어떻게 다음 컷으로 이어지는지 고민 | references/thinking.md |
| 움직임: 왜 PPT/AI 같지 않은지, 기법 목록, 정량적 목표, 카메라와 크로마키의 코드 감각 | references/motion.md |
| 화면이 어떻게 생겼는지: 무대, 큰 글자, 크로마키, 디스크, 숫자, 차트, 영상 제목, 자막, 실물 프레임 | references/style.md |
| 두 컷 사이의 연결 방식, 카메라 움직임과 동작의 감각 | references/grammar.md |
| 시작 | references/opening.md |
| 영상 검색, 다운로드, 인포인트 선정, 크로마키, 뉴스 원문, 원음 | references/sourcing.md |
| 스크립트, 음성, 원음 편집 컷, 음향 효과, 오디오 믹싱 | references/audio.md |
| 제출 전 자가 점검 | references/review.md |
| 실전에서 부딪힌 함정 | references/lessons.md |
도구 (scripts/)
| 스크립트 | 용도 |
|---|---|
footage.py | search 영상 검색 / get 다운로드(일부 구간만, 오디오만 가능) / sheet 타임코드 포함 연락처 표 / scenes 컷 편집 포인트 / extract 30fps 프레임 시퀀스 추출 / wave 원음 컷 편집 지점의 음량 확인 |
snap_news.mjs | 뉴스 및 보고서 원문 스크린샷: 광고 차단, 플로팅 레이어 제거, 폰트 로딩 완료 후 키워드 박스 측정 |
matte.py | 실물 영상 프레임별 크로마키(GPU에서 ISNet 약 0.25초/프레임), 숨겨진 컷 편집 포인트 알림, 사람 외곽 프레임 부분만 저장 |
matte_clean.py | 크로마키 후처리: 가는 선과 잔여물 제거, 열 단위로 붙어 있는 등받이 제거, 각 프레임의 머리-어깨 중심점 기록(사람 배치 시 추적용) |
stage25d.js | 페이지 내에서 사용하는 작은 도구: 레이어 카메라, 방향 모션 블러, 피사계 심도, 크로마키 사람 추적, 큰 글자, 부제목, 실물 배경 전환, 10억 단위 반올림. 단지 도구 함수일 뿐, 컷 자체가 아님 |
temp_voice.py | script.json → 임시 음성(edge-tts) 및 timeline.json(각 단어의 시점, 원음 삽입 구간, 영상 제목 위치); 공식 음성이 있으면 자동으로 교체, --asr로 다시 정렬 |
render.mjs | 프레임별 렌더링: --stills 프레임 보기, --video --range 구간 렌더링, --h 1080 완성본 출력 |
strip.py | 연속 프레임 이미지: 하나의 동작 8장, 스틸 프레임으로는 보이지 않는 실수 확인; 참고 영상의 동작 분석에도 사용 |
motion_check.py | 카메라 움직임 자가 점검: 화면이 전혀 움직이지 않은 시간, 정지 횟수, 카메라가 움직인 시간, 전후 레이어, 푸시/풀 비율, 참고 영상의 구간과 비교 |
mix.py | 음성, 원음, 음향 효과, 배경 음악(배경 음악은 음성에 양보) → -14 LUFS; --check 각 구간 음량 측정 |
의존성: ffmpeg, yt-dlp, Python(edge-tts, numpy, opencv-python, Pillow; 선택 사항 faster-whisper; 크로마키에는 onnxruntime-gpu와 rembg가 다운로드한 모델 필요), Node 및 Playwright. 설치 방법은 README 참조.
음성: 먼저 임시 음성 사용
- 먼저
temp_voice.py로 edge-tts 임시 음성을 생성하고, 타임라인을 구성하며, 화면은 이를 따라 제작. - 스크립트와 화면이 확정된 후, 공식 음성(모든 TTS, 음성 클로닝 또는 실제 녹음 가능)으로 교체: 각 문장을
voice/<id>.final.wav로 저장하고,temp_voice.py script.json --asr실행. - 음성은 항상: 시작과 끝의 무음을 제거하고, 원래 속도를 유지하며, 문장 간 멈춤은 오직
gap에 의해서만 결정됨. 사후에 atempo로 가속하지 말 것. 무음을 제거한 후 가속하면 매우 급박하게 들림.
영상 구성 방법 (뼈대, 그대로 따라 할 것이 아님)
- 하나의 영상 디렉터리:
index.html,film.css,film.js,timeline.json,assets/(실물 프레임은assets/clips/<이름>/, 크로마키는assets/cut/<이름>/). 카메라 유틸리티를 사용하려면scripts/stage25d.js를 복사하여 film.js보다 먼저 로드. film.js는window.ready와window.seek(t)를 노출.seek는 t가 속한 컷(shot(t0, t1, fn))을 찾아fn(t)를 호출하여 이 프레임의 HTML을 반환하고, 이미지 디코딩 및 폰트 로딩이 완료될 때까지 기다림. 동일한 t는 항상 동일한 프레임을 그리며, CSS 애니메이션이나 재생 클록에 의존하지 않음.- 시점은 항상
cue(문장id, '단어')로 작성하고, 타임라인에서 가져오며, 초 단위로 하드코딩하지 않음. 음성이 교체되면 전체 영상이 자동으로 연기됨. - 실물 영상은 프레임 시퀀스로 추출하여 t에 따라 프레임을 가져오고,
<video>를 사용하지 않음. 프레임 수를 등록해야 하며, 누락되면 존재하지 않는 프레임을 가져와 검은 화면이나 빈 프레임이 발생할 수 있음. - 한 컷 = 하나의 카메라 위치 함수 + 여러 레이어 화면: 카메라 위치는
camPath(t, 시작점, 이동표), 각 레이어는L(카메라위치, 이전프레임카메라위치, z, html)로 감싸며, 먼 레이어는 z가 작고, 모션 블러가 자동으로 계산됨. - 컷 간 연결이 필요할 때는 이전 컷의 함수가 마지막 순간에 그린 화면을 가져와 다음 컷의 시작 장면으로 사용(카메라 밀어넣기, 축소하여 이동, 펼치기, 옆으로 밀어내기). 이렇게 하면 이전 컷의 마지막 상태와 다음 컷의 첫 번째 상태가 자연스럽게 일치함.
- 720p로 효과를 확인하고, 1080p는 최종 확정 후에만 한 번 렌더링.