원문 캡처 · github.com
원문 캡처 · github.com

HatchZip은 압축 파일을 풀면서 원본 압축 파일을 단계적으로 줄여, 압축 파일과 해제된 파일을 동시에 보관할 공간이 없는 상황에서도 작업할 수 있게 하는 도구다. Windows, macOS, Linux에서 ZIP·7z·tar 등 여러 형식을 지원하며, 작업을 멈췄다가 이어서 할 수도 있다.

게임 데이터 180GB를 압축 해제해 200GB가 되는 경우, WinRAR·7-Zip·unzip은 압축 파일과 해제된 파일이 동시에 있어야 해 총 380GB가 필요하다. HatchZip은 압축 파일의 끝에서부터 파일을 꺼내고, 작업을 마친 부분을 압축 파일에서 잘라낸다. 이렇게 확보한 공간을 다음 파일을 쓰는 데 재사용하므로 200GB보다 조금 더 큰 여유 공간으로 작업할 수 있다고 프로젝트는 설명한다.

HatchZip은 파일을 변경하기 전에 전체 작업이 디스크 공간에 들어가는지 확인하고, 작업 중에도 일정한 여유 공간을 남긴다. 작업을 중단하거나 프로그램이 비정상 종료돼도 진행 상황을 보존해 다시 이어서 실행할 수 있다. 지원 형식은 ZIP, 7z, tar, .tar.gz와 분할 압축 파일이다.

설치와 사용

코드를 작성하지 않고 데스크톱 앱을 설치해 사용할 수 있다. Windows 10·11용 설치 파일과 Apple Silicon 및 Intel용 macOS 앱은 아래 링크에서 받을 수 있다. Linux 설치 방법과 최신 릴리스 파일도 프로젝트 저장소에서 확인할 수 있다.

설치 파일은 Windows에서 사용자별로 설치되며 관리자 권한이 필요하지 않고, 마우스 오른쪽 버튼 메뉴도 추가한다. 설치하면 hatchzip 명령과 짧은 이름인 hz가 PATH에 등록된다. Windows 설치 파일과 macOS 앱은 아직 코드 서명이 되어 있지 않다. Windows에서는 경고 창에서 ‘More info’를 누른 뒤 ‘Run anyway’를 선택해야 한다. macOS 15 이상에서는 앱을 한 번 연 다음 시스템 설정의 ‘개인정보 보호 및 보안’에서 실행을 허용해야 한다.

Windows 설치 프로그램을 실행한 뒤 압축 파일을 마우스 오른쪽 버튼으로 눌러 HatchZip을 선택하고 ‘Extract here’를 고르면 된다. Windows 11에서는 메뉴가 ‘Show more options’ 아래에 있다. 또는 시작 메뉴에서 HatchZip을 열어 압축 파일을 선택한 다음 ‘Extract’를 누를 수 있다. Windows에서는 아직 코드 서명이 되지 않아 ‘Windows protected your PC’ 경고가 표시될 수 있다. 이 경우 ‘More info’를 선택하고 ‘Run anyway’를 누른다.

데스크톱 앱은 최신 릴리스에서 받을 수 있다. 제공 파일은 Windows 10·11(x64)의 HatchZip-x64-setup.exe, macOS Apple Silicon의 HatchZip-aarch64.dmg, macOS Intel의 HatchZip-x86_64.dmg, Debian·Ubuntu용 hatchzip_amd64.deb, Fedora·openSUSE용 hatchzip-x86_64.rpm, 일반 Linux용 HatchZip-x86_64.AppImage다.

명령줄 도구만 설치하려면 Linux와 macOS에서 다음 명령을 실행한다.

sh
curl -fsSL https://raw.githubusercontent.com/xevrion/hatchzip/main/install.sh | sh

Windows PowerShell에서는 다음 명령을 사용한다.

powershell
irm https://raw.githubusercontent.com/xevrion/hatchzip/main/install.ps1 | iex

