📁 File5. 애플리케이션 파일03 · Archives — ZIP · TAR · GZ · 7z · RAR

03 · Archives — ZIP · TAR · GZ · 7z · RAR

이 문서가 답하는 질문: ZIP 과 TAR 의 진짜 차이는 무엇인가? 왜 .tar.gz 는 두 단계를 거치는가? 압축 해제 시 무엇이 위험한가? MIME: application/zip, application/x-tar, application/gzip, application/x-7z-compressed


한 줄 답 (Pyramid Top)

아카이브의 본질은 “컨테이너(여러 파일을 한 파일로) ↔ 압축(바이트 줄이기)” 의 분리. ZIP 은 둘을 한 번에, TAR 는 컨테이너만, GZ 는 압축만 한다. 그리고 모든 아카이브 처리기는 Zip Slip · Zip Bomb · symlink traversal 을 막아야 한다.


Why — 컨테이너와 압축은 왜 분리되었나

UNIX 의 철학 — 한 도구 한 일

tar (1979, Tape ARchive) — 본래 테이프 백업용. 여러 파일을 한 개의 연속 스트림 으로 이어붙이기만 함. 압축 X. compress (1984) → gzip (1992) — 바이트 줄이기 하나만.

그래서 UNIX 에서는:

파일들 → tar → 한 스트림 → gzip → 압축 스트림

파이프로 연결: tar cf - dir/ | gzip > dir.tar.gz.

Windows / DOS 의 철학 — 한 도구 다 하기

ZIP (1989, Phil Katz) — 컨테이너 + 압축 + 인덱스 를 한 포맷에. 각 파일을 개별로 압축. 랜덤 액세스 가 가능 (전체를 풀지 않고 한 파일만).

Windows: ZIP 더블클릭 → 내부 구조 보기 → 파일 하나만 추출
UNIX:    .tar.gz 는 전부 풀어야 한 파일을 본다 (스트림이라서)

이 두 철학이 지금도 살아있다.


How — 각 포맷의 내부 구조

TAR — 헤더 + 데이터의 반복

[ Header (512B) | Data (N×512B) ] [ Header | Data ] ... [ 0×1024 (END) ]

각 헤더에 파일명·크기·권한·시간이 ASCII 로 적혀있고, 데이터는 그 뒤에 그대로. 압축 없음.

$ tar tvf backup.tar
-rw-r--r-- raw/staff      1234 2026-05-10 10:00 docs/a.txt
drwxr-xr-x raw/staff         0 2026-05-10 10:00 docs/img/
-rw-r--r-- raw/staff    245678 2026-05-10 10:00 docs/img/cover.png

한계: 한 파일 위치를 알려면 처음부터 헤더 체인을 따라가야 함 (랜덤 액세스 X).

ZIP — 끝의 Central Directory 가 인덱스

[ Local File Header + 압축 데이터 ] × N
[ Central Directory (모든 파일의 메타) ]
[ End of Central Directory Record (EOCD) ]   ← 끝

PDF 의 xref 와 같은 패턴 — 인덱스가 끝에. EOCD 의 매직 PK\x05\x06 를 끝에서 찾고, 거기서 Central Directory 위치를 안다.

$ unzip -l app.zip
Archive:  app.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   245678  2026-05-10 10:00   src/main.js
    12345  2026-05-10 10:00   src/lib/util.js
       89  2026-05-10 10:00   package.json
---------                     -------
   258112                     3 files

unzip -l 이 빠른 이유: EOCD 만 읽으면 됨. 데이터 안 풀어도 됨.

ZIP의 압축 알고리즘

메소드내부값비고
Store0압축 없음 (이미 PNG/JPEG 인 경우)
Deflate8zlib, 가장 흔함
Deflate649사전 64KB → 호환성 떨어짐
BZIP212더 작지만 느림
LZMA147z 와 같음
Zstandard93최신, 빠르고 작음

GZ / BZ2 / XZ / ZSTD — 단일 스트림 압축

이들은 컨테이너가 아니다. 단일 바이트 스트림을 압축. 여러 파일을 묶으려면 TAR 로 먼저 한 스트림을 만들고 그 위에 압축.

