소개
이 스터디를 세 주 들었는데 만든 영상이 0편이었습니다. 재료가 없어서도 아니고 도구가 없어서도 아니었습니다.
주차
그때 내린 결론
만든 영상
1주차
이미지 115장 중 94장이 행사 사진, 음성 21개는 전부 회의 녹음. 재료가 없다
0편
2주차
20260701_umap 그림 7장을 소재로 확정. 재료를 찾았다
0편
3주차
설치 팩에 영상 제작 스킬 30개가 들어와 있었다. 도구도 생겼다
0편
3주차 발표에서 다른 분이 쓴 순서에 제 재료를 넣어 보니 어디가 비었는지 드러났습니다.
타이틀 → 컷별 나레이션 → 컷별 길이(7.28초, 3.79초 …) → 1차 블루프린트 → 생성
단계
제 상태
타이틀
없음
컷별 나레이션
없음
컷별 길이
없음
소재 이미지
7장 있음
제작 도구
스킬 30개 (호출 0회)
앞뒤가 있고 가운데가 비어 있었습니다. 막힌 자리는 대본이었습니다.
umap 그림 7장으로 대본을 쓰려면 그 분석을 설명하는 문장을 새로 써야 합니다. 대본 쓰기가 새 일이 되는 구조입니다.
그러다 눈에 들어온 것이 다른 스터디에 낸 사례 발표 자료였습니다. 수요일 결과물검수 스터디에서 발표한 자료 두 벌이 이미 있었습니다. 사례 발표 자료는 영상 대본으로 조건이 좋습니다.
영상 대본에 필요한 것
사례 발표 자료에 이미 있는 것
훅
첫 장에 잡아 둔 한 문장
뼈대
슬라이드 순서가 곧 컷 순서
숫자
측정해서 넣은 값과 출처
그림
발표용으로 만든 figure
팔레트
발표 자료의 색과 글꼴
무엇보다 남에게 한 번 설명해 본 자료라 순서가 이미 검증돼 있습니다. 이 자료로 영상을 만들어 보기로 했습니다.
진행 방법
쓴 도구와 비용
종량제 API를 한 번도 부르지 않았습니다.
자리
쓴 것
비용
나레이션
Edge TTS ko-KR-SunHiNeural
0원
슬라이드
HTML을 크롬 헤드리스로 캡처
0원
자막·합성·전환
ffmpeg 8.1.1
0원
글꼴
Pretendard (OFL)
0원
배경음악
macOS 기본 제공 Apple Loops
0원
두 번째 판
리모션 (무료 등급)
0원
전체 흐름
사례 발표 자료 → 나레이션 원고 → 문장 단위로 자르기 → 문장별 TTS
↓
슬라이드 HTML → 크 롬 헤드리스 캡처 → 자막 PNG 합성 → 크로스페이드 → 배경음악 → mp4 + srt
1단계. 발표 자료를 나레이션으로 옮기기
슬라이드는 문장이 끊겨 있습니다. 명사로 끝나는 제목, 표, 불릿이라 소리 내어 읽으면 어색합니다. 두 편에서 이 문제를 다르게 풀었습니다.
편
대본으로 쓴 것
손이 든 정도
video1
발표 자료를 문장으로 풀어 씀
46문장으로 다시 이어야 했습니다
video2
발표 준비 중에 쓴 블로그 글
이미 문장이라 손댈 것이 적었습니다
발표 자료를 만들 때 블로그 글을 함께 써 두면 영상 대본이 공짜로 생깁니다.
TTS가 읽을 원고라 아라비아 숫자를 전부 한글로 바꿨습니다. 자리마다 계열이 다릅니다.
자리
표기
예
개수·횟수
고유어
스물일곱 개, 아흔세 개, 마흔한 번
퍼센트·비율
한자어
이십이 퍼센트
백 단위 이상
한자어
백이십 개
2줄을 그대로 두면 "이줄"로 읽고, 1번은 "한 번"으로 읽습니다. 화면 자막에서는 반대로 숫자로 되돌립니다.
2단계. 슬라이드를 코드로 정의
슬라이드를 하나씩 그리지 않고 HTML의 배열 하나로 정의했습니다. 종류는 title, h2, big, quote, fig, flow, table, two, lines 아홉 가지입니다.
const S = [
{k:'title', h:'논문이 주장한 수치를', s:'<em>직접 재봤습니다</em>'},
{k:'big', kicker:'코퍼스', big:'57', unit:'편',
cap:'25편에서 시작해<br>survey-refresh를 돌릴 때마다 늘었다', teal:true},
{k:'fig', src:'01_wiki_nodes.png', cap:'핵심 논문을 위키 정본 노트로 옮긴 결과'},
{k:'table', kicker:'같은 채점기, 갈린 결과',
rows:[['단독 가설 (n=8)','0.260','bad'],
['병용 약물 (n=11)','0.642','teal'],
['사람 상한선 (n=78)','0.745','']]},
{k:'two', kicker:'그런데', heads:['원문이 말하는 것','원문이 말하지 않는 것'],
rows:[['전문의 9명을 두 그룹으로','몇 명씩, 어떤 기준으로 나눴는지'],
['가설 78건, 15개 항목 루브릭','두 그룹이 같은 78건을 봤는지']]},
];
캡처는 크롬 헤드리스로 돌립니다.
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
for n in $(seq 1 24); do p=$(printf "%02d" $n)
"$CHROME" --headless --disable-gpu --hide-scrollbars --allow-file-access-from-files \
--window-size=1920,1080 --screenshot="slide_$p.png" --virtual-time-budget=2500 \
"file://$PWD/07_슬라이드.html#$n" >/dev/null 2>&1
done
발표 자료의 팔레트와 figure는 그대로 가져왔습니다. 슬라이드 자체는 새로 만들어야 했습니다. 발표용 슬라이드는 발표자가 말로 채우는 것을 전제해 정보가 성기고, 영상 슬라이드는 자막이 아래를 차지하니 세로 여백도 다릅니다.
3단계. 문장과 슬라이드를 잇기
문단이 아니라 문장 단위로 잇습니다. 문단을 균등분할하면 문단 안에서 조금씩 밀려 슬라이드가 멘트보다 먼저 뜹니다.
# 슬라이드번호 문장시작 문장끝
1 1 2 # 타이틀
4 7 7 # 기권 시험 10건 # "근거 문서가 없는 변이 열 건을 넣어 봤습니다"
5 8 8 # figure # "열 건 전부 판단을 보류했습니다"
그림 슬라이드는 그 그림이 뒷받침하는 문장에 붙입니다. 붙일 문장이 없으면 나레이션 문장을 둘로 나눕니다. 실제로 기권 재현율 그래프가 리뷰 이야기와 짝지어져 있어, 한 문장을 "열 건을 넣어 봤습니다"와 "열 건 전부 보류했습니다"로 나눴습니다.
4단계. 한 번에 빌드
zsh 05_build.sh
문장별 TTS부터 결합까지 한 번에 돕니다. 주요 설정은 스크립트 머리에 모아 뒀습니다.
KEEP=0.13; SGAP=0.14; FPS=30
BGM_DB=-15 # 나레이션보다 약 15dB 아래
BGM_IN=13 # 배경음악을 까는 앞쪽 초
BGM_OUT=17 # 뒤쪽 초
XF_SLIDE=0.35 # 슬라이드가 바뀔 때 전환
XF_CAP=0.10 # 자막만 바뀔 때 전환
문장 사이 무음을 앞뒤로 다 잘라 냅니다. 이 한 줄이 이번에 가장 크게 바뀐 자리입니다.
ffmpeg -nostdin -y -i "s_$n.mp3" \
-af "silenceremove=start_periods=1:start_duration=0.10:start_threshold=-38dB:start_silence=$KEEP,\
silenceremove=stop_periods=-1:stop_duration=0.25:stop_threshold=-38dB:stop_silence=$KEEP" \
-c:a libmp3lame -q:a 2 "t_$n.mp3"
크로스페이드는 전환 시각을 손으로 계산해야 합니다. 앞 장의 끝과 뒷장의 시작이 겹치는 만큼 길이가 줄어드니, 각 장에 전환 길이의 절반씩을 덧대 총 길이를 지킵니다.
D = [0.0] + [XF_SLIDE if sl[i] != sl[i-1] else XF_CAP for i in range(1, n)] + [0.0]
seg = [d[i] + D[i]/2 + D[i+1]/2 for i in range(n)] # 각 장에 절반씩 덧댄다
acc, prev = seg[0], '0:v'
for i in range(1, n):
off = acc - D[i]
lines.append(f"[{prev}][{i}:v]xfade=transition=fade:duration={D[i]:.3f}:offset={off:.4f}[x{i}]")
acc, prev = acc + seg[i] - D[i], f'x{i}'
5단계. 자막이 본문을 가리는지 기계로 점검
발표에서 "지루하지 않게 만드는 것이 포인트"라고 했는데, 화면을 자주 바꾸다 보니 표와 흐름도가 길어져 자막이 본문 아래를 가리는 일이 생겼습니다. 눈으로 놓치기 쉬워 검사기를 하나 만들었습니다.
"""자막이 놓일 자리를 슬라이드 본문이 침범했는지 검사한다."""
W, H = 1920, 1080
BOTTOM = 76 # caption.py 의 값과 같아야 한다
MAX_CAP_H = 170 # 두 줄짜리 자막 상자 높이
LUMA = 100 # 이보다 밝으면 내용으로 본다
RATIO = 0.004 # 띠 화소의 0.4%를 넘으면 침범으로 본다
y0, y1 = H - BOTTOM - MAX_CAP_H, H - 12
for p in sorted(root.glob('slide_*.png')):
band = Image.open(p).convert('L').crop((0, y0, W, y1))
ink = sum(1 for v in band.getdata() if v > LUMA)
if ink > (y1 - y0) * W * RATIO:
bad.append((p.name, ink / ((y1 - y0) * W) * 100))
배경이 어두운 단색이라 글자와 카드와 figure는 확실히 밝습니다. 빌드 3-1단계에 넣어 매번 돌게 했습니다.
== 3-1. 자막 자리 침범 점검 ==
⚠ 자막 자리를 침범한 슬라이드 1장
slide_13.png 띠 안 내용 0.5%
→ .stage 아래 여백을 늘리거나 해당 슬라이드 내용을 줄인다
6단계. 배경음악을 분위기로 고르기
매번 같은 곡을 깔지 않으려고 테마를 만들어 두고 고릅니다.
zsh bgm_pick.sh list # 테마 목록
zsh bgm_pick.sh set calm-guitar nylon # 앞은 차분하게, 끝은 여운으로
zsh bgm_pick.sh audition voice.mp3 # 전 테마를 나레이션에 얹어 들어 볼 파일
테마
어울리는 자리
calm-guitar
차분한 해설. 기본값
bright
소개, 밝은 결과
summers-end
회고, 겪은 일을 돌아보는 글
nylon
섬세한 마무리, 여운
soft-waves
시스템·기술 설명
moving
진행감이 필요한 대목
drifting
문제 제기, 실패담
배경음악은 앞 13초와 마지막 17초에만 깝니다. 내내 틀면 나레이션과 따로 놉니다. 가운데 구간은 실측 -91dB로 사실상 무음입니다.
7단계. 후속 이야기는 이어 붙이기
발표한 사례가 이어지는 이야기라면 새 영상을 만들지 않고 기존 영상 뒤에 붙였습니다.
판
길이
내용
v1
2:34
원고를 자기 잣대로 검사한 이야기. 120개 중 27개가 근거를 못 찾았다
v2
3:58
여기에 한 주 뒤가 붙습니다. 다시 돌리니 158개 중 26개였습니다
v1이 "검수는 쓰는 동안 같이 가는 일"로 닫히는데 그다음 주에 실제로 어떻게 됐는지가 이어지니 한 편으로 완결됩니다. 따로 만들면 앞 편을 안 본 사람은 27이 무엇인지 모릅니다. 붙이기 전 판은 video2/backup_v1/에 남겨 두었습니다.
쓴 프롬프트
작업은 대부분 스크립트가 하고, 판단이 필요한 자리에서만 프롬프트를 썼습니다. 전문입니다.
소재를 고를 때
내 블로그 글 목록과 사례 발표 자료를 훑어서 영상 대본으로 쓸 만한 것을 3편 골라줘.
기준은 세 가지다. 훅이 첫 문단에 서 있는가, 측정한 숫자가 들어 있는가, 문장으로 이어져 있는가.
고른 이유와 예상 길이를 함께 적고, 파일 내용을 그대로 붙여넣지는 마.
슬라이드를 나눌 때
02_나레이션.txt를 문장 단위로 자르고, 각 문장을 어떤 슬라이드에 붙일지 08_timeline.txt를 만들어줘.
규칙:
- 문단이 아니라 문장 단위로 잇는다
- figure 슬라이드는 그 그림이 뒷받침하는 문장에 붙인다
- 붙일 문장이 없으면 나레이션 문장을 둘로 나누자고 제안한다
- 한 슬라이드가 15초를 넘으면 쪼갤 자리를 알려준다
완성본을 검수할 때
final.mp4에서 5초, 40초, 95초, 150초, 205초 프레임을 뽑아 하나씩 열어보고 아래를 판정해줘.
- 자막과 화면이 같은 내용을 말하는가
- 슬라이드 번호의 전체 장수가 맞는가
- 낱말 가운데서 줄바꿈이 갈리지 않았는가
"괜찮다"가 아니라 "몇 초 지점에서 무엇이 어긋났다"로 답할 것.
만든 결과
폴더
길이
대본 출처
슬라이드
문장
자막
3주차/video1/
3분 24초
결과물검수 1주차 사례 발표자료
24장
46개
46줄
3주차/video2/
3분 58초
결과물검수 2주차 사례. 발표 준비 중 쓴 블로그 글
29장
57개
57줄
두 소재는 같은 자리에서 발표한 것입니다. 결과물검수 스터디는 N주차 과제 사례를 N+1주차 세션에서 발표하는데, 3주차 세션에서 2주차 사례를 주된 사례로 발표하면서 차례가 밀린 1주차 사례를 함께 발표했습니다.
video2는 리모션 판도 함께 냈습니다. 2주차 과제 B안이 클로드 리모션이었는데, 같은 슬라이드 정의를 React 컴포넌트로 옮기니 숫자가 0에서 세어 올라가고 표가 줄마다 차례로 나타납니다.
cd remotion && npx remotion render Main out/verivar_remotion.mp4
리모션이 더 쉽다는 스터디장의 말이 맞았습니다. 크로스페이드 하나를 ffmpeg에서 넣으려면 전환 56개의 시작 시각을 손으로 계산해야 하는데, 리모션에서는 Sequence에 프레임 수만 주면 끝납니다.
결과와 배운 점
측정 결과
항목
1주차
2주차
3주차
만든 영상
0편
0편
2편
길이
없음
없음
3분 24초 + 3분 58초
대본
없음
없음
있음 (사례 발표 자료)
든 비용
없음
없음
0원
종량제 도구 호출
없음
없음
0회
배운 점
사례 발표 자료가 가장 가까운 영상 대본입니다. 훅과 뼈대와 숫자와 그림이 이미 한 벌로 묶여 있고, 무엇보다 남에게 설명해 본 자료라 순서가 검증돼 있습니다. 새 소재를 찾을 것이 아니라 이미 발표한 것을 돌아보는 편이 빨랐습니다.
발표 자료를 만들 때 산문으로도 한 번 써 두면 이득이 큽니다. 슬라이드는 명사로 끝나고 표와 불릿이라 소리 내어 읽으면 어색합니다. video2는 발표 준비 중에 쓴 블로그 글이 있어 손댈 것이 적었고, video1은 발표 자료를 46문장으로 다시 이어야 했습니다. 발표 준비의 부산물이 다음 결과물의 재료가 됩니다.
재료와 도구가 있어도 대본이 없으면 안 만들어집니다. 1주차 결론은 재료 부족이었고 2주차에 재료를 찾았고 3주차에 도구가 30개 생겼는데도 0편이었습니다. 세 주 동안 재료와 도구만 보고 있었습니다.
설치는 진행이 아닙니다. 스킬 30개가 한꺼번에 생겼을 때 목록만 보면 파이프라인이 완성된 것 같았는데 호출은 0회였습니다. 결국 완성한 두 편은 그 30개를 하나도 쓰지 않고 ffmpeg와 TTS만으로 만들었습니다.
타이밍은 문장 단위로 잡아야 어긋나지 않습니다. 문단을 균등분할해 배분하면 문단 안에서 조금씩 밀려 슬라이드가 멘트보다 먼저 뜹니다. 문장별로 TTS를 만들고 그 길이를 그대로 화면 길이로 쓰니 어긋남이 사라졌습니다.
하네스는 두 번째 편에서 자랍니다. 첫 편은 그 소재에 맞춰 만들다 보니 재사용할 부분과 그 프로젝트 전용이 섞여 있었습니다. 두 번째 편을 만들자 로고 파일 이름이 하드코딩된 자리, 자막이 본문을 가리는 자리, 앞머리 무음이 남는 자리가 차례로 드러났습니다.
후속 이야기는 새 영상보다 이어 붙이는 편이 낫습니다. 따로 만들면 앞을 안 본 사람은 27이 무엇인지 모릅니다.
나만의 꿀팁
팁
내용
발표 자료를 쓸 때 블로그도
영상 대본이 공짜로 생깁니다
TTS 원고는 숫 자를 한글로
개수는 고유어, 퍼센트와 백 단위는 한자어. 자막에서는 숫자로 되돌립니다
자막 자리를 미리 비워 두기
화면 아래 250px을 본문이 못 쓰게 막으면 겹침이 구조적으로 사라집니다
눈으로 볼 것을 기계에 맡기기
겹침 점검 20줄이 매번 24장, 29장을 검사합니다
배경음악은 앞뒤에만
내내 틀면 나레이션과 따로 놉니다
과정 중에 어떤 시행착오를 겪었나요
zsh 배열은 1부터 셉니다. bash 문법인
${IMG[$((i-1))]}을 그대로 썼더니 이미지가 하나씩 밀려 나왔습니다. 72초 지점 프레임을 뽑아 보고서야 알았습니다.wc -l은 끝 개행이 없으면 하나 적게 셉니다. 문장 33개인데 32개까지만 처리하고 멈췄습니다.grep -c ""로 바꿔 해결했는데, 같은 실수를 다른 파일에서 또 해서 마지막 프레임이 빠진 채 렌더가 실패했습니다.ffmpeg가 표준입력을 삼킵니다.
while read안에서 부르니 슬라이드 절반이 건너뛰어졌습니다.-nostdin을 붙여야 합니다.zsh가
$VAR[a]를 배열 첨자로 읽습니다. 필터 인자가 통째로 깨졌습니다.${VAR}[a]로 감싸야 합니다.adelay로 민 오디오를-t가 잘라 냅니다. 마무리 음악이 통째로 사라졌습니다. 지연은 합치는 단계에서 줘야 합니다.폴더마다 빌드 스크립트가 따로 놀았습니다. 첫 편 폴더의 스크립트는 앞머리 무음 제거가 들어가기 전 판이라, 후속 절을 붙여 빌드하니 묵음이 51곳 나왔습니다. 고친 것을 하네스에만 넣고 기존 작업 폴더에는 반영하지 않은 탓입니다.
슬라이드 장수가 바뀌면 전부 다시 찍어야 합니다. 끝쪽 네 장만 다시 캡처했더니 앞쪽에 옛 전체 장수가 남아 "10 / 18"로 표시됐습니다.
브라우저는 한국어를 글자 단위로 끊습니다. 자막이 "쓰는 동 / 안"처럼 낱말 가운데서 갈렸습니다.
word-break: keep-all로 막았습니다.
도움이 필요한 부분이 있나요
사람이나 캐릭터가 나오는 화면은 무료 대체재를 못 찾았습니다. HeyGen과 생성 영상 API는 종량제입니다. 앞뒤 15초만 붙이는 식으로 쓰신 분이 있으면 비용이 얼마나 나왔는지 듣고 싶습니다.
감정이 실린 더빙도 남은 자리입니다. Edge TTS는 평탄합니다. 스터디장이 추천한 구글 AI 스튜디오는 무료로 감정 표현이 어느 정도 된다고 하니 다음에 비교해 보려고 합니다.
앞으로의 계획
다음 사례 자료도 영상으로 만들겠습니다. 사례 자료 블로그 글을 함께 써 두면 대본이 준비된 상태로 시작합니다.
다음 편은 하네스를 고치지 않고 만들어 보겠습니다. 두 편에서 드러난 것을 전부 되돌려 넣었으니, 세 번째 편에서 또 고칠 자리가 나오는지가 하네스가 굳었는지를 재는 잣대가 됩니다.
실제 화면 녹화로 슬라이드 일부를 교체하겠습니다. 검사기가 도는 터미널과 대시보드입니다.
리모션 판에 그래프가 그려지는 애니메이션을 넣겠습니다. 막대가 0에서 자라 1.00에 닿는 화면입니다.
도움 받은 글 (옵션)
23기 영상 파이프라인 3주차 강의 (스터디장): 더빙 도구 세 개 비교와 구글 AI 스튜디오 추천, 타입캐스트의 감정 표현이 유료라는 점, 기획은 챗GPT·이미지는 그록으로 나눠 쓰는 이유
23기 영상 파이프라인 3주차 발표: 영상 전체에 한 줄이 아니라 컷 단위로 초·폰트·효과까지 적는 프롬프트, 나레이션 길이를 먼저 뽑아 블루프린트를 만드는 흐름, 자료가 많으니 폴더 정리로 마무리한다는 순서
23기 영상 파이프라인 2주차 강의 (스터디장): 클로드 리모션 실습 절차와 "리모션 이 숙제가 더 쉽다"는 말, 지루하지 않게 만드는 것이 포인트라는 기준
23기 AI 결과물 검수 1·2주차 사례 발표 (본인): 이번 두 편의 대본이 여기서 나왔다