두 설치 방법은 최신 릴리스에서 해당 기기에 맞는 빌드를 내려받고, 공개된 SHA-256 체크섬과 대조한 뒤 사용자 계정에만 hatchzip과 hz를 설치한다. 맨 페이지와 셸 자동 완성도 함께 설치되며, 루트 또는 관리자 권한은 필요하지 않다.

Cargo로 명령줄 도구를 설치할 수도 있다.

sh
cargo install hatchzip

이 명령은 crates.io의 hatchzip에서 도구를 빌드한다. 엔진은 hatchzip-core 라이브러리로도 제공된다.

소스에서 빌드하려면 Rust를 설치한다. Rust 버전은 rust-toolchain.toml에 고정되어 있으며 자동으로 설치된다.

sh
git clone https://github.com/xevrion/hatchzip.git
cd hatchzip
cargo build --release

실행 파일은 target/release/hatchzip과 target/release/hatchzip-gui이며, Windows에서는 확장자가 .exe다. Linux에서 데스크톱 앱을 빌드하려면 fontconfig 헤더(libfontconfig1-dev 또는 fontconfig-devel)가 필요하다.

셸 자동 완성과 맨 페이지는 다음 명령으로 설치할 수 있다.

sh
hatchzip completions bash > ~/.local/share/bash-completion/completions/hatchzip
hatchzip completions zsh > "${fpath[1]}/_hatchzip"
hatchzip completions fish > ~/.config/fish/completions/hatchzip.fish
hatchzip completions powershell >> $PROFILE

hatchzip man > ~/.local/share/man/man1/hatchzip.1 # then: man hatchzip

공간 확인과 압축 해제

압축을 풀기 전에 check로 필요한 공간을 확인할 수 있다. 이 명령은 압축 파일의 색인과 디스크의 남은 공간을 읽지만 파일을 변경하지 않는다.

sh
hatchzip check game.zip

예시 결과는 다음과 같다.

text
game.zip  180.2 MB zip, 200.0 MB uncompressed in 22 entries
  available                    50.5 MB
  standard extraction needs    200.0 MB
  required                     29.9 MB (+5.0 MB reserved)
  available after              30.6 MB

기본 명령은 압축 파일 옆에 압축 파일 이름을 딴 폴더를 만들고 그 안에 푼다. 출력 폴더는 비어 있거나 아직 존재하지 않아야 한다. 다른 폴더를 지정하거나 분할 압축 파일을 풀 수도 있다.

sh
hatchzip extract game.zip # into ./game/
hatchzip extract game.zip -o D:\Games\Foo # somewhere else
hatchzip x game.7z.001 # split archive; any part's name works

HatchZip은 공간 계획을 보여주고 확인을 요청한 뒤 압축을 푼다. 완료되면 압축 파일은 제거되고 출력 폴더에 해제된 파일만 남는다. 분할 압축 파일은 각 볼륨의 내용이 모두 추출되는 즉시 해당 볼륨을 삭제한다.

  • -o, --output <dir>: 출력 폴더를 지정한다. 기본값은 압축 파일 옆에 만드는, 압축 파일 이름을 딴 폴더다. 폴더는 비어 있거나 없어야 한다.
  • --keep-free <size>: HatchZip이 사용하지 않고 남겨 둘 공간을 지정한다. 예를 들어 500MB, 10GB를 쓸 수 있다. 기본값은 2GB로, 디스크 공간을 0까지 쓰지 않게 한다.
  • --verify-first: 삭제 전에 압축 파일 전체를 읽고 모든 파일의 체크섬을 확인한다. 시간이 더 걸리며, 다시 내려받기 어려운 파일에 권장된다.
  • -y, --yes: 확인 질문을 건너뛴다. 터미널에서 실행하지 않을 때 필요하다.
  • -q, --quiet: 오류와 확인 질문만 출력한다.

