제품 사용 가이드를 매번 손으로 만들기 싫어서 — 영상 넣으면 문서가 나오는 파이프라인을 만들었다

제품을 하나 만들면 따라오는 잡일이 있다. 사용 가이드 문서. 화면을 녹화해 설명하는 건 30분이면 되는데, 그걸 남이 볼 수 있는 문서로 옮기는 건 반나절이 걸린다. 스크린샷을 일일이 캡처하고, 무슨 말을 했는지 받아적고, 순서를 다시 짜고. 게다가 우리는 사내에서 여러 SaaS 제품을 굴린다. 제품마다 이걸 반복할 생각을 하니 답이 안 나왔다.

그래서 영상을 넣으면 가이드 문서가 나오는 파이프라인을 만들었다. 이름은 guide-maker. 화면녹화 mp4(웹 클릭 + 한국어 음성설명) 하나를 입력하면, 스텝별 사용 가이드를 MD와 PDF로 뽑는다. 특정 제품 전용이 아니라 여러 제품에 재사용하는 범용 도구로 짰다.

4단계 파이프라인

흐름은 단순하다.

① input   원본 mp4를 projects/<제품>/input/ 에 넣는다
② process  ffmpeg로 프레임 추출 + faster-whisper로 한국어 자막 생성
③ 재구성   프레임 + 자막을 모듈 단위 사용 가이드(MD)로 재구성
④ PDF      pandoc + wkhtmltopdf + 나눔폰트로 MD → PDF

guide-maker 4단계 파이프라인 — input·process는 스크립트 자동, 재구성은 사람 판단, PDF 변환

스크립트가 자동으로 하는 건 ②까지다. ③ 재구성은 자동화하지 않고 사람이 판단해서 만든다. 어디까지 자동화하고 어디부터 사람 몫으로 남길지 — 그 경계를 어디에 그었는지가 이 도구의 핵심이라 뒤에서 따로 적는다.

① 프레임을 “고정 간격”으로 뽑지 않은 이유

처음엔 30초마다 한 장씩 뽑았다. 문제는 화면 전환이 잦은 구간에선 중요한 장면을 놓치고, 정지 화면이 긴 구간에선 똑같은 그림만 쌓인다는 거였다. 그래서 장면 전환 감지와 간격 fallback을 하나의 필터로 결합했다.

ffmpeg -i "$VIDEO" \
  -vf "select='gt(scene,0.3)+not(mod(t,30))',showinfo" \
  -vsync vfr -q:v 2 "frame_%05d.jpg"

gt(scene,0.3)은 화면이 30% 이상 바뀌면 잡고, not(mod(t,30))은 그런 전환이 없어도 30초마다 한 장은 확보한다. 둘을 더해서, 전환이 잦으면 촘촘하게·없으면 최소한으로 뽑힌다.

여기서 한 가지 함정이 있었다. 이렇게 뽑은 프레임은 frame_00001.jpg처럼 순번으로 저장돼서, 자막(타임스탬프)과 짝지을 수가 없다. 그래서 showinfo 로그를 stderr로 받아 각 프레임의 원본 재생시각(pts_time)을 추출하고, 파일명을 _t00-12-34.jpg처럼 타임스탬프로 리네임했다. 이제 “이 스크린샷 시점에 무슨 말을 했는지”를 자막과 매칭할 수 있다.

실제로 102분짜리 제품 데모 녹화 하나에서 이 방식으로 382장이 나왔다.

② 자막은 그냥 받아적으면 안 됐다

STT는 faster-whisper large-v3(int8)를 썼다. GPU가 있으면 쓰고 없으면 CPU로 자동 폴백한다. 그런데 회의 녹화 특성상 두 가지를 손봐야 했다.

  • 무음 구간: vad_filter=True로 발화가 없는 구간을 잘라냈다. 안 그러면 조용한 데서 헛것을 받아적는다.
  • 반복 아티팩트: 긴 침묵 뒤엔 whisper가 직전 문장을 계속 되풀이하는 버릇이 있다. condition_on_previous_text=False로 이 루프를 눌렀다.

여기에 하나 더. 받아적은 자막을 통짜 텍스트로 두면 뒤에서 재구성할 때 경계를 잡기 나쁘다. 그래서 발화 사이 정적이 1초 이상이면 문단을 나눴다. 말이 끊기는 지점이 대체로 화제가 바뀌는 지점이라, 이걸로 대화의 “턴”을 근사했다. 문단이 나뉘어 있으니 ③단계에서 스텝 경계를 잡기가 훨씬 쉬워졌다.