포맷알고리즘압축률속도
.gzDEFLATE (LZ77 + Huffman)보통빠름
.bz2Burrows-Wheeler + Huffman좋음느림
.xzLZMA2매우 좋음매우 느림
.zstZstandard (Facebook)좋음매우 빠름

.tar.gz vs .tar.zst — 2020년 이후 zstd 가 압축률·속도 둘 다 우위 → Arch Linux, Ubuntu 22.04, Fedora 가 패키지 포맷을 zstd 로 전환.

7z — Solid Compression

7z (1999, Igor Pavlov) — 여러 파일을 하나의 스트림으로 합쳐서 압축 (solid). 같은 패턴이 파일 경계를 넘어 재사용 → 압축률 +10~30%. 비용: 한 파일 추출에도 전체를 디코드.

$ 7z l archive.7z
   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2026-05-10 10:00:00 ....A      1245678               docs/a.txt
2026-05-10 10:00:00 ....A       567890               docs/b.txt
2026-05-10 10:00:00 ....A       890123       234567  docs/all (solid block)

RAR — 사유, but 흔함

  • 매직: 52 61 72 21 1A 07 00 (Rar!...)
  • 알고리즘 사양 비공개 (디코더만 무료, 인코더 유료)
  • 리눅스 표준 도구는 unrar. RAR 5 부터 더 강한 sliding dictionary.

What — CLI / 실전

매직 / 식별

$ xxd file.zip | head -1
00000000: 504b 0304 1400 0000 0800 ...   PK..............
 
$ xxd file.tar | head -1     # 257번째 byte 부터 'ustar'
00000000: 6d79 6669 6c65 2e74 7874 ...
 
$ xxd file.gz | head -1
00000000: 1f8b 0800 ...                  ..
 
$ xxd file.7z | head -1
00000000: 377a bcaf 271c ...             7z..'.
 
$ xxd file.xz | head -1
00000000: fd37 7a58 5a00 ...             .7zXZ.
 
$ xxd file.zst | head -1
00000000: 28b5 2ffd ...                  (./

자주 쓰는 명령

# 목록만
unzip -l a.zip
tar tvf a.tar.gz
7z l a.7z
 
# 풀기
unzip a.zip -d out/
tar xzf a.tar.gz -C out/
tar xJf a.tar.xz -C out/   # xz
tar xf  a.tar.zst -C out/  # zstd (최근 tar 는 자동 감지)
7z x a.7z -oout/
 
# 만들기
zip -r a.zip dir/
tar czf a.tar.gz dir/
tar caf a.tar.zst dir/      # 확장자로 자동 선택
7z a -mx9 a.7z dir/         # mx9 = 최대 압축

Python 으로

import zipfile, tarfile
 
with zipfile.ZipFile("a.zip") as z:
    for info in z.infolist():
        print(info.filename, info.file_size, info.compress_size)
 
with tarfile.open("a.tar.gz", "r:gz") as t:
    t.extractall("out/")   # ← 위험 (filter='data' 권장, Py3.12+)

What-if — 보안 위협 백화점

1) Zip Slip — Path Traversal 의 클래식

ZIP 안에 다음과 같은 엔트리가 있다면:

../../../../etc/cron.d/evil

순진한 파서는 unzip -d /tmp/upload/ 했을 때 /etc/cron.d/evil 을 만든다.

대응 (Python 예시):

import os, zipfile
 
def safe_extract(zf: zipfile.ZipFile, dest: str):
    dest = os.path.realpath(dest)
    for member in zf.infolist():
        target = os.path.realpath(os.path.join(dest, member.filename))
        if not target.startswith(dest + os.sep):
            raise ValueError(f"Zip slip detected: {member.filename}")
        zf.extract(member, dest)

Python 3.12+ tarfile.extractall(filter='data') 는 자동 검증.

2) Zip Bomb — 압축률 폭탄

42.zip — 42KB 가 풀면 4.5 PB. 구조: 5중 중첩 ZIP, 각 단계마다 16개 사본, 1.3 GB 의 0 으로 채워진 파일.

더 진화한 Non-Recursive Zip Bomb (David Fifield, 2019) — 한 번 풀어서 281 TB. Central Directory 의 압축률을 인위적으로 부풀려서.

