# InsuRo 이미지 편집기 — 맥락 노트

## 핵심 의도
- 보험 설계사가 서류 이미지를 편집할 때 외부 도구 없이 InsuRo 안에서 해결
- 워터마크 삭제가 1순위 — 픽셀 정보 70%+ 변경되도록 필터/반전 등 조합 필요
- 배경 제거/투명은 증명사진, 서류 이미지 처리용

## 기술 결정
- fabric.js 선택: Canvas 기반 오브젝트 모델, 텍스트/이미지/도형 동시 지원, Undo/Redo 구현 용이
- 배경 제거는 클라이언트 사이드 WASM (@imgly/background-removal) — 서버 부하 없음
- 워터마크 삭제는 Canvas 인페인팅 (AI API 호출 아님, 클라이언트 자체 처리)

## UI 위치
- InsuRo 메뉴: 분석 & 도구 > 이미지 편집기
- 라우트: /tools/image-editor
- DashboardLayout 래퍼 사용

## 참고
- InsuRo 디자인 시스템: Shadcn UI + Tailwind
- 기존 도구 페이지 참고: MediScan.tsx, KeywordAnalysis.tsx

## Phase 1 구현 결정 (task-2192, 2026-04-25)

### A. fabric.js v6 필터 API — 왜 `fabric.filters`에서 destructure 하는가?
- fabric.js v6에서 필터 클래스는 `export * as filters from './filters'` 형태로 namespace export됨
- 직접 `import { Brightness } from 'fabric'`는 TypeScript에서 타입 에러 발생
- 해결: `const { Brightness, ... } = fabric.filters;` 런타임 destructure 사용

### B. 비네팅 구현 방식 — 왜 Canvas 2D overlay인가?
- fabric.js 내장 필터에 비네팅이 없음
- Canvas 2D radial gradient overlay로 구현하면 실시간 프리뷰 가능 + 필터 중첩에 영향 없음

### C. Undo/Redo 구현 — 왜 상태 직렬화 방식인가?
- fabric.js의 `canvas.toJSON()` / `canvas.loadFromJSON()`으로 전체 상태 스냅샷
- 최대 50단계 히스토리 스택
- 개별 객체 추적보다 구현이 단순하고 모든 변경을 일관되게 처리 가능

## Phase 2 구현 결정 (task-2194, 2026-04-25)

### D. 오버레이 코드 분리 — 왜 훅 + 패널 분리인가?
- ImageEditor.tsx가 이미 1441줄이므로, 오버레이 로직을 useImageOverlay.ts 훅으로 분리
- UI는 OverlayPanel.tsx 컴포넌트로 분리 (6개 서브패널)
- ImageEditor.tsx에는 훅 호출 + 패널 렌더링만 추가 (최소 변경)

### E. 모자이크 구현 — 왜 Canvas 2D ImageData 직접 조작인가?
- fabric.js에 pixelate 필터가 있지만, 부분 영역에만 적용하려면 ImageData 직접 조작이 필요
- blockSize×blockSize 블록 평균 색상으로 채우는 방식 (8x8 또는 16x16)
- 처리 결과를 fabric.FabricImage로 원래 위치에 오버레이

### F. 워터마크 타일 — 왜 격자형 IText 반복인가?
- Canvas 전체에 균일한 간격으로 워터마크 텍스트를 반복 배치
- fontSize 기반으로 stepX/stepY 동적 계산
- 각 워터마크 객체에 data.type='watermark'로 식별 → 일괄 제거 가능

### G. 드로잉 형광펜 — 왜 opacity 0.3인가?
- fabric.js v6에서 freeDrawingBrush의 globalCompositeOperation 미지원
- opacity 0.3 + 브러시 3배 두께로 형광펜 효과 시뮬레이션

## Phase 3 구현 결정 (task-2195, 2026-04-26)

### H. AI 기능 코드 분리 — 왜 useImageAI.ts + AIPanel.tsx인가?
- Phase 2와 동일한 패턴: 훅(로직) + 패널(UI) 분리
- ImageEditor.tsx에는 훅 호출 + 패널 렌더링만 추가 (최소 변경)
- 4개 AI 도구는 각각 독립적 — 하나의 훅에서 4개 기능을 모두 관리

### I. 워터마크 삭제 — 왜 8방향 이웃 픽셀 평균 인페인팅인가?
- AI API 호출 없이 클라이언트에서 처리해야 하는 요구사항
- 8방향 이웃 평균 + 3회 반복 패스로 점진적 블렌딩 효과
- 완벽한 제거가 아닌 "흐리게 만들기" 수준 — 사용자 기대치 관리 필요
- fabric.Rect로 선택 영역 지정 → offscreen canvas에서 처리 → backgroundImage로 교체

### J. 배경 제거 — 왜 @imgly/background-removal dynamic import인가?
- WASM 파일이 ~10MB로 큼 → 첫 사용 시에만 로드하여 초기 번들 크기 최소화
- `await import('@imgly/background-removal')` 패턴으로 코드 스플리팅
- progress 콜백으로 0~100% 진행률 UI 제공

### K. 서류 스캔 보정 — 왜 히스토그램 스트레칭 + unsharp mask인가?
- 3단계 프리셋: (1) 대비 강화(히스토그램 min/max → 0~255 매핑) → (2) 밝기 보정(평균 밝기 200 미만이면 올림) → (3) 샤프닝(3x3 unsharp mask)
- 기울기 자동 교정은 Hough Transform의 구현 복잡도 대비 효용이 낮아 미포함
- 원클릭으로 서류 사진을 선명하고 읽기 쉽게 만드는 것이 핵심 목표

## Phase 4 구현 결정 (task-2197, 2026-04-26)

### M. 콜라주/일괄처리 — 왜 fabric.js Canvas 대신 offscreen Canvas 2D인가?
- 콜라주/일괄처리는 fabric.js의 오브젝트 모델이 불필요 — 단순 이미지 합성/변환
- offscreen canvas(`document.createElement('canvas')`)로 이미지 합성이 더 효율적
- fabric.js Canvas 인스턴스를 공유하면 편집기 상태와 충돌 가능
- 콜라주/일괄처리는 독립 훅으로 분리하여 편집기 Canvas와 완전 격리

### N. ZIP 다운로드 — 왜 JSZip인가?
- 브라우저 네이티브 ZIP API 없음
- JSZip은 가장 널리 사용되는 라이브러리 (npm weekly 5M+)
- 순수 JavaScript, 서버 의존 없음
- base64 → binary 변환, Blob 생성을 간단한 API로 처리

### O. 콜라주 레이아웃 — 왜 비율 기반 셀 좌표인가?
- 출력 크기가 가변이므로 절대 좌표 대신 0~1 비율로 셀 위치 정의
- gap은 비율이 아닌 절대 px — 출력 크기에서 gap을 빼고 나머지를 비율로 배분
- cover 모드 drawImage — aspect ratio 유지하며 셀을 꽉 채움 (letterbox 없음)

### L. 색상 교체 — 왜 fabric이벤트 대신 any 타입 핸들러인가?
- fabric.js v6의 `mouse:down` 이벤트 타입이 `TPointerEvent`(Touch/Mouse 유니온)이라 MouseEvent 캐스팅 불가
- `e.scenePoint`를 직접 사용하여 캔버스 좌표를 얻음 (`getPointer`는 v6에서 제거됨)
- 색상 거리 = sqrt((r1-r2)^2 + (g1-g2)^2 + (b1-b2)^2), tolerance 0~100 → 0~441 매핑
