본문 바로가기

업무 자동화 · 도구 검증 · 서비스 운영

반복 업무를 줄이는 방법,
직접 시험하고 기록합니다.

예약 명단 정리부터 알림 자동화, 앱 운영까지.
원본 예제와 확인표, 실험 결과를 함께 나눕니다.

첫 번째 실험 · 예약 명단

이름이 같으면
같은 예약일까요?

B102 · 방문자B9월 10일
B103 · 방문자B9월 11일

다른 예약입니다. 둘 다 남겨야 합니다.

가상 명단 12행으로 확인한 결과 →
업무 자동화

파이썬 중복 파일 찾기: SHA-256으로 목록만 확인하기

by 코딩히어로 2026. 9. 12.
300x250
반응형

이름이 다른 파일도 내용이 같을 수 있습니다. 중복 파일을 정리하기 전에는 파일 크기와 내용의 SHA-256 값을 비교해 후보 목록을 만드는 방법을 쓸 수 있습니다. 아래 예제는 파일을 읽고 목록만 출력합니다. 삭제나 이동은 하지 않습니다.

사진 백업이나 문서 사본을 점검할 때 쓸 수 있는 작은 스크립트입니다. 같은 내용의 파일 두 개, 내용이 다른 파일 한 개, 빈 파일 두 개를 만든 뒤 로컬 Python 3.14.6에서 결과를 확인했습니다.

1. 이름 대신 크기와 내용을 비교하는 이유

비교 항목알 수 있는 것
파일명이름이 같은지
파일 크기바이트 수가 같은지
SHA-256내용이 같은지 판단할 강한 단서

크기가 다르면 바이트 내용도 다릅니다. 크기가 같다고 내용까지 같은 것은 아니므로 같은 크기끼리 해시를 계산합니다. SHA-256이 같은 파일은 중복 후보로 묶지만, 삭제 판단 전에는 파일의 용도와 보존 위치도 확인해야 합니다.

여기서 말하는 중복은 바이트 기준입니다. 같은 사진이라도 재압축하거나 메타데이터를 수정하면 값이 달라질 수 있습니다. 화면에서 비슷하게 보이는 사진을 찾는 작업과는 비교 기준이 다릅니다.

2. 검사 전용 스크립트 만들기

새 작업 폴더에 samples 폴더를 만들고, 검사할 파일의 사본을 넣습니다. 아래 코드를 find_duplicates.py로 저장합니다. 검사 중인 파일을 다른 프로그램이 수정하지 않는 상태에서 실행하세요.

from pathlib import Path
from collections import defaultdict
import hashlib

root = Path("samples")
if not root.is_dir():
    raise SystemExit("samples 폴더를 확인하세요.")

by_size = defaultdict(list)
for path in sorted(root.rglob("*")):
    if path.is_symlink() or not path.is_file():
        continue
    by_size[path.stat().st_size].append(path)

def digest(path):
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()

groups = 0
for size, paths in sorted(by_size.items()):
    if len(paths) < 2:
        continue
    by_hash = defaultdict(list)
    for path in paths:
        by_hash[digest(path)].append(path)
    for matches in by_hash.values():
        if len(matches) > 1:
            groups += 1
            print(f"그룹 {groups}: {size} bytes")
            for path in matches:
                print(" ", path)

print(f"중복 후보 그룹: {groups}")

파일 전체를 한 번에 메모리에 올리지 않고 1MiB씩 읽습니다. 같은 크기의 파일이 여러 개일 때만 해시를 계산하지만, 후보 파일의 내용은 끝까지 읽습니다. 대용량 폴더에서는 디스크 읽기 시간이 필요합니다.

심볼릭 링크 파일은 제외합니다. 이 예제는 일반적인 로컬 폴더 사본용이며, 동시에 변경되는 폴더나 특수한 파일시스템의 완전한 스냅샷을 만들지는 않습니다. 읽기 권한이나 접근 오류가 나면 실행이 중단되므로 끝까지 완료됐는지 확인합니다.

3. 작은 파일로 결과부터 확인하기

처음에는 실제 문서 대신 다음 연습 파일로 확인할 수 있습니다. 새 폴더에서 실행하며, 같은 이름이 있으면 xb 모드가 덮어쓰기를 막습니다.

from pathlib import Path

root = Path("samples")
root.mkdir(exist_ok=True)
files = {
    "a.txt": b"sample\n",
    "b.txt": b"sample\n",
    "c.txt": b"change\n",
    "empty-a.txt": b"",
    "empty-b.txt": b"",
}
for name, content in files.items():
    with (root / name).open("xb") as f:
        f.write(content)
python3 find_duplicates.py

확인 결과는 빈 파일 두 개가 한 그룹, a.txt와 b.txt가 한 그룹으로 총 2개였습니다. c.txt는 크기가 7바이트로 a.txt와 같지만 내용이 달라 묶이지 않았습니다. 이 차이를 보면 파일 크기만 비교할 때와 해시까지 비교할 때의 역할이 드러납니다.

4. 목록을 해석할 때 남겨 둘 기준

후보 두 개 중 어느 파일을 남길지는 해시가 정해 주지 않습니다. 문서의 공식 보관 위치, 프로그램이 참조하는 경로, 백업 정책을 확인한 뒤 정리 대상을 정합니다. 서로 다른 경로가 같은 실제 파일을 가리키는 하드 링크일 수도 있어, 목록의 크기를 더한 값이 곧 회수 가능한 공간은 아닙니다.

관측 결과다음 확인
빈 파일끼리 묶임프로그램이 사용하는 표시 파일인지
백업 사본끼리 묶임복구용으로 따로 보관해야 하는지
내용은 같고 경로는 다름각 경로를 참조하는 작업이 있는지

저장 공간 확인이 목적이라면 리눅스에서 큰 파일을 찾는 순서로 먼저 범위를 줄일 수 있습니다. 정리 후 파일시스템 사용량이 예상과 다르면 df와 du가 다른 경우도 구분해 살펴보세요.

자주 묻는 질문

해시가 같으면 바로 삭제해도 되나요?

이 스크립트는 중복 후보를 찾습니다. 보관 목적과 경로 의존성까지 확인하지는 않습니다. 중요한 파일은 바이트 단위 재비교와 백업 확인을 거친 뒤 별도로 정리합니다.

파일명에 한글이나 공백이 있어도 되나요?

pathlib가 경로 객체로 파일을 열기 때문에 공백으로 이름을 쪼개지 않습니다. 화면에 출력된 경로와 실제 파일을 함께 확인하세요. 경로 표시 환경에 따른 문자 표현은 별도입니다.

사진 내용이 비슷한데 목록에 없어요.

압축률, 해상도, 메타데이터가 달라 바이트가 달라졌을 수 있습니다. 시각적으로 비슷한 사진은 이미지 유사도처럼 다른 비교 기준이 필요합니다.

구현에 사용한 기능은 hashlib 공식 문서와 pathlib 공식 문서를 기준으로 했습니다. 검증 범위는 위의 작은 연습 파일이며, 사용자 문서 폴더를 전수 검사한 결과는 아닙니다.

300x250
반응형

댓글