Ctrl+C를 한 번 누르면 HatchZip은 현재 쓰는 파일을 마무리하고 진행 상황을 저장한 뒤 종료한다. 두 번 누르면 즉시 종료하며, 이 경우도 안전하다고 안내한다. 같은 명령을 다시 실행하면 이어서 작업한다.

sh
hatchzip extract game.zip

프로그램 충돌, 정전, 다른 작업이 디스크 공간을 채운 경우에도 같은 방식으로 다시 실행할 수 있다. 진행 상황은 압축 파일 옆의 작은 파일 두 개, game.zip.hatchzip과 game.zip.hatchzip-tail에 저장된다. 압축 해제가 끝나지 않았다면 이 파일들을 삭제하면 안 된다. 압축 파일에서 이미 제거된 색인이 이 파일들에 들어 있기 때문이다.

중단한 압축 해제를 포기하려면 다음 명령으로 남은 압축 파일을 복구할 수 있다.

sh
hatchzip restore game.zip

복구 후에는 아직 추출하지 않은 항목만 담긴 일반 압축 파일이 남고, 7-Zip·WinRAR·unzip·tar로 열 수 있다. 이미 추출한 파일은 출력 폴더에 그대로 남는다. 복구 작업은 새 색인만 덧붙이므로 색인 크기만큼의 여유 공간만 필요하다. 중간에 작업이 끊겨도 다시 실행하면 마무리된다.

파일 목록을 확인하거나 체크섬을 검사하는 명령은 다음과 같다.

sh
hatchzip list game.7z # what's inside, with sizes
hatchzip test game.7z # read every file and verify its checksum, writing nothing

일반 tar에는 파일 내용의 체크섬이 없다. 따라서 tar에서 test는 모든 파일을 끝까지 읽을 수 있는지만 확인한다.

종료 상태 코드는 다음과 같다.

  • 0: 성공.
  • 1: 실패. 압축 해제가 시작됐다면 진행 상황을 저장한다.
  • 2: 명령줄이 올바르지 않다.
  • 3: 여유 공간이 부족하다. 아무것도 변경하지 않았다.
  • 4: 압축 파일이 손상됐다. 아무것도 삭제하지 않았다.
  • 130: Ctrl+C로 일시 중지했다. 같은 명령을 다시 실행하면 이어서 작업한다.

공간 절약 효과와 측정 조건

프로젝트는 실제 압축 도구로 만든 게임 데이터 200MB(파일 21개)를 약 50MB의 여유 공간이 있는 220MiB 디스크에서 테스트했다고 밝혔다. 디스크 공간 테스트에는 노트북 디스크가 꽉 찬 상황을 모사하기 위해 220MiB tmpfs를 사용했다. 속도 테스트는 ext4에서 진행했다.

압축 파일unzip·7-Zip의 결과HatchZip의 결과
ZIP, 180MBunzip은 21개 중 6번째 파일에서 공간 부족으로 중단완료, 모든 파일의 바이트가 동일
7-Zip 25.01로 만든 7z, -ms=20m(솔리드 블록 10개)380MB 필요0.64초 만에 완료, 모든 파일의 바이트가 동일
40MB 볼륨 5개로 분할한 7z380MB 필요완료, 볼륨을 하나씩 삭제해 최대 사용량 210MB
GNU tar로 만든 200MB tar400MB 필요0.12초 만에 완료, 모든 파일의 바이트가 동일
7-Zip 기본 설정으로 만든 7z(솔리드 블록 1개)380MB 필요작업 전 거부. 솔리드 블록 전체를 꺼낼 때까지 공간을 회수할 수 없어 여유 공간 200MB가 필요

같은 데이터를 담은 ZIP의 압축 해제 속도는 HatchZip이 0.25~0.35초, unzip 6.00은 1.48~1.50초였다고 프로젝트는 보고했다. 이 비교에는 unzip이 생략하는 fsync도 포함됐다. 여러 파일을 병렬로 압축 해제해 속도를 높이므로, 압축 파일에 큰 파일 하나만 들어 있다면 단일 코어 속도로 처리된다.

