백업 디스크나 NAS에 쌓아 둔 자료는 평소에 열어 보지 않기 때문에, 파일이 조용히 깨지거나(비트 로트) 누가 실수로 지워도 한참 뒤에야 알게 됩니다. 파일마다 SHA-256 해시를 기록해 두고 주기적으로 비교하면 이런 변화를 바로 잡아낼 수 있습니다. 표준 라이브러리만 쓰는 스크립트라 파이썬 3.8 이상이면 추가 설치 없이 동작합니다.
1. 동작 방식
- 첫 실행: 폴더 안 모든 파일의 크기, 수정 시각, SHA-256을
manifest.json에 저장 (기준점) - 이후 실행: 다시 계산해서 기준과 비교하고 4가지로 분류해 보고
| 분류 | 판단 기준 | 의미 |
|---|---|---|
| missing | 기준에는 있는데 지금 없음 | 삭제·이동됨 |
| added | 지금 새로 생김 | 새 파일 |
| changed | 해시와 수정 시각이 모두 바뀜 | 정상적인 편집일 가능성 높음 |
| corrupted | 수정 시각은 그대로인데 해시만 바뀜 | 손상 의심, 즉시 확인 필요 |
2. 전체 코드
"""폴더 무결성 점검: 첫 실행은 기준(manifest) 생성, 이후 실행은 변경/누락/신규 파일 보고."""
import hashlib, json, sys, time
from pathlib import Path
CHUNK = 1024 * 1024 # 1MB씩 읽어 대용량 파일도 메모리 부담 없음
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
while block := f.read(CHUNK):
h.update(block)
return h.hexdigest()
def scan(root: Path) -> dict:
result = {}
for p in root.rglob("*"):
if p.is_file():
st = p.stat()
result[str(p.relative_to(root))] = {"size": st.st_size, "mtime": int(st.st_mtime), "sha256": sha256(p)}
return result
def main(root: str, manifest: str = "manifest.json"):
root_p, man_p = Path(root), Path(manifest)
now = scan(root_p)
if not man_p.exists():
man_p.write_text(json.dumps(now, ensure_ascii=False, indent=1), encoding="utf-8")
print(f"기준 생성: 파일 {len(now)}개 → {man_p}")
return 0
base = json.loads(man_p.read_text(encoding="utf-8"))
missing = sorted(set(base) - set(now))
added = sorted(set(now) - set(base))
changed, corrupted = [], []
for k in set(base) & set(now):
if base[k]["sha256"] != now[k]["sha256"]:
# 수정 시각이 그대로인데 내용만 바뀌었다면 손상(비트 로트) 의심
(corrupted if base[k]["mtime"] == now[k]["mtime"] else changed).append(k)
report = {"time": time.strftime("%Y-%m-%d %H:%M:%S"), "missing": missing, "added": added,
"changed": sorted(changed), "corrupted": sorted(corrupted)}
print(json.dumps(report, ensure_ascii=False, indent=1))
return 1 if (missing or corrupted) else 0
if __name__ == "__main__":
sys.exit(main(*sys.argv[1:]))
파일을 1MB씩 나눠 읽기 때문에 수십 GB짜리 영상 파일도 메모리를 거의 쓰지 않습니다. 누락이나 손상 의심이 있으면 종료 코드 1을 돌려줘서, 다른 스크립트나 스케줄러에서 실패로 감지할 수 있습니다.
3. 실행 방법
# 첫 실행: 기준 생성 (manifest는 점검 대상 폴더 밖에 두기)
python integrity_check.py "D:\Backup\Photos" "C:\check\photos_manifest.json"
# 다음부터는 같은 명령으로 비교 결과 출력
python integrity_check.py "D:\Backup\Photos" "C:\check\photos_manifest.json"
manifest 파일을 점검 폴더 안에 두면 그 파일 자체가 매번 “변경”으로 잡히므로 반드시 바깥에 둡니다. 정상적으로 파일을 추가·수정한 뒤에는 manifest를 지우고 다시 실행해 기준을 새로 만듭니다.
4. 매일 자동 실행
윈도우 작업 스케줄러: 관리자 명령 프롬프트에서 등록합니다. 결과는 로그 파일에 쌓이도록 합니다.
schtasks /Create /TN "IntegrityCheck" /SC DAILY /ST 03:00 /TR "cmd /c python C:\check\integrity_check.py D:\Backup\Photos C:\check\photos_manifest.json >> C:\check\integrity.log 2>&1"
리눅스·NAS(cron): crontab -e에 추가합니다.
0 3 * * * /usr/bin/python3 /opt/check/integrity_check.py /volume1/photos /opt/check/photos_manifest.json >> /opt/check/integrity.log 2>&1
5. 활용 팁
- 대용량 폴더: HDD 기준 대략 100~200MB/s로 해시를 계산하므로 수 TB라면 몇 시간이 걸립니다. 폴더를 나눠 요일별로 점검하면 부담이 줄어듭니다.
- 알림: 종료 코드가 1일 때만 메일이나 메신저 웹훅을 보내도록 감싸면, 문제가 있을 때만 알림을 받을 수 있습니다.
- 원본·백업 비교: 원본 폴더로 만든 manifest를 백업 폴더 점검에 그대로 쓰면, 백업이 원본과 똑같이 복사됐는지 검증할 수 있습니다.