압축 파일 안의 문서 한 장만 고쳐야 할 때, 전부 풀지 않고 바꾸기

납품용 압축 파일을 보냈는데 안에 든 문서 한 장에 오타가 있습니다. 파일이 수백 개라 전부 풀고 고치고 다시 묶으려니 시간도 걸리고 임시 폴더도 지저분해집니다.

ZIP은 파일마다 따로 압축해 담고 맨 뒤에 목차를 붙이는 구조라, 한 개만 바꾸는 길이 따로 있습니다. 텍스트 400개가 든 압축 파일에서 한 개만 새 내용으로 바꿔 보며 세 가지 방법을 재 봤습니다.

ZIP 파일 하나 교체 실측 표. 텍스트 400개가 든 5,054,615바이트 압축 파일에서 한 개만 바꿨다. 전부 풀고 다시 묶기는 2.39초에 5,042,635바이트 400항목, 메모리에서 다시 묶기는 1.51초에 5,042,259바이트 400항목으로 둘 다 중복이 없다. 뒤에 덧붙이기는 0.01초로 가장 빠르지만 5,054,908바이트에 401항목이 되고 같은 이름이 1건 중복된다. 측정 환경은 윈도우 11, 파이썬 3.14.6, 표준 라이브러리 zipfile
ZIP 파일 하나 교체 실측 표. 텍스트 400개가 든 5,054,615바이트 압축 파일에서 한 개만 바꿨다. 전부 풀고 다시 묶기는 2.39초에 5,042,635바이트 400항목, 메모리에서 다시 묶기는 1.51초에 5,042,259바이트 400항목으로 둘 다 중복이 없다. 뒤에 덧붙이기는 0.01초로 가장 빠르지만 5,054,908바이트에 401항목이 되고 같은 이름이 1건 중복된다. 측정 환경은 윈도우 11, 파이썬 3.14.6, 표준 라이브러리 zipfile

가장 빠른 방법에 함정이 있습니다

세 방법 모두 목표한 파일의 내용은 새 것으로 바뀌었습니다. 확인해 보니 셋 다 26,400바이트짜리 새 내용을 정확히 돌려주었습니다. 갈린 것은 그다음입니다.

방법걸린 시간항목 수같은 이름 중복
전부 풀고 다시 묶기2.39 초400없음
메모리에서 다시 묶기1.51 초400없음
뒤에 덧붙이기0.01 초4011 건

덧붙이기는 240배 가까이 빠릅니다. 뒤에 새 내용을 이어 붙이고 목차만 갱신하면 되기 때문입니다. 그런데 지운 것이 아니라 더한 것이라 같은 이름이 두 번 들어갑니다.

항목이 401개가 됐고, 압축 파일 크기도 5,054,908바이트로 원본보다 오히려 커졌습니다. 옛 내용이 그대로 남아 있는 것입니다.

이 상태가 위험한 이유는 어느 쪽을 꺼낼지 프로그램마다 다르기 때문입니다. 목차를 먼저 보는 도구는 나중 것을, 앞에서부터 훑는 도구는 옛것을 꺼냅니다. 내 화면에서는 고쳐진 문서가 보이는데 받는 쪽에서는 옛 문서가 열릴 수 있습니다.

고친 문서에 지워야 할 내용이 있었다면 문제가 더 큽니다. 파일 안에 옛 내용이 통째로 남아 있으니 지운 것이 아닙니다.


안전하면서 빠른 쪽

메모리에서 다시 묶는 방법이 균형이 좋습니다. 임시 폴더에 400개를 쏟아 놓았다가 다시 읽는 왕복이 없어서, 전부 푸는 것보다 1.6배 빨랐습니다. 결과는 항목 400개에 중복 없음으로 깨끗합니다.

import zipfile

TARGET = "docs/report_137.txt"
NEW = "고친 내용입니다.\n".encode("utf-8")

with zipfile.ZipFile("archive.zip") as zin, \
     zipfile.ZipFile("archive_new.zip", "w", zipfile.ZIP_DEFLATED) as zout:
    for info in zin.infolist():
        data = NEW if info.filename == TARGET else zin.read(info.filename)
        zout.writestr(info.filename, data)

# 확인
with zipfile.ZipFile("archive_new.zip") as z:
    names = z.namelist()
    print(len(names), "항목,  중복", len(names) - len(set(names)), "건")

마지막 두 줄이 중요합니다. 바꾼 뒤에는 항목 수와 중복을 반드시 확인하시는 편이 좋습니다. 위 측정에서 덧붙이기의 문제가 드러난 것도 이 확인 덕분입니다.

주의할 점이 하나 있습니다. 이 방식은 원본을 고치지 않고 새 파일을 만듭니다. 같은 이름으로 덮어쓰려 하면 읽는 중에 쓰게 되어 파일이 상합니다. 새로 만든 뒤 이름을 바꾸는 순서로 하시면 됩니다.

안에 뭐가 들었는지부터

고칠 파일의 정확한 이름을 알아야 위 코드가 동작합니다. 폴더 경로까지 포함한 이름이 필요합니다. 압축을 풀지 않고 목록만 보는 방법은 파일명이 깨져 보일 때의 처리와 함께 알아 두면 편합니다. 이름이 깨진 상태라면 바꿔 넣을 때도 그 이름을 그대로 써야 합니다.

바꾼 뒤 파일이 이상해졌다면 어디가 깨졌는지 확인하는 방법으로 짚어 볼 수 있고, 크기 때문에 보내기 어렵다면 조각으로 나눠 보내는 방법 쪽이 이어집니다.

압축 파일 안의 문서를 고쳤다면 보내기 전에 항목 수를 세어 보세요. 원본보다 하나 늘어 있다면 옛 내용이 아직 그 안에 있습니다.

참고한 공식 문서

위 시간과 크기는 윈도우 11에서 파이썬 3.14.6의 표준 라이브러리 zipfile로 측정했습니다. 시간은 파일 개수와 디스크 상태에 따라 달라지므로 순서만 참고하시면 됩니다.