프로젝트는 여러 파일 형식에서 작업 중단 시점을 바꿔 가며 배치의 안전하지 않을 수 있는 두 시점에 강제 종료하는 테스트를 했고, 재개한 작업마다 동일한 결과가 나왔다고 밝혔다. 지원 파일 시스템으로 ext4, btrfs, XFS, NTFS, APFS, exFAT, FAT32 USB 드라이브를 든다.

출력 크기 외에 필요한 추가 공간은 한 번에 해제할 수 있는 가장 큰 단위의 크기 정도다. ZIP과 tar는 파일 하나, 7z는 솔리드 블록 하나가 그 단위다. 큰 파일을 담은 ZIP이나 tar는 파일 시스템이 홀 펀칭(hole punching)을 지원하면 해제 중 256MiB마다 압축 데이터 공간을 반환할 수 있다. 프로젝트는 600MiB짜리 저장 또는 Deflate 방식 ZIP 파일 하나를 950MiB tmpfs에서 해제한 사례를 들었다. 이 경우 필요한 여유 공간은 작업 전 629MB였지만 실제로는 367MB로 줄었다. 이 방식은 ext4, XFS, btrfs, tmpfs에서 가능하며, NTFS와 APFS에서는 아직 검증되지 않았다. hatchzip check는 각 압축 파일에 필요한 정확한 공간을 계산한다.

안전성과 한계

HatchZip은 압축을 풀면서 원본 압축 파일을 훼손하므로, 아직 안전하게 디스크에 저장되지 않은 파일을 잃지 않도록 설계했다고 프로젝트는 설명한다.

  • 공간 계획이 맞는지 확인하기 전에는 아무것도 변경하지 않는다. 처음 데이터를 건드리기 전에 전체 작업을 재생해 가장 많은 여유 공간이 필요한 순간을 찾으며, 총 출력 크기만 확인하지 않는다.
  • 파일을 쓴 뒤 물리 디스크에 플러시하고, 제자리로 이름을 바꾼 다음에야 그 파일이 들어 있던 압축 파일 부분을 잘라낸다. 어느 시점에 충돌해도 파일 또는 압축 데이터 가운데 하나는 남고, 둘 다 사라지지는 않는다는 설명이다.
  • 압축 파일이 손상되면 그 지점에서 작업을 중단한다. 추출하는 동안 모든 파일의 CRC-32를 확인하며, 손상된 파일은 쓰지 않고 그 파일과 그 뒤의 내용은 압축 파일에 남긴다.
  • 출력 폴더 바깥으로 벗어나는 경로, 서로 겹치는 항목, 중복 항목이 있는 악성 압축 파일은 삭제 작업 전에 거부한다. setuid와 setgid 비트는 복원하지 않는다. 심볼릭 링크는 일반 파일을 모두 쓴 뒤에 만들어 링크를 통해 파일을 쓰지 못하게 한다.

다운로드 자체가 손상된 경우에는 이 보호가 완전하지 않다. 손상 지점 앞의 파일은 추출되고 압축 파일은 일부 사라진다. 다시 내려받을 수 없는 압축 파일에는 --verify-first를 사용하라고 안내한다. btrfs나 APFS에서 압축 파일을 포함한 스냅샷이 공간을 잡고 있으면 스냅샷을 삭제하기 전까지 그 공간은 반환되지 않는다.

지원 형식

