====== _Uncertain 재분류 절차 가이드 ======
생성일: 2026-02-23
===== 개요 =====
이 문서는 TakeoutPhotoSanitizer 실행 후 _Uncertain 폴더에 분류된 파일을
SHA-256 원본을 변경하지 않고 안전하게 재분류하는 절차를 설명한다.
핵심 원칙:
* 기존 SHA-256.txt는 절대 수정하지 않는다.
* 파일 바이트(이미지 내용)는 절대 수정하지 않는다.
* 재분류는 별도의 오버라이드(override) 기록 파일로 관리한다.
* 향후 앨범, 인물, 장소 등 확장 가능성을 고려한다.
-----
===== 0단계 – 운영 폴더 생성 =====
다음과 같이 From_Google_Takeout과 같은 레벨로 _ops 및 하위 폴더를 생성한다.
(작업 디렉토리)
└─ Photos_Backup\
├─ From_Google_Takeout\
│ ├─ 2019\
│ ├─ 2020\
│ ├─ ...
│ └─ SHA-256.txt ← hash DB
└─ _ops\
├─ reclass\
└─ notes\
이 폴더 및 그 하위에는 운영 및 관리 파일만 저장한다.
-----
===== 1단계 – _Uncertain 목록 추출 =====
PowerShell 명령:
$unc = "From_Google_Takeout\_Uncertain"
$out = "_ops\reclass\uncertain_review.csv"
if (!(Test-Path -LiteralPath $unc)) {
throw "_Uncertain 폴더가 없습니다: $unc"
}
Get-ChildItem -LiteralPath $unc -File -Recurse |
Select-Object FullName, Name, Length, LastWriteTime |
Export-Csv -LiteralPath $out -NoTypeInformation -Encoding UTF8
생성 파일:
Photos_Backup\_ops\reclass\uncertain_review.csv
-----
===== 2단계 – 수동 검토 =====
엑셀에서 uncertain_review.csv를 열고 다음 열을 추가한다.
* year_final (확정 연도, 예: 2014)
* note (판단 근거 메모)
확신이 있는 파일만 year_final을 채운다.
저장 파일:
Photos_Backup\_ops\reclass\uncertain_review_done.csv
-----
===== 3단계 – 연도 오버라이드 파일 생성 =====
PowerShell 스크립트:
# 작업 디렉토리(= Photos_Backup)에서 실행한다고 가정
$in = "_ops\reclass\uncertain_review_done.csv"
$out = "_ops\reclass\year_override.tsv"
# 입력 파일 확인
if (!(Test-Path $in)) {
throw "입력 파일이 없습니다: $in"
}
# 출력 폴더가 없으면 생성
$outDir = Split-Path -Parent $out
if (!(Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir | Out-Null }
Import-Csv $in |
ForEach-Object {
# year_final 정리(공백 제거)
$yy = ("" + $_.year_final).Trim()
# 확정 연도(4자리)만 처리
if ($yy -notmatch '^\d{4}$') { return }
# FullName 확인
$src = $_.FullName
if ([string]::IsNullOrWhiteSpace($src)) { return }
if (!(Test-Path -LiteralPath $src)) { return }
# note 정리 (TSV 깨짐 방지: 탭/개행 제거)
$note = ("" + $_.note)
$note = $note -replace "`t", " "
$note = $note -replace "(\r\n|\n|\r)", " "
$note = $note.Trim()
# sha256 계산
$h = (Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash
# TSV 한 줄 출력
"{0}`t{1}`t{2}`t{3}" -f $h, $yy, (Get-Date -Format "yyyy-MM-dd"), $note
} | Set-Content -LiteralPath $out -Encoding UTF8
파일 형식:
sha256year_finaldatenote
이 파일은 “수동 재분류 패치 기록”이다. 매 실행 시 재생성(덮어쓰기) 된다.
-----
===== 4단계 – 실제 파일 이동 =====
파일을 해당 연도 폴더로 이동한다.
# 작업 디렉토리(= Photos_Backup)에서 실행한다고 가정
$in = "_ops\reclass\uncertain_review_done.csv"
$rootMedia = "From_Google_Takeout"
$log = "_ops\reclass\move_log.tsv"
# 입력 파일 확인
if (!(Test-Path -LiteralPath $in)) {
throw "입력 파일이 없습니다: $in"
}
# Media 루트 확인
if (!(Test-Path -LiteralPath $rootMedia)) {
throw "Media 루트 폴더가 없습니다: $rootMedia"
}
# 로그 폴더가 없으면 생성
$logDir = Split-Path -Parent $log
if (!(Test-Path -LiteralPath $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
# 카운터(요약 출력용)
$cnt_total = 0
$cnt_skip = 0
$cnt_moved = 0
$cnt_failed = 0
Import-Csv -LiteralPath $in |
ForEach-Object {
$cnt_total++
# year_final 정리(공백 제거)
$yy = ("" + $_.year_final).Trim()
if ($yy -notmatch '^\d{4}$') { $cnt_skip++; return }
# 원본 파일 경로 확인
$src = $_.FullName
if ([string]::IsNullOrWhiteSpace($src)) { $cnt_skip++; return }
if (!(Test-Path -LiteralPath $src)) { $cnt_skip++; return }
# 안전장치: _Uncertain 내부 파일만 이동(의도치 않은 이동 방지)
# (대소문자 무시, 경로 구분자 혼용을 감안해 \\ 와 / 모두 허용)
if ($src -notmatch '[\\/]+From_Google_Takeout[\\/]+_Uncertain[\\/]+') { $cnt_skip++; return }
# 목적 연도 폴더 생성
$dstDir = Join-Path $rootMedia $yy
if (!(Test-Path -LiteralPath $dstDir)) { New-Item -ItemType Directory -Path $dstDir -Force | Out-Null }
# 목적 파일명 (CSV의 Name 필드 우선, 없으면 src에서 파일명 추출)
$name = $_.Name
if ([string]::IsNullOrWhiteSpace($name)) { $name = [System.IO.Path]::GetFileName($src) }
$dst = Join-Path $dstDir $name
# 동일 파일명 충돌 시 sha256 prefix로 회피
if (Test-Path -LiteralPath $dst) {
$h8 = (Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.Substring(0,8)
$dst = Join-Path $dstDir ("{0}_{1}" -f $h8, $name)
}
$status = "moved"
try {
Move-Item -LiteralPath $src -Destination $dst -ErrorAction Stop
$cnt_moved++
}
catch {
$status = "move_failed: " + ($_.Exception.Message -replace "(\r\n|\n|\r)", " " -replace "`t"," ")
$cnt_failed++
}
# 로그 기록(탭/개행 제거)
$srcLog = ($src -replace "`t"," " -replace "(\r\n|\n|\r)"," ")
$dstLog = ($dst -replace "`t"," " -replace "(\r\n|\n|\r)"," ")
# 로그 형식: time, year_final, src, dst, status
"{0}`t{1}`t{2}`t{3}`t{4}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $yy, $srcLog, $dstLog, $status |
Add-Content -LiteralPath $log -Encoding UTF8
}
# 요약 출력
Write-Host ("[MOVE] total={0} moved={1} failed={2} skipped={3} log={4}" -f $cnt_total, $cnt_moved, $cnt_failed, $cnt_skip, $log)
이 과정은 파일 바이트를 변경하지 않으므로 SHA-256은 유지된다.
-----
===== (선택) 향후 확장을 위한 주석 파일 =====
생성 가능 파일:
Photos_Backup\_ops\notes\annotations.tsv
형식:
sha256keyvaluedate
예:
AAA... place Jeju Seongsan 2026-02-23
AAA... people Mom;Dad 2026-02-23
이 구조는 향후 앨범, 인물, 장소, 키워드 확장에 사용된다.
-----
===== 반복 운영 루프 =====
- _Uncertain 검토
- year_final 입력
- year_override.tsv 생성
- 파일 이동
- _Uncertain 감소 확인
- 반복
-----
===== 설계 철학 =====
* 파일은 불변 객체이다.
* SHA-256은 영구 식별자이다.
* 재분류는 메타데이터이다.
* 앨범 및 주석은 관계 레이어이다.
문서 끝