자막 파일을 열었더니 �� 나 뙥뙢 같은 글자가
가득합니다. 파일이 망가진 게 아니라, 읽는 방식을 잘못 고른 것입니다.
핵심. 글자는 파일에 "글자"로 저장되지 않습니다. 숫자(바이트)로 저장되고, 그 숫자를 어떤 표로 읽느냐에 따라 전혀 다른 글자가 나옵니다. 깨져 보인다는 건 다른 표로 읽고 있다는 뜻입니다. 파일은 멀쩡합니다.
윈도우에서 메모장으로 저장할 때 인코딩 목록에 ANSI 가 있습니다. 이게 함정입니다. ANSI 는 특정 인코딩의 이름이 아니라 "이 컴퓨터의 시스템 언어가 쓰는 코드 페이지" 라는 뜻이기 때문입니다.
| 윈도우 시스템 언어 | ANSI 의 실체 |
|---|---|
| 한국어 | CP949 (EUC-KR) |
| 일본어 | CP932 (Shift_JIS) |
| 중국어 간체 | CP936 (GBK) |
| 중국어 번체 | CP950 (Big5) |
| 영어·서유럽 | CP1252 |
| 러시아어 | CP1251 |
| 태국어 | CP874 |
그래서 한국에서 "ANSI 로 저장"한 자막은 실제로는 CP949 파일이고, 그 파일을 일본어 윈도우나 유럽산 TV 에 가져가면 깨집니다. 받는 쪽은 자기 나라 표로 읽기 때문입니다. 파일이 자기가 어느 표를 썼는지 적어 두지 않으므로 받는 쪽은 짐작할 수밖에 없습니다.
UTF-8 은 이 문제를 없애려고 만든 것입니다. 모든 나라의 글자를 한 표에 담았으므로 나라를 옮겨도 같은 글자가 나옵니다. 요즘 플레이어·TV·웹이 기대하는 것이 이것입니다.
깨진 글자의 생김새가 어느 표로 읽히고 있는지 알려 줍니다. 아래는 실제로 자주 보는 조합입니다.
| 이렇게 보이면 | 원본은 | 지금 읽는 표는 |
|---|---|---|
| 안녕하세요 | UTF-8 한국어 | CP1252 (서유럽) |
| ��� | CP949 한국어 | UTF-8 |
| 갾갻갿 같은 낯선 한글 | 중국어 GBK | CP949 |
| 뙥뙢 처럼 읽을 수 없는 한글 | 일본어 Shift_JIS | CP949 |
| привет | UTF-8 러시아어 | CP1252 |
규칙이 하나 있습니다. à  � 같은
서유럽 글자가 섞여 나오면 원본이 UTF-8 인 경우가 거의 확실합니다.
UTF-8 의 한글 한 글자는 3바이트인데, 그걸 1바이트씩 끊어 읽으면 저런 모양이 됩니다.
반대로 멀쩡해 보이는 한글인데 읽을 수 없는 조합(뙥뙢, 갾갻)이면
원본이 다른 나라의 2바이트 인코딩입니다. 중국어나 일본어 자막을 한국어 윈도우가
CP949 로 읽어서 그렇습니다.
여기가 까다로운 부분입니다. 코드 페이지들은 서로의 바이트를 거의 다 "읽을 수" 있습니다. 태국어 파일을 키릴로 읽어도 깨진 기호 없이 멀쩡한 키릴 문자가 나오고, 중국어 파일을 CP949 로 읽으면 멀쩡한 한글이 나옵니다. 그래서 "이 문자 체계가 맞는가"만으로는 절대 가려낼 수 없습니다.
갈리는 지점은 빈도입니다. 실제 한국어 글은 자주 쓰는 수백 개 음절이 본문의 대부분을 차지하지만, 잘못 읽힌 한글은 11,172개 음절 공간에 고르게 흩어집니다. 자막 변환기는 언어별 상용 글자 집합에 얼마나 걸리는지를 바이트 단위로 채점해 판별하고, 신뢰도가 낮으면 칩에 빨간 점을 띄웁니다.
그래도 한국어 CP949 와 중국어 GBK 처럼 원리적으로 애매한 경우가 남습니다. 이때 쓰라고 감지 우선 언어 설정이 있습니다. 애매하면 그쪽으로 저울을 기울입니다. 물론 파일마다 직접 지정하는 게 가장 확실합니다.
자막이면 확인이 쉽습니다. 목록에 SRT · 자막 342개 처럼 자막 개수가
뜹니다. 이 숫자가 0 으로 빨갛게 나오면 원본 인코딩을 잘못 고른 것입니다.
제대로 읽히면 숫자가 나옵니다.
UTF-8 로 둡니다. (BOM 없는 쪽 — 이유는 아래)미리보기에서는 멀쩡한데 플레이어에서만 깨지는 경우가 있습니다. 이건 다른 문제이고, 원인은 거의 항상 둘 중 하나입니다.
SMI 자막·HTML·XML·CSS 는 자기 인코딩을 파일 안에 적어 둡니다.
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=euc-kr">
바이트만 UTF-8 로 바꾸고 이 줄을 그대로 두면 파일이 거짓말을 하게 됩니다. 플레이어는 적힌 말을 믿고 euc-kr 로 읽기 때문에, 변환하고도 깨집니다. 메모장의 "UTF-8 로 저장"이 이 함정에 빠지는 대표적인 경우입니다 — 메모장은 저 줄을 건드리지 않습니다.
변환기의 문서 안의 charset 선언 옵션이 기본값 "고쳐 쓰기"로 이걸 같이 바꿔 줍니다.
cp949 ms949 ks_c_5601-1987 gb2312
shift-jis 같은 흔한 별칭도 알아봅니다.
BOM 은 파일 맨 앞에 붙는 EF BB BF 세 바이트입니다. SRT 는 첫 줄이
자막 번호 1 인데, 앞에 BOM 이 붙으면 일부 플레이어·TV 가 그걸 번호의
일부로 읽어 그 자막 묶음 전체를 건너뜁니다. 첫 자막 한 줄이 통째로 안 나오는
증상입니다.
그래서 대상 인코딩은 UTF-8 (BOM 포함) 이 아니라 그냥 UTF-8
을 고릅니다. WebVTT 는 규격이 BOM 을 허용하므로 괜찮습니다.
UTF-8 → CP949 처럼 좁은 표로 내보낼 때는 담을 수 없는 글자가 생깁니다. 이모지(🎬)나 중국어 간체(简体)를 CP949 로는 적을 수 없습니다. 변환기는 이런 글자를 세어 알려 주고, 처리 방식을 셋 중에 고르게 합니다.
| 방식 | 결과 | 쓸 때 |
|---|---|---|
| 물음표로 대체 | ? | 기본값. 뭔가 빠졌다는 게 눈에 보입니다. |
| 숫자 참조로 보존 | 가 | HTML·SMI 처럼 그 표기를 해석하는 포맷일 때. 글자가 살아남습니다. |
| 그냥 삭제 | (사라짐) | 장식용 기호를 털어낼 때. |
UTF-8 로 내보낼 때는 이 문제가 없습니다. UTF-8 은 모든 글자를 담으므로 손실이 0 입니다. 특별한 이유가 없다면 대상은 UTF-8 로 두세요.
파일 안에 charset 선언이 없는 SRT 라면 대체로 됩니다. 다만 두 가지를 조심해야 합니다. 윈도우 메모장은 버전에 따라 BOM 을 붙입니다(위의 첫 자막 문제). 그리고 SMI 는 선언을 안 고쳐 주므로 메모장으로는 제대로 안 됩니다. 무엇보다 파일이 여러 개면 하나씩 열고 저장해야 합니다.
미리보기를 띄워 두고 행의 셀렉트를 하나씩 바꿔 보세요. 자막 파일이면 자막 개수가 가장 빠른 신호입니다 — 0 에서 숫자로 바뀌는 순간이 정답입니다. 자막이 아닌 텍스트라면 미리보기 왼쪽 칸의 글자가 말이 되는지로 판단합니다.
브라우저 표준(WHATWG) 동작입니다. 웹 표준은 ISO-8859-1 을 Windows-1252 의 별칭으로 정의하고 있습니다. 둘은 0x80–0x9F 구간만 다른데, 실제 문서에서 그 구간은 거의 Windows-1252 쪽 의미로 쓰이므로 결과적으로 더 맞는 경우가 많습니다.
읽기 32종, 쓰기 31종입니다. CP949·Shift_JIS·EUC-JP·GBK·GB18030·Big5 같은 동아시아 인코딩, Windows-125x 계열, KOI8·IBM866 같은 키릴, 그리스어·히브리어·아랍어·태국어, 그리고 UTF-8 과 UTF-16 (BOM 유무 각각)입니다. ISO-2022-JP 는 구조상 읽기만 됩니다.
아니요. 변환은 전부 브라우저 안에서 일어나고, 파일을 받는 서버 자체가 없습니다. 소스 전체가 그대로 공개돼 있으니 직접 확인하실 수 있습니다.