Builds the "indexed library" foundation and first intelligent features on top of the organizer (sql.js index, non-destructive in-place indexing). Phase 0 — Library index: - sql.js (WASM SQLite) index DB; contentHash-keyed assets, resumable indexing (skip by path+mtime), batch persistence (chosen over native better-sqlite3 which fails to build on Node 24 / Python 3.12) - Library folders (in place, non-destructive) + background indexer w/ progress - Thumbnails generated in the AI worker (canvas->webp), cached in userData; served via photoai-media://thumb by hash; thumbnail grid w/ pagination Phase 1 — AI quality assessment & culling: - Focus (Laplacian variance), exposure (histogram), eyes-open (face-api EAR) computed in one analyze pass alongside the thumbnail - Culling filters (candidate/rejected) + quality badges - Adjustable thresholds (live SQL re-classification from stored raw scores, no re-analysis) + manual star rating (0-5) and color labels (usermeta) Phase 2 — CLIP natural-language / similarity search: - @huggingface/transformers (WASM/WebGPU, no native build) - CLIP image/text embeddings (lazy-loaded); Korean queries auto-translated via opus-mt-ko-en into the English CLIP - Embeddings stored as SQLite BLOBs; "build search index" batch w/ progress; brute-force cosine search; new Search tab - Note: models download from HF Hub on first use; fully-offline ORT-wasm packaging and KO search-accuracy tuning are follow-ups Tabs added (Organize / Library / Search). All typecheck/tests(12)/build green; boot smoke verified across phases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
NextGen Photo AI — 확장 기획 타당성 검토 & 로드맵
상태: 검토(Review) / 계획 초안 · 작성: 2026-06-01 대상 기획: "NextGen Photo AI — 지능형 사진 관리 솔루션" 현재 베이스: ARCHITECTURE.md (Electron + face-api 폴더 정리기)
0. 한 줄 결론
기술적으로 전부 로컬(On-device)로 구현 가능합니다. 단, 이는 기능 추가가 아니라 제품의 성격 전환입니다: 현재 "폴더를 1회 스캔해 인물/날짜로 이동"하는 무상태(stateless) 정리기 → 사진을 지속적으로 색인(index)하고 검색·평가·그룹화하는 상태 기반 라이브러리(DAM). → 핵심은 새 기능들이 아니라 그 아래 깔릴 "라이브러리 인덱스" 기반(임베딩 + 메타 + 썸네일 DB) 입니다. 이것부터 만들어야 나머지가 전부 그 위에 올라갑니다.
1. 기능별 타당성 (로컬 구현 관점)
| 기능 | 핵심 기술 | 로컬 가능성 | 난이도 | 비고 |
|---|---|---|---|---|
| [P1] AI 품질 평가 / Culling | 초점=라플라시안 분산, 노출=히스토그램, 감은 눈=face-api 랜드마크(EAR), 미학=NIMA(ONNX) | ✅ 높음 | 중 | 초점/노출/눈은 모델 없이 가능. 미학 점수만 ONNX 모델 필요 |
| [P1] 자연어 검색 | CLIP 이미지/텍스트 임베딩(transformers.js, ONNX, WebGPU) + 벡터 검색 | ✅ 가능 | 상 | 한국어 쿼리 처리 방식 결정 필요(§4-1) |
| [P1] 시각적 유사 검색 | 동일 CLIP 임베딩 코사인 유사도 | ✅ 높음 | 중 | 임베딩만 있으면 쉬움 |
| [P2] 스마트 그룹화 | 임베딩 클러스터링(k-means/임계값) | ✅ 높음 | 중 | Phase 2 임베딩 재사용 |
| [P2] 자가 정화(중복/저품질) | 지각적 해시(pHash/dHash) + 품질 점수 | ✅ 높음 | 중 | pHash는 순수 JS 구현 가능 |
| (시나리오 A) RAW 파일 | libraw/dcraw (WASM 또는 네이티브) | ⚠️ 가능하나 무거움 | 상 | 범위 포함 여부 결정 필요(§4-2) |
→ 품질 평가/Culling이 모델 리스크가 가장 낮고 가치가 높음 → 1순위로 출시 권장.
2. 필요한 기술 스택 추가
| 영역 | 후보 | 메모 |
|---|---|---|
| ML 런타임 | transformers.js v3 (@huggingface/transformers) | ONNX Runtime Web, WebGPU 가속 + WASM 폴백. 전부 로컬 |
| 임베딩 모델 | CLIP ViT-B/32(영어, ~경량) 또는 다국어 CLIP(jina-clip-v2 등, 무거움) | §4-1 결정에 따름 |
| 한국어 처리(옵션) | opus-mt-ko-en(쿼리 번역, 소형) | 영어 CLIP + 쿼리만 KO→EN 번역하는 절충안 |
| 미학 점수(옵션) | NIMA류 ONNX | 없으면 초점/노출/눈만으로 v1 |
| 인덱스 DB | better-sqlite3 (메타+점수+pHash) + 임베딩(BLOB 또는 별도 바이너리) | JSON은 수천 장에서 한계 → SQLite 필요 |
| 벡터 검색 | 초기 브루트포스 코사인(~5만 장까지 OK) → 대규모 시 sqlite-vec / hnswlib-wasm | 단계적 |
| 썸네일 | sharp(네이티브) 또는 canvas 리사이즈 + 디스크 캐시 | 그리드 UI 필수 |
| 지각 해시 | dHash/aHash 순수 JS | 중복 탐지 |
| RAW(옵션) | libraw-wasm / dcraw | §4-2 결정 |
모델 동봉 시 앱 용량이 수백 MB 증가합니다(CLIP
150350MB + 런타임). 로컬·오프라인 정책상 동봉 또는 최초 실행 시 1회 다운로드 방식 결정 필요.
3. 아키텍처 변화 (가장 중요한 부분)
현재 3-프로세스(Main / UI / 추론창)에 2개 축을 추가:
추가 1) Library Index (영속 상태)
SQLite DB: 파일별 { contentHash, path, exif, faces,
clipEmbedding(512d), qualityScores, pHash, thumbnailRef }
- 파일은 contentHash로 식별 → 경로가 바뀌어도 추적, 중복 인식
- "이동/복사" 정리기와 공존: 정리기도 이 인덱스를 활용
추가 2) AI Worker (백그라운드 색인기)
- 기존 '숨김 추론창' 패턴 확장 또는 Web Worker(+WebGPU)
- 신규/변경 파일을 배치로: CLIP 임베딩 + 품질 점수 + pHash 계산
- 재개 가능(resumable), UI 프리징 0 (별 프로세스/워커)
새 데이터 흐름:
임포트/스캔 → (AI Worker) 임베딩·점수·해시 계산 → Index DB 저장
검색: 쿼리 → (번역?) → 텍스트 임베딩 → DB 벡터 코사인 Top-K → 결과 그리드
Culling: Index의 품질 점수로 필터 → 후보군/제외 뷰
그룹화: 임베딩 클러스터링 → 클러스터 뷰
정화: pHash 근접쌍 + 저품질 → 정리 제안
비파괴(non-destructive) 색인이 핵심: 검색/평가/그룹화는 파일을 옮기지 않고 "제자리에서" 인덱싱합니다. 현재의 이동 기반 정리기와는 철학이 다르므로, 둘을 어떻게 공존시킬지(§4-3)가 설계 관건입니다.
4. 열린 질문 — 확정됨 (2026-06-01)
| # | 항목 | 결정 |
|---|---|---|
| 1 | 한국어 자연어 검색 | 영어 CLIP + 쿼리 KO→EN 번역(경량·빠름). Phase 2에서 정확도 실측 |
| 2 | RAW 지원 | 차후(Phase 4). 우선 JPEG/PNG/WebP |
| 3 | 라이브러리 모델 | 인덱스 기반 통합 — 비파괴 색인을 기본으로, 기존 이동/복사 정리기도 인덱스 활용 |
| 4 | 진행 범위 | Phase 0 + Phase 1 먼저 |
| 5 | 모델 배포 | (미정 — Phase 1은 모델 거의 불필요. Phase 2 진입 시 결정) |
| 6 | 타깃 규모 | 초기 수천~수만 장 가정(브루트포스 벡터검색으로 충분), 대규모는 Phase 4 |
5. 단계별 로드맵 (권장 시퀀스)
각 Phase는 독립 출시 가능하도록 설계.
- Phase 0 — 라이브러리 인덱스 기반 (필수 선행) SQLite 인덱스 + contentHash 식별 + 썸네일 캐시 + AI Worker 스캐폴드(재개 가능 배치). 화면 변화 없음, 토대만.
- Phase 1 — [P1] 품질 평가 & 스마트 Culling (첫 출시 권장) 초점(라플라시안)·노출(히스토그램)·감은 눈(face-api 랜드마크) 점수 → '고품질 후보 / 제외' 뷰. 모델 리스크 최저, 가치 즉시.
- Phase 2 — [P1] CLIP 임베딩 + 자연어/유사 검색 임베딩 색인 + 검색 UI(쿼리바·결과 그리드·"이 사진과 비슷한"). §4-1 결정 반영.
- Phase 3 — [P2] 스마트 그룹화 + 자가 정화 임베딩 클러스터링 + pHash 중복/저품질 정리 제안. Phase 1·2 산출물 재사용.
- Phase 4 — 옵션/결정 의존 RAW 지원, 미학(NIMA) 점수, 대규모용 ANN 인덱스(sqlite-vec/HNSW).
6. 리스크 & 솔직한 평가
- 규모: 이 확장은 현재 앱과 비슷하거나 더 큰 작업량입니다(수 주 단위, 다중 Phase). "정리기"에서 "지능형 라이브러리"로의 제품 전환입니다.
- 한국어 검색 품질: CLIP 계열은 영어 중심 → 번역/다국어 모델 선택에 따라 KPI(Search Success Rate)가 크게 좌우됨. 초기 POC로 실측 검증 필요.
- 성능/하드웨어: 수천 장 임베딩은 GPU(WebGPU)에서도 수 분~수십 분. 저사양(WASM)에서는 훨씬 느림 → 백그라운드·재개·진행률 UX 필수.
- 앱 용량/배포: 모델 동봉 시 수백 MB. 배포 방식 결정 필요.
- RAW: 브라우저 환경 RAW 디코딩은 까다로움 → 범위 신중 결정.
7. 제안
Phase 0 + Phase 1(인덱스 기반 + 품질/Culling)을 1차 목표로 삼는 것을 권장합니다. 모델 리스크가 낮고, 자연어 검색(Phase 2)의 토대(인덱스·워커)를 그대로 재사용합니다. 자연어 검색은 §4-1 결정 후 소규모 POC로 한국어 정확도부터 실측하고 본 구현에 들어가는 것을 권합니다.
8. Phase 0 + Phase 1 상세 실행 계획 (확정 범위)
8.1 기술 추가 (이 단계 한정)
better-sqlite3(네이티브)→sql.js(WASM SQLite)로 확정. 이유: 이 환경(Node 24 + Python 3.12)에서 네이티브 컴파일이 실패(distutils 제거)했고, 네이티브 모듈은 사용자 PC마다 ABI/빌드툴 문제가 재발한다. WASM은 빌드/재빌드가 전혀 없어 Windows+macOS 배포가 단순. 인메모리 DB를userData/index.db로 export 영속화. (수천~수만 장 메타데이터 규모에 충분. 추후 필요 시 indexDb 추상화 뒤에서 네이티브로 교체 가능.)- 썸네일은 네이티브(sharp) 없이 AI Worker(렌더러)의 canvas로 생성 → 바이트를 Main이 캐시. 네이티브 의존 0.
✅ Phase 0-a 완료(2026-06-01):
indexDb(sql.js) + asset/quality 스키마 + 영속화. Electron 부팅 시userData/index.db생성 확인. typecheck/build/스모크 통과.
- Phase 1 품질 점수는 모델 거의 불필요: 초점=라플라시안 분산, 노출=휘도 히스토그램, 감은 눈=face-api 랜드마크(EAR). face-api는 이미 추론창에 로드됨.
8.2 데이터 모델 (SQLite)
asset(
id INTEGER PK, contentHash TEXT UNIQUE, path TEXT, ext TEXT,
sizeBytes INT, mtime INT, width INT, height INT,
exifYear TEXT, exifMonth TEXT, indexedAt INT
)
quality(
assetId FK, focus REAL, exposure REAL, eyesOpen REAL,
flag TEXT -- 'candidate' | 'blurry' | 'eyesClosed' | 'badExposure'
)
-- Phase 2 예약: embedding(assetId, vec BLOB), phash(assetId, hash)
- contentHash(파일 내용 해시)로 식별 → 경로가 바뀌어도 추적, 정확 중복 인식.
(contentHash, mtime)동일하면 재색인 스킵(재개 가능). - 썸네일:
userData/thumbs/<contentHash>.webp.
8.3 프로세스/모듈 추가
Main
indexDb.ts SQLite 래퍼 (better-sqlite3)
libraryStore.ts 색인 대상 라이브러리 폴더 관리(settings)
indexer.ts 오케스트레이터: 라이브러리 워크 → 워커 디스패치 → DB upsert → 진행률
cullingService.ts Phase1 결과 조회/뷰 데이터 제공
AI Worker (기존 숨김 추론창 확장 or 신규 'indexer' 창)
qualityEngine.ts 초점/노출/EAR 점수 + 썸네일 생성 (canvas + face-api 재사용)
UI (신규 화면)
LibraryView 라이브러리 폴더 지정 + 색인 진행률
CullingView '고품질 후보 / 제외(흐림·눈감음·노출)' 그리드 + 점수/오버라이드
- 비파괴: 색인은 파일을 옮기지 않음. Culling 결과로 '제외'를 태그만 하고, 원하면 사용자가 별도 폴더로 내보내기(기존 fileOps 재사용, 선택).
- 기존 정리기와 통합: 정리 잡도 인덱스(EXIF/얼굴)를 재사용하도록 점진 연결.
8.4 작업 순서 (체크리스트)
- Phase 0-a 완료:
indexDb(sql.js로 확정) 스키마/마이그레이션 + Electron 부팅 시 DB 생성 검증 - Phase 0-b 완료: contentHash(샘플 sha1) + 라이브러리 폴더 지정 UI(라이브러리 탭) +
indexer워크/재개(경로·mtime 스킵)/진행률 IPC/배치 영속화/취소. 헤드리스 파이프라인 검증(중복 0) + 부팅 스모크 통과 - Phase 0-c 완료: AI Worker(추론창) canvas로 썸네일(webp) 생성 →
userData/thumbs/<hash>.webp캐시(비파괴), 색인 시 자동 생성 + 원본 치수 저장,photoai-media://thumb해시 기반 보안 제공, 라이브러리 탭 썸네일 그리드(페이지네이션). typecheck/build/부팅 스모크 통과 - Phase 1-a 완료:
qualityEngine초점(라플라시안 분산)/노출(히스토그램 클리핑) 점수 → DB - Phase 1-b 완료: 감은 눈(EAR, face-api 랜드마크, Tiny 검출기) 점수 →
classifyFlag로 종합 분류(candidate/blurry/eyesClosed/badExposure). 색인 시infer:analyze로 썸네일+품질 1회 로드 통합 - Phase 1-c (기본) 완료: 라이브러리 그리드에 컬링 필터(전체/고품질 후보/제외) + 품질 배지. 임계값 튜닝 UI · 사진별 수동 오버라이드는 후속으로 보류
- Phase 1-d(옵션): 제외 사진 내보내기/이동 액션 (보류)
8.6 Phase 1 마무리 (임계값 튜닝 + 수동 오버라이드) — 완료(2026-06-01)
- 품질 임계값(초점/노출/눈)을 설정에 저장 + 슬라이더 UI. 임계값 변경 시 저장된 원본 점수로 SQL CASE 실시간 재분류(재분석 없음)
- 별점(0~5) + 색라벨(5색) 수동 메타(
usermeta테이블), 썸네일 호버 편집, 별점 최소 필터
8.7 Phase 2 (CLIP 자연어/유사 검색) — 기본 완료(2026-06-01)
@huggingface/transformers(WASM/WebGPU, 네이티브 빌드 0) 도입- CLIP(
Xenova/clip-vit-base-patch32) 이미지/텍스트 임베딩 — 추론창 lazy-load - 한국어 쿼리 자동 번역(
Xenova/opus-mt-ko-en) → 영어 CLIP - 임베딩 SQLite BLOB 저장 + "검색 색인 생성"(임베딩 배치, 진행률/취소) — 기본 색인과 분리
- 브루트포스 코사인 검색 + 검색 탭(임베딩 상태/생성 + 검색바 + 결과 그리드)
- typecheck/build/부팅 스모크 통과
⚠️ Phase 2 런타임 미검증 항목: CLIP/번역 모델은 최초 사용 시 HF Hub에서 다운로드(온라인 1회 필요, 이후 캐시). ONNX Runtime WASM의 완전 오프라인 패키징(CDN 대신 동봉)과 한국어 검색 정확도 실측은 후속 과제. 임베딩 생성은 이미지당 수백 ms(WASM) → 대량은 시간 소요(백그라운드).
- i18n(ko/en) · 다크모드 · 검증(typecheck/test/build/스모크)
8.5 리스크 (이 단계)
- better-sqlite3 ABI: Electron 버전별 재빌드 필요 → CI/빌드에서
electron-builder가 처리하나, dev 실행 시electron-rebuild1회 필요할 수 있음. - 대량 색인 UX: 수천 장 썸네일/점수 계산은 시간 소요 → 백그라운드·재개·진행률 필수(이미 설계 반영).
- EAR 기반 눈감음은 휴리스틱 → 임계값 튜닝 및 오버라이드 UI로 보완.
본 계획 승인 시 **Phase 0-a(인덱스 DB 토대)**부터 착수.