---
task_id: task-2349
type: context
scope: task
created: 2026-05-02
updated: 2026-05-02
status: completed
---

# 맥락 노트: task-2349

**task**: task-2349

---

## 결정 근거

### Sideload 확장 동적 감지로 EXT_ID 환경변수 제거
- **근거**: 사이드로드 확장은 ID가 사용자/브라우저마다 동적으로 생성됨 → `import.meta.env.VITE_INSURO_HELPER_EXT_ID`를 빌드타임에 박는 방식은 호환 불가.
- **대안과 기각**:
  - 옵션1: 사용자가 EXT_ID를 수동 입력 → UX 부담, 기각.
  - 옵션2: externally_connectable + chrome.runtime.sendMessage(extId,...) → 여전히 EXT_ID 필요, 기각.
  - 채택: DOM 데이터 속성(`data-insuro-helper`) + `window.postMessage` 핸드셰이크. content.js가 InsuRo 페이지에서 실행되어야 하므로 manifest matches에 insuro.biz / localhost:5173 추가.

### A안 — 입력 폼 완전 제거 (2단계 동적 입력 vs 1단계 캡처 표시)
- **근거**: ohmymanager에서 사용자가 이미 플랜+조건을 정해서 캡처가 들어옴. InsuRo에서 또 입력하면 이중 입력이며, 시드 더미 플랜밖에 없는 상태에서 입력 폼은 무의미.
- **대안과 기각**:
  - 옵션1: 입력 폼 + 캡처 데이터 둘 다 유지(점진 전환) → UI 복잡도 증가, 어떤 데이터를 우선할지 모호. 기각.
  - 옵션2(채택): 입력 폼 제거. 캡처 클릭 → 자동 계산. 보장 토글로만 인라인 조정.

### Phase 1 → Phase 2 분리 가능성
- 시간 부족 시 Phase 1만 머지해도 토스트 거짓 알림은 즉시 해소됨. Phase 2는 동일 task 내 이어서 진행하되 PR 분리 가능.

## 3 Step Why 자문 결과

- **1st Why** "왜 이 설계가 필요한가?"
  - A: 확장 미감지로 항상 토스트가 뜨는 거짓 알림 + UI에 의미 없는 입력 폼이 잔존하여 사용자 혼란 → 캡처 워크플로우 정합성 회복 필요.
- **2nd Why** "왜 A(DOM 마커 + 입력 폼 제거)가 최선의 접근인가?"
  - B: DOM 마커는 사이드로드/스토어 모두에서 동작하며, 입력 폼 제거는 캡처 데이터를 단일 입력원으로 만들어 시스템 의도와 일치.
- **3rd Why** "왜 B가 다른 대안보다 나은가?"
  - C: EXT_ID 수동 입력/이중 폼 유지 모두 운영자에게 추가 부담. DOM 마커는 즉시·동기 감지가 가능해 첫 페인트에서 바로 단계 결정 → 깜빡임 0.

## 참조 자료

- task-2336 보고서 (Chrome Extension 피벗): `memory/reports/task-2336.md`
- task-2346 보고서 (composite-calculate 정규화): `memory/reports/task-2346.md`
- 시스템 3문서: `memory/plans/insuro-composite-design/`
- 백엔드 endpoints:
  - `GET /api/insuro/composite-design/recent-views` — `server/main.py:7162`
  - `POST /api/insuro/composite-calculate` — `server/main.py:6989`
  - `GET /api/insuro/composite-coverages` (담보 마스터)

## 주의사항

- **manifest matches 확장**: content.js가 insuro.biz / localhost:5173에서도 실행되어야 마커 주입 가능. 매니페스트 변경 시 ohmymanager 캡처 로직과 분리(분기 처리)하여 InsuRo 페이지에서는 inject.js 주입을 스킵하도록.
- **Service Worker 캐시**: PWA 캐시 때문에 신버전 반영이 느릴 수 있음. 검증 시 Ctrl+Shift+R 필수.
- **확장 재로드**: 사용자가 chrome://extensions에서 reload 해야 매니페스트 변경 반영.
- **selected_coverages 기본값**: 캡처 자동 계산 시 master /composite-coverages 전체를 amount=1000(만원)으로 디폴트 선택. 보장 토글로 제외 가능.
- **PII**: extension content.js와 background.js의 ohmymanager JWT는 보존만. 외부 AI(Codex/Gemini) 전달 전 sanitize 필수.

## Codex 사전 검증 결과 반영 (2026-05-02)

### 환각 (무시)
- "cancelled 마커 필요" critical: task 파일에 취소 선언 없음, 회장 승인된 진행 task. 환각 확정.

### 채택된 보강
- **(High) manifest matches 분기 처리**: content.js 단일 파일을 사용하되 `window.location.hostname`으로 도메인 분기.
  - `mmlfcp.ohmymanager.com`: inject.js 주입 + capture (기존 동작 유지)
  - `insuro.biz` / `localhost:5173`: 마커(`data-insuro-helper`) + HELLO postMessage + SET_INSURO_JWT 수신만
  - 두 환경에서 코드 충돌 없도록 host_permissions, content_scripts.matches 모두 확장
- **(Medium) postMessage 보안 강화**:
  - `setInsuroJwt`는 외부 `externally_connectable` + `chrome.runtime.sendMessage` 경로를 우선 시도하고, 실패/extId 부재 시에만 postMessage 폴백.
  - postMessage 시 `event.origin === window.location.origin` 검증 (content.js 측)
  - HELLO 메시지에 `nonce`(UUID) 포함 → 페이지 측에서 같은 nonce로 응답하는 핸드셰이크는 본 task에선 단순화: 마커가 더 빠르고 동기적이므로 nonce는 최소 적용.
  - SET_INSURO_JWT는 origin 검증 통과 + content.js → background.js로 forward (page-world JS가 직접 chrome.storage 접근 불가하므로 content.js가 신뢰 경계)
- **(High) UI 전환 — A/B/C 상태 머신**: pingExtension 결과(installed) + recentViews 길이 두 변수만으로 상태 머신 분기. 기존 plansFetch/coveragesFetch 로직은 단계 C에서 selected_coverages 디폴트용으로만 활용.