③ 어디까지 자동화하고, 어디부터 사람 몫으로 둘까

프레임 382장 + 자막이 준비되면, 이걸 사람이 볼 문서로 바꾸는 게 제일 어려운 일이다. 보통은 여기서 LLM API를 호출하는 코드를 파이프라인에 박아 이 단계까지 자동화한다. 나는 그러지 않고, 이 단계를 사람이 판단하는 수동 단계로 남겼다.

이유는 결과물의 성격 때문이다. 원본 녹화는 발표 순서대로 흘러가지만, 좋은 사용 가이드는 기능(모듈) 단위로 재구성돼야 한다. 마이크 점검·잡담은 빼고, 흩어져 나온 같은 기능 얘기는 한군데로 모으고, 382장 중 정말 필요한 스크린샷 7~8장만 골라 꽂는 판단이 필요하다. 이건 프롬프트 한 번으로 끝나는 일이 아니라 보면서 고치는 작업이다.

그래서 이 단계는 AI 코딩 도구를 옆에 두고 대화형으로 만든다. 초안을 보고 “이 부분은 순서를 바꾸자”, “이 스크린샷 말고 저거”를 그 자리에서 반복한다. 트레이드오프는 분명하다 — 완전 무인 자동화는 포기했다. 사람이 한 번은 붙어야 한다. 대신 얻은 건 재구성 품질을 그 자리에서 잡는 것, 그리고 이 판단을 코드로 박았을 때의 불안정성(제품마다 화면·용어가 달라 한 방에 안 나온다)을 피한 것이다. 반나절짜리 잡일을 없애는 게 목적이었지, 사람을 완전히 빼는 게 목적이 아니었으므로 이 경계는 맞았다.

범용으로 짠 방법과 v1 → v2

제품이 늘어도 스크립트를 복사하지 않도록, scripts/와 파이썬 venv는 공용으로 두고 제품별로는 작업 폴더만 복제하게 했다.

mkdir -p ~/guide-maker/projects/<제품>/{input,work/{frames,transcript},output}

첫 제품은 v1으로 한 번 뽑고, 아쉬운 점을 반영해 v2를 다시 뽑았다. v1은 스크린샷을 대충 시간순으로 넣었는데, v2에선 “발표 순서가 아니라 모듈 단위로 재구성”이라는 원칙을 세우고 이미지도 8장 → 7장으로 오히려 줄였다. 문서 품질은 스크린샷 개수가 아니라 구조에서 나온다는 걸 두 번 만들고 알았다.

한계와 다음 계획

  • ③단계를 사람이 판단하므로, “폴더에 영상만 던지면 문서가 나오는” 완전 무인은 아직 아니다. 반복이 많은 제품이면 재구성 규칙을 스크립트로 고정해 무인 쪽으로 밀어볼 생각이다.
  • 자막 정확도는 발음·전문용어에서 여전히 흔들린다. 제품별 용어 사전을 STT 후처리에 붙이는 걸 검토 중이다.
  • 지금은 한국어 녹화 기준이다. 영어 데모용 경로는 따로 있지만 아직 다듬지 않았다.

자주 묻는 질문

Q. 왜 재구성 단계를 코드로 자동화하지 않았나?
결과물이 “한 번에 뽑고 끝”이 아니라 보면서 고치는 재구성 작업이라서다. 제품마다 화면·용어가 달라 한 방에 안정적으로 나오지 않는다. 그래서 이 단계는 사람이 판단하는 수동 단계로 남겼다. 완전 무인 자동화를 포기한 대신, 재구성 품질을 그 자리에서 잡는 트레이드오프다.

Q. 프레임을 장면 전환으로만 뽑으면 안 되나?
정지 화면이 긴 구간에서 아무 프레임도 안 잡히는 문제가 생긴다. 그래서 장면 전환 감지에 “N초마다 최소 한 장” fallback을 더해 두 경우를 모두 커버했다.

Q. GPU가 없어도 되나?
된다. faster-whisper가 CPU(int8)로 폴백한다. 느릴 뿐 결과는 나온다. large-v3 모델은 첫 실행 때 약 3GB를 1회만 받는다.

Q. 자막을 문단으로 나눈 이유는?
통짜 텍스트보다 문단이 나뉘어 있어야 뒤 단계에서 스텝 경계를 잡기 쉽다. 발화 사이 1초 이상 정적을 기준으로 나눠 대화의 턴을 근사했다.

Leave a Comment