파일 이름이 같아도 원문이 바뀌었는지 SHA256으로 확인하는 실험

작성 2026.10.10

파일 이름이 같아도 내용이 바뀔 수 있으므로, 개정은 논리 이름과 원문 바이트의 SHA256을 함께 사용해 연결할 수 있습니다. 아래 실험은 회의실 A→B→B 입력에서 새 개정, 새 개정, 동일 내용으로 판정하고 두 버전을 보존합니다.

주황색 로봇과 모양이 다른 두 종이, 폴더를 그린 삽화
파일 원문을 비교하는 모습을 표현한 AI 삽화입니다. 실제 앱 화면이 아닙니다.

같은 이름 아래 바뀐 내용을 기록하기

policy.txt라는 이름만 기록하면 이전 내용과 현재 내용을 구분하기 어렵습니다. 이 예제는 같은 논리 이름 아래 해시가 달라지면 개정을 추가하고, 이미 기록한 조합이면 unchanged를 반환합니다.

자작 입력은 ‘검토 장소: 회의실 A’와 ‘검토 장소: 회의실 B’이며, 각각 끝에 줄바꿈이 있습니다. 두 번째 내용을 한 번 더 넣어 중복 입력이 개정 수를 늘리는지도 확인합니다.

입력 순서 논리 이름 내용 관찰된 판정 개정 수
1 policy.txt 검토 장소: 회의실 A new_revision 1
2 policy.txt 검토 장소: 회의실 B new_revision 2
3 policy.txt 검토 장소: 회의실 B unchanged 2

복사해 실행할 전체 코드

아래 코드를 source_revision_demo.py로 저장하고 실행합니다. 원문 바이트는 해시를 이름으로 하는 .bin 파일에 저장하고, 개정 연결 정보는 revisions 사전에 담습니다.

import hashlib
import json
from pathlib import Path
from tempfile import TemporaryDirectory


def capture(folder, logical_name, content, revisions):
    digest = hashlib.sha256(content).hexdigest()
    key = (logical_name, digest)
    if key in revisions:
        return 'unchanged', digest
    path = folder / (digest + '.bin')
    if path.exists():
        assert path.read_bytes() == content
    else:
        path.write_bytes(content)
    revisions[key] = str(path)
    return 'new_revision', digest


with TemporaryDirectory(prefix='source-revision-demo-') as temporary:
    folder = Path(temporary)
    revisions = {}
    v1 = '검토 장소: 회의실 A\n'.encode('utf-8')
    v2 = '검토 장소: 회의실 B\n'.encode('utf-8')
    # Synthetic source text; this is not an actual meeting record.
    for content in (v1, v2, v2):
        status, digest = capture(folder, 'policy.txt', content, revisions)
        print(status, digest, 'revisions=' + str(len(revisions)))
    assert len(revisions) == 2
    assert sorted(p.read_bytes() for p in folder.glob('*.bin')) == sorted([v1, v2])
    print('preserved_v1_and_v2=True')
python3 source_revision_demo.py

capture는 먼저 바이트의 해시를 계산하고 논리 이름과 묶어 조회합니다. 새 조합이면 원문을 저장하고 원장에 추가하며, 같은 해시 이름의 파일이 이미 있으면 실제 바이트가 같은지도 assertion으로 확인합니다.

관찰된 출력 읽기

이번 결과는 Python 3.14.5로 자작 텍스트를 로컬에서 한 번 실행한 기록입니다. 해시는 아래 출력과 같으며, 개정 수 1·2·2와 두 원문 보존을 assertion으로 확인했습니다.

new_revision e7543035077b46a79c6180591f36bb669a44f4bec79ff75dfbb413e6cda80014 revisions=1
new_revision bfa2e82e5ac05490768ce31b722f29f21b60512b5bd8ecea1fcb74f7dfa5434f revisions=2
unchanged bfa2e82e5ac05490768ce31b722f29f21b60512b5bd8ecea1fcb74f7dfa5434f revisions=2
preserved_v1_and_v2=True

첫 번째와 두 번째 입력은 장소를 나타내는 바이트가 달라 서로 다른 해시를 얻었습니다. 세 번째 입력은 두 번째와 같은 바이트이므로 새 원장 항목이 생기지 않았습니다.

마지막 확인은 실행 중 임시 폴더에 A와 B 원문이 모두 있었다는 뜻입니다. 같은 논리 이름으로 새 내용을 받아도 앞선 원문을 덮어쓰지 않는 동작을 이 작은 예제로 확인할 수 있습니다.

임시 실험을 운영 보관으로 착각하지 않기

이 코드는 TemporaryDirectory를 사용하므로 해당 블록을 빠져나가면 임시 파일이 삭제됩니다. revisions도 메모리에만 있는 사전이어서, 프로그램을 다시 실행했을 때 이전 개정을 복구하는 지속 보관 구현은 아닙니다.

운영에 적용하려면 원문별 영구 저장과 함께 논리 자료 ID, 버전 SHA, 수집 시각, 출처 URL을 기록하는 원장을 연동해야 합니다. 같은 파일 이름을 어떤 논리 자료에 연결할지도 정해야 서로 다른 자료의 이력을 혼동하지 않습니다.

해시가 달라졌다는 사실은 바이트가 달라졌다는 신호이며, 의미가 바뀌었는지나 출처가 믿을 만한지는 별도로 확인해야 합니다. 줄바꿈 방식이나 인코딩만 달라져도 해시는 바뀔 수 있습니다.

이 실험은 실제 운영, 동시 수집, 장애 후 복구를 검증하지 않았습니다. 대용량 파일에서는 전체 read_bytes 대신 청크 처리나 hashlib.file_digest를 고려할 수 있지만, 여기서는 성능과 메모리 사용을 측정하지 않았습니다.

공식 참고: Python hashlib 문서. sha256은 바이트를 입력받고 hexdigest는 16진수 다이제스트를 반환합니다. 해시는 내용 식별에 활용할 수 있지만 사실의 진위나 작성자를 입증하지 않습니다. 확인일: 2026-10-10.

질문과 정정

답글 남기기

댓글은 공개됩니다. 비밀번호·연락처 등 개인정보를 남기지 마세요.