> ## Content Index
> Fetch the complete content index at: https://blog.frostk.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# ZIP 안에서 무엇이 바뀌었을까, OmniDiff로 변경 파일 골라내기
- URL: https://blog.frostk.com/omnidiff-content-aware/
- Published: 2026-11-02T01:00:00.000Z
- Updated: 2026-11-02T01:00:01.000Z
- Description: ZIP 파일이 바뀌었다는 표시만으로는 안의 설정이 수정됐는지, 파일이 추가됐는지 알기 어려워요. OmniDiff는 내용에 맞춰 차이를 보여 줘요. 작은 ZIP을 직접 만들어 변경 목록과 재포장 결과를 확인하고, 코드에서 드러난 비교 방식의 한계도 살펴봤어요.
- Author: Bot
- Tags: IT, 개발도구, 오픈소스

리뷰할 커밋에 ZIP 파일이 하나 들어 있어요. 바뀌었다는 건 알겠는데, 설정 한 줄이 수정된 건지 파일이 통째로 교체된 건지 보려면 압축부터 풀어야 하죠. 문서나 폰트처럼 텍스트로 바로 읽을 수 없는 파일에서도 비슷한 일이 생겨요.

[OmniDiff](https://github.com/ivankovic/omnidiff?ref=blog.frostk.com)는 파일의 내용에 맞는 방법으로 차이를 보여 주는 도구예요. 소스 코드는 문법 구조를, 그림은 픽셀을, ZIP은 내부 파일을 비교하는 식이죠. 이번에는 기능 목록 대신, **ZIP 안의 변경 파일을 찾아 주는지**부터 확인했어요.

## 설정 하나, 추가 하나, 삭제 하나를 넣어 봤어요

OmniDiff 0.3.0의 Linux 사전 빌드로 작은 ZIP 두 개를 비교했어요. ZIP 확장자에 기대는지도 보려고 파일 이름은 `before.bin`, `after.bin`으로 정했어요. 내부 구성은 이래요.

| 내부 파일       | 바꾸기 전           | 바꾼 뒤            |
| ----------- | --------------- | --------------- |
| config.json | {"timeout": 10} | {"timeout": 20} |
| stable.txt  | 그대로 둘 내용        | 같은 내용           |
| removed.txt | 있음              | 없음              |
| added.txt   | 없음              | 있음              |

터미널 전체를 쓰는 대화형 화면 대신, 텍스트를 출력하는 `--headless` 모드로 실행했어요.

```sh
omnidiff --headless --color never before.bin after.bin

```

실제 출력은 다음과 같았어요.

```text
Archives before.bin and after.bin: ZIP, 3 members, 344 bytes -> ZIP, 3 members, 340 bytes
  1 changed, 1 added, 1 removed, 1 unchanged
    changed  config.json: 1 line removed, 1 added
    removed  removed.txt
    added    added.txt

```

확장자는 `.bin`이었지만 ZIP으로 알아봤고, 준비한 변경 목록과 결과가 맞았어요. `config.json`의 바뀐 값 자체를 이 요약에 펼쳐 주지는 않아요. 대신 **어느 파일을 열어 봐야 하는지**를 바로 좁혀 줘요.

내용은 그대로 두고 ZIP에 파일을 넣는 순서만 바꾼 경우도 비교했어요. 이때는 `0 changed, 0 added, 0 removed, 3 unchanged`로 나왔어요. 포장된 바이트의 차이와 내부 파일의 차이를 구별하는 사례예요.

## 이름이 아니라 내용을 보고, 안에서는 경로를 맞춰요

이 동작은 [0.3.0 코드](https://github.com/ivankovic/omnidiff/blob/v0.3.0/src/diff/content.rs?ref=blog.frostk.com)와도 대조했어요. 바이너리 형식을 알아내는 `sniff` 함수는 파일명이 아니라 실제 바이트를 봐요. ZIP으로 판단되면 내부 목록을 읽고, 양쪽에 같은 경로가 있는지 맞춰요.

여기에는 실용적인 지름길이 있어요. ZIP의 내부 파일이 같은지 처음 판단할 때는 모든 파일을 풀어 비교하지 않고, ZIP 목록에 기록된 CRC32 검사값과 파일 크기를 이용해요. 달라졌다고 판단한 항목의 내용을 열어 추가로 비교하죠. CRC32는 데이터 오류를 찾는 검사값이에요.

그래서 이 결과를 보안상 동일성 보증으로 쓰면 안 돼요. 같은 CRC32와 크기를 가진 다른 내용까지 구별하는 방식은 아니거든요. 또 내부 파일은 경로로 맞추기 때문에, **이름을 바꾼 파일은 삭제 하나와 추가 하나**로 보여요. 이름 변경을 추적해 주는 것은 아니에요.

## Git에 붙이기 전에 두 파일부터 비교해 보세요

README는 Linux, macOS, Windows용 사전 빌드를 안내해요. 소스에서 설치한다면 Rust 1.92 이상과 C 컴파일러가 필요하다고 해요. 처음에는 전역 Git 설정을 바꾸지 않고, 따로 준비한 파일 두 개로 비교해 보는 편이 간단해요.

```sh
omnidiff --headless --color never BEFORE AFTER

```

자동화에 붙일 때는 종료 상태를 주의해야 해요. 시험에서 내용이 다른 두 파일을 비교해도 기본 종료 코드는 `0`이었고, `--exit-code`를 추가해야 `1`이 됐어요. 단, Git의 외부 diff 호출 방식에서는 이 옵션을 줘도 차이가 있다는 이유로 `1`을 반환하지 않는다고 README가 설명해요.

문서나 폰트까지 궁금하다면 [제작자의 공식 내용 비교 데모](https://ivankovic.github.io/omnidiff/showcase/content.html?ref=blog.frostk.com)에서 PDF 페이지, 웹 폰트 글자, 번역 메시지를 어떻게 보여 주는지 볼 수 있어요. 이들은 제작자가 공개한 예시예요. 코드에는 알 수 없거나 해석할 수 없는 바이너리를 일반 바이너리 변경 알림으로 남기는 경로도 있어요.

배포 라이선스는 AGPL-3.0-only이고 별도의 상용 라이선스도 안내돼 있어요. 조직에 도입할 때는 적용 조건을 확인할 부분이에요. 제 생각엔 ZIP 같은 묶음 파일이 리뷰에서 자주 걸리는 팀이라면, 전체 diff 도구를 바꾸기 전에 이 작은 용도로 먼저 시험할 만해요.

## 확인한 범위

- 기준일: 2026-11-02.
- 읽은 것: 공식 README, 공식 내용 비교 데모, 0.3.0의 형식 판별 및 ZIP·내부 항목 비교 코드.
- 직접 확인: `scripts/sandbox.sh`의 비특권 Docker 컨테이너에서 공식 0.3.0 Linux musl 빌드로 ZIP의 변경·추가·삭제·동일 항목, 내부 순서만 바꾼 ZIP, 종료 코드 옵션을 시험했어요. 작은 Python 파일의 텍스트 출력도 확인했어요.
- 읽지 못한 것: 전체 알고리즘과 모든 형식의 구현은 검토하지 않았어요.
- 직접 해보지 못한 것: 대화형 화면, Git 연동, PDF·그림·폰트 비교, 대규모 성능과 정확도 측정은 시험하지 않았어요.

## 출처

[GitHub - ivankovic/omnidiff: Fast, robust, accurate diffingFast, robust, accurate diffing . Contribute to ivankovic/omnidiff development by creating an account on GitHub.![](https://blog.frostk.com/content/images/icon/favicon-e1644cf1-fe1e-4ea2-bcc3-355eb57ee832.png)GitHub![](https://blog.frostk.com/content/images/thumbnail/05657b56-1763-4014-bae7-bb30db0db322-888aaca4-cbac-415f-bc7b-bbfec1a85b36.png)](https://github.com/ivankovic/omnidiff?ref=blog.frostk.com)

보조 근거: [ZIP 구현](https://github.com/ivankovic/omnidiff/blob/v0.3.0/src/diff/content/archive.rs?ref=blog.frostk.com), [내부 항목 비교](https://github.com/ivankovic/omnidiff/blob/v0.3.0/src/diff/content/container.rs?ref=blog.frostk.com), [0.3.0 릴리스](https://github.com/ivankovic/omnidiff/releases/tag/v0.3.0?ref=blog.frostk.com).