받은 압축 파일을 풀었더니 안에 든 한글 파일명이 ║╕░φ╝¡.txt 같은 외계어나 물음표(???)로 바뀌어 있는 경우가 있습니다. 정작 파일을 열면 내용은 멀쩡한데 이름만 알아볼 수 없습니다.
파일이 망가진 것이 아닙니다. ZIP이 파일명을 저장하는 방식과, 압축을 푸는 프로그램이 그 이름을 읽는 방식이 어긋났을 뿐입니다. 윈도우 11과 파이썬 3.14.3 표준 라이브러리 zipfile로 직접 만들어 확인한 결과를 근거로 풀어 보겠습니다.
ZIP은 파일명을 어떻게 저장하나
ZIP 안에는 각 파일의 이름이 바이트로 적혀 있고, "이 이름이 어떤 인코딩이냐"를 알려 주는 작은 표식이 따로 있습니다. ZIP 규격에서 이 표식은 일반 목적 플래그의 11번째 비트, 값으로는 0x800입니다.
이 비트가 켜져 있으면 "파일명은 UTF-8"이라는 뜻입니다. 꺼져 있으면 인코딩이 정해져 있지 않아, 압축 푸는 쪽이 자기 시스템 기본 인코딩으로 짐작해서 읽습니다. 한국어 윈도우에서는 그 짐작이 보통 CP949입니다.
문제는 오래된 한국어 압축 프로그램들이 이 비트를 켜지 않고, 파일명을 CP949 바이트 그대로 넣어 둔다는 점입니다. 그 ZIP을 UTF-8로 읽는 환경에서 풀면 바이트 해석이 어긋나 이름이 깨집니다.
실제로 만들어 보니 이렇게 갈립니다
같은 파일명 보고서.txt를 두 가지 ZIP으로 만들었습니다. 하나는 UTF-8 플래그를 끄고 이름을 CP949 바이트로 넣은 레거시 방식, 다른 하나는 UTF-8 플래그를 켠 현대 방식입니다. 그리고 둘 다 파이썬 zipfile로 다시 읽었습니다.
먼저 디스크에 실제로 적힌 한글 파일명의 바이트입니다.
보고서.txt (CP949) : ba b8 b0 ed bc ad 2e 74 78 74 (10 byte)
이 같은 바이트를 두 ZIP이 어떤 플래그로 감싸느냐에 따라 읽히는 결과가 달라집니다.
| ZIP | UTF-8 비트(0x800) | 읽은 파일명 | 결과 |
|---|---|---|---|
| 레거시(CP949) | 꺼짐 | ║╕░φ╝¡.txt | 깨짐 |
| 현대(UTF-8) | 켜짐 | 보고서.txt | 정상 |
레거시 ZIP에서 zipfile이 내놓은 이름이 바로 ║╕░φ╝¡.txt입니다. 이름이 깨진 것은 파일 손상이 아니라, 인코딩 표식이 없어 읽는 쪽이 다른 규칙으로 해석한 결과입니다.
왜 압축 프로그램마다 결과가 다를까
같은 ZIP을 윈도우 기본 압축 풀기, 7-Zip, 반디집류로 각각 풀면 결과가 갈리는 일이 흔합니다. 원인은 단순합니다. UTF-8 비트가 꺼진 ZIP을 만났을 때 이름을 무슨 인코딩으로 짐작하느냐가 프로그램마다 다르기 때문입니다.
시스템 기본 코드페이지(CP949)로 짐작하는 도구는 한국어 윈도우에서 이름을 맞게 풉니다. 반대로 무조건 UTF-8로 읽거나 다른 언어 코드페이지로 짐작하는 도구는 같은 파일에서 이름을 깨뜨립니다. 파일이 달라진 게 아니라 짐작이 달라진 것입니다.
해결책 1: 보내는 쪽이 UTF-8로 압축하기
가장 깔끔한 해결은 압축을 만드는 단계에서 UTF-8 플래그를 켜는 것입니다. 받는 사람이 어떤 OS를 쓰든 이름이 그대로 보입니다. 최신 7-Zip, 반디집, 맥과 리눅스의 기본 압축은 대체로 UTF-8로 파일명을 저장합니다.
파이썬 zipfile은 파일명에 한글처럼 비ASCII 문자가 있으면 UTF-8 플래그를 자동으로 켜 줍니다. 위 현대 ZIP이 그렇게 만들어졌습니다.
import zipfile
with zipfile.ZipFile("modern.zip", "w") as z:
z.write("보고서.txt") # 비ASCII 이름이면 zipfile이 0x800 플래그를 자동으로 켠다
다만 받는 사람이 전부 구버전 한국어 윈도우 압축기만 쓴다면, 그쪽에서는 UTF-8 이름이 도리어 깨져 보일 수 있습니다. 환경에 따라 다를 수 있으니 받는 환경을 알면 거기에 맞추는 편이 안전합니다.
해결책 2: 받는 쪽이 인코딩을 지정해 풀기
이미 받은 ZIP이 레거시 방식이라 고칠 수 없다면, 푸는 쪽에서 이름을 바로잡을 수 있습니다. 핵심은 zipfile이 플래그 없는 이름을 임시로 CP437로 디코딩해 둔다는 점입니다. 그래서 CP437로 되돌린 뒤 CP949로 다시 읽으면 원래 이름이 복원됩니다.
import zipfile, os
with zipfile.ZipFile("legacy.zip") as z:
for info in z.infolist():
name = info.filename
if not (info.flag_bits & 0x800): # UTF-8 플래그가 없으면
name = name.encode("cp437").decode("cp949") # CP949로 재해석
with open(name, "wb") as f:
f.write(z.read(info))
실제로 위 코드를 레거시 ZIP에 돌리니 깨졌던 ║╕░φ╝¡.txt가 보고서.txt로 복원되어 저장되었습니다. 플래그가 꺼진 한글 ZIP은 cp437로 인코딩한 뒤 cp949로 디코딩하면 이름이 돌아옵니다.
GUI 도구만 쓴다면, 7-Zip의 압축 풀기 옵션이나 반디집의 인코딩 선택 메뉴에서 파일명 인코딩을 한국어(CP949)로 바꿔 주면 같은 효과를 얻습니다. 메뉴 위치와 표기는 버전에 따라 다를 수 있습니다.
자주 묻는 질문
Q. 이름이 깨진 채로 풀렸는데 그 파일을 다시 압축하면 굳어 버리나요?
이름은 다시 바꿀 수 있습니다. 다만 한 번 잘못 디코딩된 이름은 원본 정보를 일부 잃을 수 있으니, 가능하면 원본 ZIP을 위 방법으로 다시 푸는 편이 확실합니다.
Q. 맥에서 만든 ZIP을 윈도우에서 풀 때도 깨지던데요.
방향만 반대인 같은 문제입니다. 맥 기본 압축은 UTF-8로 저장하는데, 그 ZIP을 UTF-8 비트를 무시하고 CP949로 짐작하는 구형 도구로 풀면 깨집니다. UTF-8을 제대로 읽는 도구로 풀면 해결됩니다.
Q. 내용물(텍스트)도 같이 깨졌어요.
그건 파일명이 아니라 파일 안 글자의 인코딩 문제로, 별개의 원인입니다. 압축과 무관하게 그 파일 자체를 올바른 인코딩으로 다시 열어야 합니다.
한글 파일명이 깨지면 ZIP의 UTF-8 비트(0x800)가 켜졌는지부터 떠올려 보세요. 보내는 쪽은 UTF-8 지원 도구로 묶고, 받는 쪽은 인코딩을 CP949로 지정해 풀면 양쪽 어디서도 이름이 살아남습니다.
'파일 형식 백과사전' 카테고리의 다른 글
| 같은 사진을 WebP, AVIF, JPG로 저장하면 용량이 얼마나 차이 날까 (직접 측정 후기) (0) | 2026.07.15 |
|---|---|
| 같은 폴더를 ZIP, 7Z, TAR.GZ로 압축하면 용량이 얼마나 다를까, 3종 직접 측정 (0) | 2026.07.14 |
| XML로 받은 데이터를 JSON으로 바꾸고 싶을 때, 표준 라이브러리만으로 해결하기 (0) | 2026.07.13 |
| SVG를 PNG로 변환하는 법, 출력 크기는 어떻게 정해야 할까 (0) | 2026.07.12 |
| 앱이 만든 .db 파일, 안에 든 데이터를 들여다보는 단계별 방법 (0) | 2026.07.11 |