대응:

  • 풀린 크기 한계 (예: 100 MB)
  • 압축률 한계 (예: 100배 초과 시 거부)
  • 풀면서 누적 카운트, 초과 시 abort

TAR 는 symlink 를 보존 → 다음 시퀀스가 가능:

1) tar 안의 첫 엔트리: link  →  symlink('/tmp/extracted/innocent', '/etc')
2) tar 안의 둘째 엔트리: /tmp/extracted/innocent/cron.d/evil
                       ↑ 풀어보면 실제 경로는 /etc/cron.d/evil

대응: tarfile.extractall(filter='data') (symlink 무시), 또는 직접 검증.

ZIP 은 hard link 가 없지만, TAR 는 가능. ZIP 도 일부 구현은 동일 파일을 다른 이름으로 가리키는 트릭이 가능.

5) 큰 파일 (4 GB 한계) — ZIP64

옛 ZIP 은 32bit 크기 필드 → 4 GB 한계. ZIP64 (2001) 가 64bit 확장. 옛 도구로 풀면 깨짐. 확인: unzip -v 출력 끝에 Zip64 표시.

6) 인코딩 — 파일명 깨짐

ZIP 의 파일명은 본래 CP437. 한국어 파일명을 압축하면 → 윈도우는 CP949, 맥/리눅스는 UTF-8 → 서로 깨짐. ZIP 2003+ 는 UTF-8 플래그(bit 11) 추가. 그래도 호환성 문제 잦음. 대응: unzip -O cp949 a.zip 으로 강제 인코딩.

# Mac 에서 한국어 ZIP 깨질 때
$ unzip -O cp949 韓國.zip

7) Encrypted ZIP

  • Legacy ZipCrypto (1989): 알려진 평문 공격으로 깨짐. 사용 금지.
  • AES-256 (WinZip 9+, 2003): 안전하지만 모든 도구가 지원하진 않음.
  • 7z 의 AES-256: 헤더까지 암호화 가능 → 파일명도 가려짐.

Insight — 흥미로운 이야기

“ZIP 의 발명자 Phil Katz 와 ARC 분쟁”

1986년 SEA 사의 ARC 가 BBS 표준이었음. Phil Katz 가 더 빠른 호환 구현 PKARC 를 만들자 SEA 가 소송. Katz 는 1989년 완전히 새로운 ZIP 포맷을 발표하며 사양을 공개. 압도적 호환성으로 ARC 를 대체. 당시 PKWARE 의 광고 카피: “You will use ZIP”. Katz 본인은 알코올 중독으로 2000년에 사망 (37세).

“왜 .tar.gz 가 표준이 되었나”

1992년 GNU tar 가 -z 옵션으로 gzip 을 자동 호출. 같은 시기 인터넷이 폭발 → Linux 패키지 배포 표준 → .tar.gz 가 사실상 ㄹ소프트웨어 운반의 기본. 30년이 지난 지금도 거의 모든 Linux 소스 배포는 .tar.gz 또는 .tar.xz.

“7z 는 왜 그렇게 잘 압축되는가”

두 가지 트릭:

  1. LZMA 의 큰 사전 — gzip 의 32KB vs LZMA 의 4GB 사전. 멀리 떨어진 반복도 잡음.
  2. Solid mode — 모든 파일을 한 스트림으로. 파일 간 패턴 공유. 단점: 한 파일만 꺼내려 해도 수 GB 디코드. 그래서 백업에는 좋고 라이브러리에는 나쁨.

“Zip Bomb 의 방어 전략 진화”

1990년대: 단순 크기 한계 (50MB 풀면 abort). 2000년대: 풀어보지 않고 Central Directory 의 압축률만 검사 → 우회됨. 2019: David Fifield 의 non-recursive bomb 등장 → 풀어보면서 카운트 + 압축률 둘 다 검사. 결국 stream-based decode + budget 이 정답.


요약 + Mermaid

아카이브의 본질은 컨테이너 ↔ 압축의 분리. ZIP 은 둘을 합쳤고 (랜덤 액세스 OK), TAR+GZ 는 분리했고 (스트림 친화), 7z 는 solid 로 압축률 우위. 모든 처리기는 Zip Slip · Zip Bomb · symlink traversal · 인코딩 4 가지 함정을 정적으로 막아야 한다.