릴스도 쇼츠도 네이버 클립도 결국 같은 세로 영상 — 편집기 없이 찍어내기
릴스·쇼츠·네이버 클립은 모두 1080×1920 세로 영상이다. 편집기로 매번 만드는 대신 컷시트 한 장을 ffmpeg filter_complex로 바꾸는 렌더 엔진을 만들었다. 탭 구분·stdin·zoompan에서 걸린 함정과 해결 과정.
🗺 시리즈: 네이버 클립 자동 제작 — 지금은 ① 컷시트 렌더 엔진
① 컷시트 렌더 엔진 (현재 글) · ② 한 글자씩 찍히는 타이핑 자막 · ③ 레퍼런스에서 내 나레이션 목소리 찾기 · ④ 컷 길이를 말에 맞추기
편집기 쓰는 법을 다루는 글이 아니다. 텍스트 한 장을 넣으면 영상이 나오게 만든 이야기다. 프리미어나 캡컷을 찾아 들어오셨다면 여기서 나가시는 게 낫다.
네 플랫폼 모두 1080×1920 세로라 만드는 과정은 같다. 다만 이 글에서 실제로 올려본 건 네이버 클립이고, 자막 안전 영역은 앱마다 UI가 달라 그 값만 각자 맞춰야 한다.
먼저 결과물부터. 아래 글에서 설명하는 컷시트로 렌더해 실제로 발행한 클립이다. 누르면 네이버 클립이 새 창으로 열린다.
속초 월드킹 · 45.9초 · 22컷 — 표지는 영상 0프레임을 그대로 뽑은 것이라 시작 화면과 정확히 같다
문제
네이버 블로그 쪽은 이미 손이 많이 덜어진 상태였다. 글을 정리해두면 Playwright가 스마트에디터에 옮겨 임시저장까지 해두고, 나는 확인하고 발행만 누른다(네이버 블로그 발행 자동화).
그런데 영상은 여전히 처음부터 끝까지 손으로 만들고 있었다. 한 편 만들 때마다 같은 일을 반복했다.
1080×1920으로 맞추고, 가로로 찍힌 영상은 세로로 잘라내고, 자막을 넣고, 그 자막이 앱 UI에 가리지 않는 높이인지 확인하고, 컷 순서를 바꾸고, 다시 렌더한다. 컷 하나를 고치면 그 뒤가 전부 밀린다.
더 큰 문제는 따로 있었다. “이 자막이 이 그림과 맞나”를 확인할 방법이 없었다. 22컷짜리 영상을 처음부터 끝까지 재생하면서 눈으로 대조해야 했고, 한 컷을 고치면 또 처음부터 봐야 했다.
원인
편집기는 한 번 만들기에 최적화되어 있다. 타임라인에 클립을 끌어다 놓는 인터페이스는 첫 편집에는 빠르지만, “3번 컷을 0.4초 늘리고 7번 자막 문구를 바꾼다” 같은 반복 수정에는 매번 같은 손동작을 요구한다.
반복 생산에 필요한 건 선언형 입력이다. 무엇을 어떤 순서로 몇 초씩 보여줄지를 텍스트로 적어두고, 그걸 영상으로 바꾸는 것. 그러면 수정은 텍스트 한 줄을 고치는 일이 되고, 무엇보다 컷시트 자체가 검수 대상이 된다.
해결 과정
컷시트 — 무엇을 입력으로 삼을 것인가
한 컷을 설명하는 데 필요한 최소 정보를 여덟 칸으로 잡았다.
1
type | source | start | dur | badge | main | sub | opts
| 칸 | 뜻 |
|---|---|
type | 컷 종류 (아래 참고) |
source | 파일명 |
start | 영상의 시작 초. 사진은 - |
dur | 이 컷의 길이(초) |
badge | 화면 상단 현수막 문구 |
main | 본자막 |
sub | 괄호 부연 |
opts | crop=W:H:X:Y sticker=문구@각도 at=초 pos=mid 등 |
type은 여섯 가지다.
vid— 영상. 세로가 아니면 배경을 블러로 깔고 가운데 얹는다pic— 사진. 천천히 확대되는 효과가 붙는다fill— 잘라서 화면을 꽉 채운다.crop좌표가 필요하다dual— 한 컷 안에서 자막이 한 번 바뀐다spec— 노란 박스 자막. 스펙을 빠르게 훑을 때loop— 마지막에 첫 장면으로 되돌아가는 컷
실제로 쓴 컷시트는 이렇게 생겼다.
1
2
3
4
5
# ── 훅: 결론부터 ──
vid | IMG_0969.MOV | 1.8 | 2.2 | 강원 속초, 비 오는 날 | 비 오면 여기로 가세요 | |
# ── 되감기: 전제 ──
fill | IMG_0938.MOV | 0.5 | 2.0 | | 토요일에 비가 왔고 | | crop=1080:1920:180:0
fill | IMG_0939.MOV | 3.2 | 2.5 | | 갈 데 찾다 들어온 월드킹 | | crop=1080:1920:0:0
주석(#)이 들어간 게 의외로 컸다. 컷시트를 읽으면 영상의 구성이 보인다. 훅이 어디고 마무리가 어딘지가 텍스트에 남으니, “여기서 흐름이 끊긴다”는 판단을 영상을 재생하지 않고도 할 수 있다.
한 컷이 필터그래프가 되기까지
컷 하나마다 독립된 mp4 세그먼트를 만들고 마지막에 이어 붙인다. 컷별 인코딩 설정이 같으므로 -c copy로 무손실 연결이 된다.
세로 화면을 채우는 방식은 두 가지다. fill은 원본을 잘라서 꽉 채우고, 그 외에는 배경에 블러 처리한 확대본을 깔고 원본을 가운데 얹는다.
1
2
3
COMPOSE="[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,gblur=sigma=30,eq=brightness=-0.06[bg];\
[0:v]scale=1080:1920:force_original_aspect_ratio=decrease[fg];\
[bg][fg]overlay=(W-w)/2:(H-h)/2,fps=30,format=yuv420p"
자막은 그 뒤에 drawtext로 얹는다. 좌표는 고정값으로 박아뒀다.
1
2
3
4
5
6
7
8
9
# 현수막 배지 — 전폭 띠 + 큰 글씨
drawbox=x=0:y=360:w=1080:h=124:color=black@0.62:t=fill
drawtext=...:fontsize=66:x=(w-text_w)/2:y=386
# 본자막
drawtext=...:fontsize=62:borderw=6:bordercolor=black@0.8:x=(w-text_w)/2:y=1200
# 괄호 부연
drawtext=...:fontsize=44:x=(w-text_w)/2:y=1298
이 좌표를 고정한 이유는 안전 영역 때문이다. 세로 숏폼은 앱 UI가 위아래를 가린다. 상단 15%(y < 288)와 하단 25%(y > 1440)에 자막을 두면 프로필·좋아요·설명문에 묻힌다.
자막 좌표를 고정값으로 박아둔 이유. 이 15%·25%는 공식 문서 값이 아니라 앱에서 실제로 가려지는 범위를 보고 잡은 운영 기준이다.
막힌 곳 1 — 탭으로 칸을 나눴더니 빈 칸이 사라졌다
처음엔 칸 구분을 탭으로 했다. 보기 좋게 정렬되니까. 그런데 자막이 엉뚱한 칸으로 들어갔다.
1
while IFS=$'\t' read -r TYPE SRC START DUR BADGE MAIN SUB OPTS; do
POSIX 셸에서 탭은 IFS 공백 문자다. 공백 문자는 연속되면 하나로 합쳐지고 앞뒤의 것은 무시된다. badge가 비어 있으면 그 칸이 아예 없는 것처럼 처리돼서 main이 badge 자리로 밀려 올라갔다.
파이프로 바꾸니 해결됐다. 파이프는 IFS 공백이 아니라 일반 구분자라 빈 칸이 빈 칸으로 남는다.
1
2
while IFS='|' read -r TYPE SRC START DUR BADGE MAIN SUB OPTS; do
TYPE=$(trim "$TYPE"); SRC=$(trim "$SRC") # 대신 정렬용 공백은 직접 떼어낸다
보기 좋음과 파싱 안정성을 둘 다 가져가는 대가로 trim 함수가 하나 늘었다.
막힌 곳 2 — 컷시트 줄이 통째로 건너뛰어졌다
22줄짜리 컷시트를 넣었는데 12컷만 렌더됐다. 줄이 규칙 없이 빠졌다.
범인은 ffmpeg이었다. while read 루프 안에서 ffmpeg을 호출하면 ffmpeg이 표준입력을 읽어버린다. 루프가 읽어야 할 컷시트를 ffmpeg이 먼저 먹은 것이다.
1
FF="ffmpeg -y -v error -nostdin"
-nostdin 하나로 끝났다. 셸 루프 안에서 ffmpeg을 돌린다면 조건 반사로 붙여야 하는 옵션이다.
막힌 곳 3 — 2.4초짜리 사진 컷이 수십 초가 됐다
사진에 천천히 확대되는 효과(Ken Burns)를 주려고 zoompan을 썼다.
1
ffmpeg -loop 1 -i photo.jpg -filter_complex "...,zoompan=z='min(zoom+0.0007,1.14)':d=72:..." out.mp4
결과가 수십 초짜리로 나왔다. zoompan의 d는 “입력 프레임 하나당 출력할 프레임 수”다. -loop 1이 입력 프레임을 무한히 공급하니, 프레임마다 72프레임씩 찍어내며 끝나지 않았다.
입력이 아니라 출력 쪽에서 끊었다.
1
2
D=$(awk "BEGIN{printf \"%d\", $DUR*30}")
ffmpeg -loop 1 -i photo.jpg -filter_complex "..." -frames:v "$D" out.mp4
그 밖에 걸린 것들
- 기울인 스티커에 검은 사각형이 생김 —
rotate가 알파 채널 없이 회전시켜 여백이 검게 찼다.format=rgba를pad·rotate앞에 넣어야 한다. - 이모지가 안 그려짐 —
drawtext가 컬러 이모지 폰트를 못 읽는다(Monocromatic (1bpp) fonts are not supported). 브라우저로 렌더해 투명 PNG로 만들어overlay로 얹었다. - 검수시트 생성 실패 — jpg로 뽑을 때
Non full-range YUV is not supported가 났다. mjpeg 인코더는 풀레인지를 요구한다.-pix_fmt yuvj420p를 붙여 해결. - 인자 경로가 깨짐 — 스크립트가 작업 폴더로
cd한 뒤 상대경로 인자가 무효가 됐다.cd전에 절대경로로 변환해 둔다.
검수시트 — 렌더보다 이게 더 중요했다
렌더가 끝나면 컷마다 중앙 프레임을 뽑아 한 장의 격자로 붙인다.
1
2
3
4
5
6
7
8
T=0; i=0
while IFS='|' read -r TYPE SRC START DUR REST; do
i=$((i+1))
MID=$(awk "BEGIN{printf \"%.2f\", $T + $DUR/2}")
ffmpeg -ss "$MID" -i "$OUT" -frames:v 1 -vf "scale=260:-1" -pix_fmt yuvj420p "chk/$(printf %03d $i).jpg"
T=$(awk "BEGIN{printf \"%.3f\", $T + $DUR}")
done < "$SHEET"
ffmpeg -pattern_type glob -i "chk/*.jpg" -vf "tile=5x5" -frames:v 1 검수시트.jpg
이게 이 엔진에서 가장 값어치 있는 부분이었다. 22컷을 한눈에 놓고 자막과 그림을 대조할 수 있다. 영상을 재생할 필요가 없다.
45.9초 22컷을 한 장으로. 자막과 그림이 어긋난 컷이 여기서 바로 드러난다.
실제로 이걸로 잡은 오류가 여럿이다. 자막은 공주 테마존까지 있어요인데 화면은 경찰 테마존이던 컷, 자막 없이 그림만 나가 소리까지 비던 컷, 안내판 글자 위에 자막이 겹친 컷 — 전부 격자에서 바로 보였다.
🧭 기획자·사업자라면 반복 산출물의 품질은 사람의 숙련이 아니라 입력 포맷이 결정한다. 편집기 숙련은 사람에게 남고 그 사람이 떠나면 사라지지만, 컷시트 포맷은 조직에 남는다. 그리고 검수 지점이 앞으로 당겨진다. 완성 영상을 보고 고치면 재렌더 비용이 들지만, 컷시트 텍스트를 리뷰하면 만들기 전에 걸러진다. 외주·협업 구조라면 “영상을 검수한다”를 “컷시트를 승인한다”로 바꾸는 것만으로 왕복 횟수가 줄어든다. 그리고 자동화의 한계선은 확인하고 그어야 한다. 이 글을 처음 쓸 때 나는 “클립 업로드는 모바일 앱 전용이라 파일 전달까지가 한계”라고 적었다. 틀렸다. PC 웹 업로더(
clipcreators.naver.com/web/upload)가 있었고, 거기서 설명·커버·카테고리·블로그 링크까지 브라우저 자동화로 채워 발행까지 했다. 앱만 써봤다는 이유로 “여기부터는 사람 손”이라고 선을 그었던 것이다. 한계선을 잘못 그으면 될 일을 안 하게 된다. 자동화 범위를 정할 때 “여기는 안 된다”는 판단이야말로 근거를 대야 하는 대목이다 — 되는 쪽은 해보면 바로 드러나지만, 안 된다고 접은 쪽은 아무도 다시 안 열어본다.
사용한 기술
- ffmpeg
filter_complex— 컷 하나가 곧 필터 체인 하나. 자막·크롭·블러 배경을 문자열로 조립해 넘긴다 concatdemuxer — 컷별 세그먼트를-c copy로 무손실 연결. 인코딩 설정이 같아야 한다drawtext+textfile— 한글 자막은text=대신textfile=로 넘긴다. 따옴표·콜론 이스케이프 지옥을 피한다zoompan— 사진에 천천히 확대되는 효과.d는 입력 프레임당 출력 프레임 수tile— 검수시트 격자 생성- POSIX 셸 — Windows PowerShell 포팅을 염두에 두고 bash 전용 문법을 최소화
정리
- 편집기 대신 컷시트를 입력으로 삼으면 수정이 텍스트 한 줄 고치기가 되고, 컷시트 자체가 리뷰 대상이 된다.
- 셸 루프에서 ffmpeg을 돌릴 땐
-nostdin, 칸 구분은 탭이 아니라 파이프. 둘 다 증상이 “일부 줄이 사라진다”로 똑같아 헷갈린다. - 가장 값어치 있는 산출물은 영상이 아니라 검수시트였다. 22컷을 한 장에 놓고 자막과 그림을 대조하는 것 — 이게 없으면 결국 영상을 처음부터 다시 본다.
여기까지가 컷시트 한 장이 영상이 되는 과정이다. 그래서 뭐가 나왔는지 다시 한 번.
이 글의 컷시트로 렌더해 발행한 결과물
다음 편에서는 이 엔진에 자막이 한 글자씩 찍히는 타이핑 효과를 넣는다. 글자가 늘어날 때마다 문장이 좌우로 출렁이는 문제를 외부 라이브러리 없이 푸는 이야기다.