형식지원 범위와 확인된 사항공간을 반환하는 단위
ZIP, ZIP64, 자체 압축 해제형 .exe ZIP지원. Stored, Deflate, Deflate64, bzip2, LZMA, PPMd, Zstandard를 테스트했다. 7-Zip으로 만든 압축 파일과 4GiB를 넘는 항목도 포함한다. XZ도 같은 라이브러리로 해제되지만 아직 여기서는 테스트하지 않았다.파일 하나
7z, 솔리드 및 비솔리드지원. 7-Zip 25.01로 만든 LZMA2(기본값), LZMA, LZMA2를 사용한 BCJ, bzip2, Deflate, PPMd를 테스트했다. Zstandard도 같은 라이브러리로 해제되지만 아직 테스트하지 않았다.솔리드 블록 하나
비압축 tar지원. 긴 파일 이름, 심볼릭 링크, 하드 링크를 포함한다.파일 하나
.tar.gz, .tgz지원. 여러 멤버로 된 gzip(bgzip, pigz -i)과 분할 .tar.gz.001 세트를 포함한다. 앞에서부터 해제하며, 파일 시스템이 홀 펀칭을 지원하면 디코더가 지나간 압축 데이터를 반환한다. 재개 지점에서 충돌 뒤 다시 시작할 수 있다. 아무것도 건드리기 전에 각 gzip CRC-32와 길이를 확인한다. GNU tar와 gzip, pigz로 만든 압축 파일을 테스트했다. FAT32·exFAT에서는 해제 및 재개가 가능하지만 압축 파일과 모든 파일을 동시에 담을 공간이 필요하며, check가 이를 표시한다.출력 데이터 256MiB
ZIP 또는 7z 분할 압축 파일(.001, .002 등)7-Zip이 만드는 형식을 지원한다.파일 하나 또는 블록 하나. 비워진 볼륨은 삭제한다.
암호로 보호된 ZIP(ZipCrypto, AES) 및 7z(AES, 암호화된 파일 목록 포함)지원. 암호를 묻거나 --password 또는 HATCHZIP_PASSWORD로 받을 수 있다. 삭제 전에 파일 하나에서 암호가 맞는지 확인한다. 암호는 저장하지 않으므로 재개할 때 다시 입력해야 한다.비암호화 파일과 동일
.tar.zst, .tar.xz압축 해제 전에 거부한다. 지원은 계획 중이다. 자세한 내용은 형식 안내를 참조한다.—
솔리드 블록 하나로 된 대용량 7z작동하지만 위 표의 사례처럼 블록 전체 크기만큼 여유 공간이 필요하다. 블록 내부에서 공간을 반환하는 방법은 계획 중이다.블록 전체
RAR, 네이티브 분할 ZIP(.z01)지원 계획 중이다.—

작동 방식과 프로젝트 링크

ZIP, 7z, tar는 내용을 독립된 단위의 연속으로 저장한다. ZIP과 tar에서는 파일이, 7z에서는 솔리드 블록이 단위다. ZIP과 7z의 색인은 파일 끝에 있다. HatchZip은 색인을 별도 파일에 저장한 뒤 압축 파일의 마지막 단위부터 해제한다. 각 묶음 작업이 끝나면 시작 지점까지 압축 파일을 잘라낸다. 이 작업은 모든 운영체제와 파일 시스템에서 지원된다. 분할 압축 파일에서는 볼륨 전체를 삭제하고, 한 볼륨은 줄인다.

.tar.gz는 앞에서부터만 디코딩할 수 있는 단일 압축 스트림이다. 따라서 HatchZip은 앞에서 뒤로 해제하고, 사용한 압축 데이터에 홀을 만들어 공간을 반환한다. 충돌 뒤 다시 시작할 수 있도록 재개 지점도 남긴다. 충돌에도 안전하도록 쓰기 순서를 정한 전체 설계는 작동 방식 문서에 있다.

HatchZip은 Apache License 2.0 또는 MIT 라이선스 중 하나를 선택해 사용할 수 있다. 기여 전에 CONTRIBUTING.md를 읽어야 한다. CI에서 다루지 않는 운영체제와 파일 시스템에 관한 버그 제보도 받는다.