가사 원문이 있어도 자막 싱크가 맞지 않는 이유: ASR·정렬·SRT 검증 분리
원문과 실제 발화, 시간 정렬, SRT 파일 검증을 분리하는 자막 제작 흐름. Python 예제로 구조 오류를 잡고 플레이어 확인까지 검수 범위를 구분한다.
오디오 파형과 자막 타이밍의 관계를 표현한 생성형 개념 이미지다. 실제 편집기 화면이 아니다.
가사 원문을 가지고 있는데도 영상의 자막이 늦게 나오거나, 노래에서 들리지 않는 문장이 화면에 뜰 수 있다. 텍스트를 정확하게 준비하는 일과 그 텍스트가 들리는 시간을 찾는 일은 서로 다른 작업이기 때문이다.
해결 방법은 원문, 실제 발화 전사, 시간 정렬, 자막 파일 검증을 분리하는 것이다. ASR 결과를 그대로 정답으로 삼지도 않고, 원문을 타임라인에 억지로 끼워 넣지도 않는다.
1. 원문과 실제로 들리는 말은 다른 데이터다
음성 인식(ASR)은 오디오에서 말을 추정한다. 반면 강제 정렬(forced alignment)은 주어진 전사를 오디오의 어느 구간에 배치할지 찾는다. 정렬기에 정확하지 않은 전사를 넣으면, 그럴듯한 시간이 붙어도 내용이 맞는다는 보장은 없다. PyTorch의 공식 튜토리얼도 전사와 음성을 정렬하는 문제를 별도로 다룬다. 여기서는 개념만 참고하며, 해당 문서에 표시된 폐기 예정·제거 API를 신규 구현 코드로 권장하지 않는다. PyTorch 정렬 문서
작업 데이터는 다음처럼 나누는 편이 낫다.
- 원문: 작성자가 의도한 가사나 대본이다. 수정 이력을 보존한다.
- 확인 전사: 실제 오디오를 듣고 확인한 내용이다.
- 시간표: 확인 전사의 각 구간에 시작·끝 시간을 붙인 것이다.
- 출력 파일: 시간표를 SRT 등 배포 형식으로 변환한 결과다.
원문과 발음이 조금 다른 정도인지, 단어가 바뀐 것인지, 한 줄을 아예 부르지 않은 것인지 먼저 구분한다. 맞춤법 표기를 유지할 수 있는 발음 차이와, 누락된 문장을 새로 표시하는 것은 같은 문제가 아니다.
2. ASR은 초안이고, 정렬은 시간 추정이다
노래는 일반적인 발화와 다르게 음절을 길게 늘이고 반주와 보컬이 겹친다. 이 때문에 자동 전사를 검수 없이 확정하는 방식은 피하는 것이 좋다. 이는 모델의 노래 성능을 측정한 결과가 아니라, 입력 특성을 고려한 작업 설계 판단이다.
YouTube 역시 발음, 억양, 방언, 배경 소음 때문에 자동 자막이 잘못 표시될 수 있어 검토와 수정을 권한다. 한국어 지원 여부와 한국어 노래의 모든 발음을 정확히 처리한다는 주장은 별개다. 자동 자막 안내
실무에서는 먼저 ASR 초안과 원문을 비교하고, 차이가 큰 구간을 청취한다. 그 다음 확인한 전사로 정렬하거나 수동 타이밍을 보정한다. 반복되는 후렴은 텍스트가 같아도 별도 구간으로 관리한다.
특히 누락된 줄에 시간을 강제로 배정하지 않는다. 내용이 들리지 않는 구간은 정렬 문제가 아니라 전사와 편집 방침의 문제다. 의도한 가사 전체를 별도로 소개하더라도 실제 음성과 맞추는 CC 트랙과 혼동하지 않게 구분해야 한다.
3. 설명란, 자막 트랙, 영상에 박힌 글자는 다르다
설명란에 가사를 붙여 넣는 것만으로 재생 시점에 맞춘 CC가 되지는 않는다. 영상 프레임에 글자를 합성하는 방식도 사용자가 켜고 끄는 자막 트랙과 다르다.
YouTube Studio에서는 자막 파일 업로드 시 타이밍 포함 여부를 선택할 수 있다. 자동 동기화는 영상과 같은 언어의 전사가 필요하며, 처리에 시간이 걸릴 수 있다. 따라서 텍스트가 입력된 상태, 타이밍이 계산된 상태, 게시된 자막을 플레이어에서 볼 수 있는 상태를 나누어 확인한다. 자막 추가 방법
이미 시간을 확보했다면 타이밍을 포함한 SRT로 전달하는 편이 결과를 재현하고 검토하기 쉽다. 다만 SRT는 잘못된 전사나 잘못 잡은 시간을 고쳐 주는 도구가 아니다.
4. SRT 생성 전에 기계적으로 잡을 오류
YouTube의 기본 SRT 지원은 일반 UTF-8 텍스트를 전제로 하며 스타일 마크업은 인식하지 않는다. 큐 번호, 시작·끝 시간, 본문과 빈 줄 구분을 단순하게 유지한다. 지원 자막 파일 형식
다음 예제는 정수 밀리초로 작성한 시간표를 SRT로 바꾼다. 의존성 없이 실행되며, 시간 역전과 영상 범위 초과, 빈 본문을 거부한다. 큐 겹침 금지는 SRT 전체의 보편 규칙이 아니라 이 예제에서 선택한 단일 트랙 검수 정책이다.
생성되는 파일은 다음과 같다. 이 문장과 시간은 테스트용으로 만든 예시이며 실제 음성에서 얻은 결과가 아니다.
직접 실행해 확인한 범위
위 생성 로직을 로컬 Python에서 실행했다. 정상 입력의 파일 저장과 UTF-8 재읽기가 일치했고, 아래 잘못된 입력 8건은 모두 ValueError로 거부됐다.
- 시작보다 빠른 종료
- 전체 길이를 넘는 종료
- 음수 시작 시각
- 시작과 끝이 같은 구간
- 빈 본문
- 본문 중간의 빈 줄
- 앞 큐와 겹치는 구간
- 정수가 아닌 시간
이 테스트가 확인한 것은 시간표에서 SRT로 변환하는 로직이다. 실제 오디오의 인식률, 노래 발음, 자막의 청취 싱크나 YouTube 업로드 성공을 측정한 것은 아니다. 입력의 전체 길이 역시 실제 배포 영상에서 확인한 값을 사용해야 한다.
5. 최종 검수는 플레이어에서 끝난다
파일 검증을 통과한 뒤에도 다음 단계가 남는다.
- 실제 배포 영상과 같은 버전의 오디오를 기준으로 시간을 잡았는지 확인한다.
- 첫 발화, 반복 구간, 긴 간주 뒤 재진입, 마지막 발화를 재생하며 맞춘다.
- 빠진 단어와 과도하게 오래 남는 자막, 읽기 어려운 줄바꿈을 수정한다.
- 업로드 후 언어와 게시 상태를 확인한다.
- 시청자 플레이어에서 CC 선택과 실제 표시를 확인한다.
앞에 인트로를 추가했거나 오디오 속도를 바꿨다면 기존 시간표를 그대로 재사용하지 않는다. 일정한 오프셋으로 설명되는 변경인지, 편집 구간마다 다른 보정이 필요한지부터 판단한다.
결국 자막 자동화에서 중요한 것은 한 번에 모든 것을 해결하는 모델을 고르는 일이 아니다. 내용이 맞는가, 시간이 맞는가, 파일이 유효한가, 시청자가 볼 수 있는가를 서로 다른 검증 단계로 만드는 것이다.