AI 도구 목록

낱말 시각으로 SRT 자막을 만들다가 순서가 뒤집힌 날

2026년 10월 6일Importants Studio

유튜브에 자막 파일을 올릴 때마다 음성을 들으며 시작 시각과 끝 시각을 맞추는 작업이 남았는데, 음성 합성 결과에는 이미 낱말별 시각이 들어 있었으므로 이 값을 문장 단위로 묶으면 SRT를 만들 수 있겠다고 생각했습니다.

처음에는 자막 생성기가 잘 돌아가는 듯했습니다. 그런데 캐시 파일 이름을 바꾼 뒤 문장이 엉뚱한 순서로 나오기 시작했고, 이미 끝난 영상을 다시 합성한 뒤에는 음성과 영상 길이도 달라졌습니다. 자막은 생성됐습니다. 틀린 쪽은 시각입니다.

먼저 결론부터

  • 낱말 시각만 저장하지 않고, 각 낱말이 어느 문장에 속하는지도 함께 기록합니다.
  • 자막은 문장 단위로 묶되, 한 자막이 6초를 넘으면 나눕니다.
  • 한국어 줄바꿈에서는 조사 앞과 쉼표로 묶인 숫자를 끊지 않도록 별도 규칙을 둡니다.
  • 캐시 파일 이름으로 문장 순서를 알아내던 코드는 내용 해시를 도입하면서 조용히 틀렸습니다.
  • 음성과 영상 길이가 0.35초보다 많이 다르면 SRT 생성을 멈춥니다.

낱말 시각만으로는 문장 순서를 알 수 없었습니다

음성 합성 응답에는 낱말마다 시작 시각과 끝 시각이 따라옵니다. 처음에는 파일 이름을 정렬한 뒤 파일 안의 낱말 시각을 이어 붙여 자막을 만들었고, 캐시 파일이 순번 이름일 때는 이 방식이 맞았습니다.

문제는 이름 규칙을 바꾼 뒤입니다.

캐시 이름을 순번 대신 내용 해시로 바꾸자 파일 정렬 결과가 문장 순서와 관계없어졌는데, 자막 생성기는 파일 이름 순서를 원래 문장 순서라고 믿고 있었고 오류도 내지 않았습니다. 앞 문장 뒤에 다른 문장의 뒷부분이 붙은 SRT를 만들었습니다.

원인은 파일명.

해시는 순서가 없는 값입니다. 그래서 낱말 데이터에 문장 번호를 넣고, 자막 생성 단계에서는 파일 이름이 아니라 문장 번호로 다시 묶도록 바꿨습니다. 음성 파일을 찾는 용도와 자막 순서를 정하는 용도를 같은 값에 맡긴 것이 문제입니다.

이름을 바꾼 뒤에는 그 이름을 읽는 코드를 먼저 찾아야 합니다. 한쪽만 새 규칙으로 옮기면 다른 쪽은 틀린 결과를 내도 작업이 끝날 때까지 눈치채기 어렵습니다.

자막 묶음은 문장으로 시작했습니다

낱말 시각을 그대로 SRT 항목으로 만들면 화면이 너무 자주 바뀝니다. 그래서 같은 문장 번호를 가진 낱말을 먼저 모으고, 그렇게 모인 문장 하나가 자막 후보입니다.

다만 문장이 길면 화면에 오래 남았는데, 한 자막이 6초를 넘으면 읽는 속도를 따라가기 어려워서 그 지점에서 자막을 나누도록 했습니다. 문장 전체를 보존하는 것보다 화면에서 따라갈 수 있는 길이가 먼저입니다.

문장 중간을 나눌 때는 낱말 시각을 다시 계산하지 않았습니다. 앞 묶음의 마지막 낱말 시각과 다음 묶음의 첫 낱말 시각을 그대로 씁니다. 합성 응답이 이미 낱말 시각을 주고 있으므로, 자막 생성기가 새 시각을 추측할 이유는 없습니다.

짧은 문장은 그대로 남았습니다. 긴 문장만 갈라졌습니다.

한국어 줄바꿈은 공백 기준으로 자르면 틀렸습니다

문장을 자막 폭에 맞춰 줄바꿈할 때 처음에는 공백 위치만 봤습니다. 그러자 서울 / 에서처럼 조사 앞에서 끊기는 결과가 나왔고, 뜻은 이어지는데 화면에서는 앞말과 조사가 따로 떨어졌습니다.

그래서 끊어도 되는 자리와 끊으면 안 되는 자리를 목록으로 나눠, 조사 앞은 금지하고 문장 안에서 자연스럽게 숨을 고를 수 있는 자리를 우선으로 잡았습니다. 줄 길이만 맞추는 규칙으로 될 일이 아닙니다.

숫자도 같은 문제입니다. 1,000처럼 쉼표로 묶인 숫자를 공백 없는 문자열 일부로 잘못 판단하면 숫자가 두 줄로 갈라집니다. 자막에서는 숫자가 갈라지는 순간 읽는 흐름이 멈췄습니다.

문장 분할과 줄바꿈은 다른 단계로 두고, 문장은 시각을 기준으로 나누고 줄바꿈은 읽는 모양을 기준으로 처리했습니다. 둘을 한 번에 처리했을 때보다 어디서 문제가 생겼는지 찾기 쉬웠습니다.

폴더 하나만 옮긴 뒤 완성본을 다시 만들었습니다

캐시 이름 규칙을 바꾸면서 관련 폴더를 모두 옮겼다고 생각했지만, 실제로는 한 폴더만 새 규칙을 따르고 있었고 나머지 경로에서는 기존 캐시를 찾지 못했습니다.

그 결과 이미 끝난 영상이 다시 합성됐습니다. 700자를 날렸습니다.

더 큰 문제는 다시 만든 음성 길이가 이전 결과와 같지 않았다는 점입니다. 기존 음성은 59.93초였지만 새 음성은 58.28초였고, 영상은 기존 길이를 유지하고 있는데 음성만 짧아진 셈이라 이전 낱말 시각으로 만든 자막은 뒤로 갈수록 어긋났습니다.

이 경우 자막 파일은 문법적으로는 정상입니다. 재생도 됩니다. 하지만 화면 전체에서 시각이 조금씩 밀립니다. 없는 자막보다 더 찾기 어려운 오류.

길이가 다르면 자막을 만들지 않게 했습니다

재합성 뒤에는 음성 길이와 영상 길이를 먼저 비교하도록 바꿨습니다. 둘의 차이가 0.35초를 넘으면 SRT 생성 단계에서 멈춥니다.

처음에는 자막 생성만 다시 하면 된다고 생각했지만, 자막이 참조하는 것은 문장이 아니라 실제 음성의 시간축입니다. 음성이 바뀌면 낱말 시각도 함께 바뀌어야 합니다.

그래서 길이 검사가 들어갈 자리는 자막 생성 직전입니다. 캐시를 찾았는지, 음성이 다시 만들어졌는지, 영상이 이전 결과를 유지하는지는 그다음에 확인할 일입니다. 길이가 맞지 않는 상태에서는 자막을 만들지 않는 쪽이 낫습니다.

낱말 시각으로 SRT를 만드는 일은 시각 값을 줄마다 옮기는 작업이 아닙니다. 문장 순서를 어디서 보장하는지, 줄이 어디서 끊기는지, 음성이 이전 결과와 같은지까지 확인해야 자막이 화면과 정확히 맞습니다.