PhotoDedup - 사진 정리하다가 만든 도구를 열게 된 이야기
사진 정리는 미루기 쉽다.
휴대폰, iCloud, 백업 폴더, 다운로드 폴더를 오래 쓰다 보면 같은 사진이 여러 장 생긴다. 연사 사진처럼 거의 같은 장면도 많고, 편집본과 원본이 섞인다. 처음에는 "나중에 정리하지" 하고 넘기지만, 어느 순간부터는 폴더를 여는 것 자체가 일이 된다.
문제는 단순히 용량이 아니다.
비슷한 사진이 너무 많으면 어떤 걸 남겨야 할지 판단하는 데 시간이 오래 걸린다. 자동 삭제는 불안하고, 수동 정리는 지친다. 좋은 사진을 하나라도 잘못 지우면 되돌리기 어렵다는 부담도 있다.
그래서 처음에는 내 사진 정리용으로 PhotoDedup을 만들었다.
개인 도구로 시작했다
처음 목표는 거창하지 않았다.
내 로컬 폴더를 스캔해서 비슷한 사진을 묶고, 그중 어떤 사진을 남기면 좋을지 빠르게 판단할 수 있으면 됐다. "자동 삭제"가 아니라 "검토 속도 향상"이 핵심이었다.
사진 정리에서 제일 중요한 것은 공격적인 자동화가 아니었다. 실수하지 않는 흐름이었다.
목표
PhotoDedup의 목표는 단순하다.
중복과 유사 사진을 찾아주되, 지우는 결정은 사람이 하게 한다.
자동으로 싹 지우는 도구가 아니라, 검토와 정리를 도와주는 도구다.
- 로컬 폴더를 스캔한다
- perceptual hash로 중복/유사 사진을 그룹화한다
- 해상도, 파일 크기, 선명도, 메타데이터로 keep 후보를 추천한다
- 사용자가 직접 남길 사진과 정리할 사진을 고른다
- 삭제 대신 격리 폴더나 시스템 휴지통으로 보낸다
- 필요하면 격리한 파일을 복원한다
사진은 외부 서버로 업로드하지 않는다. 스캔 데이터는 로컬 SQLite 데이터베이스에 저장된다.
이 기준이 정해지고 나니, 개인 도구였던 코드를 조금 더 일반적인 형태로 정리할 수 있겠다는 생각이 들었다.
구조
PhotoDedup은 세 부분으로 나뉜다.
| Part | Stack | Role |
|---|---|---|
| Renderer | React + Vite | 검토 UI |
| Shell | Electron | 데스크톱 창, 파일 선택, sidecar 실행 |
| Engine | Python + FastAPI | 스캔, 해시, 그룹화, 썸네일, 정리 계획 |
Electron이 로컬 Python FastAPI sidecar를 127.0.0.1에서 띄우고, renderer는 preload bridge를 통해서만 sidecar와 통신한다.
Electron shell
|
+-- React renderer
|
+-- Python FastAPI sidecar
|
+-- local folders
+-- SQLite manifest
+-- thumbnails
사진 파일과 썸네일, 매니페스트는 모두 로컬에 남는다.
왜 데스크톱 앱인가
사진 정리 도구는 로컬 파일 접근이 핵심이다. 웹앱으로 만들면 폴더 접근, 대용량 썸네일 처리, 휴지통 이동, 복원 흐름이 애매해진다. 반대로 순수 Python CLI로만 만들면 검토 과정이 너무 불편하다.
Electron을 쓴 이유는 이 균형 때문이다.
- UI는 React로 빠르게 만든다
- 로컬 파일과 OS 통합은 Electron이 맡는다
- 이미지 처리와 해시 계산은 Python 엔진이 맡는다
- 외부 업로드 없이 로컬에서 끝낸다
완전히 네이티브 앱처럼 작게 만들지는 못하지만, 개발 속도와 기능 확장성은 좋다.
열어두기로 한 이유
개인적으로 만든 도구지만, 문제 자체는 흔하다.
iCloud 사진을 오래 쓰는 사람, 여러 PC에 백업을 나눠둔 사람, 여행 사진과 연사 사진이 많은 사람은 비슷한 고민을 한다. 이미 있는 상용 도구도 많지만, 로컬에서만 돌고 어떤 기준으로 묶였는지 확인할 수 있는 도구를 원하는 사람도 있을 것이다.
그래서 개인 환경에 묶인 부분을 걷어내고, README와 빌드 흐름을 정리해서 오픈소스로 열었다.
현재 공개 버전에서 가능한 것은 다음과 같다.
- duplicate / near-duplicate grouping
- HEIC/HEIF 지원
- 품질 기반 keep suggestion
- quarantine 또는 trash 이동
- quarantine restore
- group list cache 확인과 초기화
- 한국어, 영어, 일본어 UI 라벨
- Windows installer / portable build, Linux AppImage 배포
설치
최신 바이너리는 GitHub Releases에서 받을 수 있다.
https://github.com/lisyoen/photodedup/releases
개발 환경은 renderer, shell, engine을 각각 준비한다.
cd renderer
npm ci
npm run build
cd shell
npm ci
npm run build
cd engine
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
앞으로
PhotoDedup은 "사진을 대신 지워주는 앱"이 아니라 "정리 결정을 빨리 할 수 있게 돕는 앱"으로 유지하려 한다.
중복 사진 정리에서 제일 무서운 것은 실수로 좋은 사진을 잃는 것이다. 그래서 추천은 하되, 되돌릴 수 있는 흐름과 사람이 확인하는 단계를 계속 중요하게 둘 생각이다.
개인 필요에서 시작했지만, 같은 문제를 가진 사람에게 출발점이 되면 좋겠다.
Repository: