# 계획서: InsuRo 복합설계 AI 계산기

**프로젝트**: InsuRo 복합설계 — ohmymanager 데이터 캡처 + 1사/2사/3사 최적 조합 + history + CRM 연동
**위치**: 분석 & 도구 > 복합설계 AI 계산기 (`/composite-design`)
**작업 레벨**: Lv.4 (다단계 Phase)
**status**: in-progress
- 옛 Phase 1 (직접 크롤링, task-2333): **폐기** (2026-05-01 피벗)
- 옛 Phase 2 (Chrome Extension 피벗, task-2336): ✅ 머지 완료
- 옛 Phase A (UX 정합화, task-2349): ✅ 머지 완료
- 옛 Phase B (버전 정책 (c) 분기 보충, task-2351): ✅ 머지 완료
- **신 Phase 0 (사전 조사, task-2354): ✅ 보고서 완료 + 회장 승인 (2026-05-02)**
- **신 Phase 1 (캡처+history 기본, task-2354): ✅ 머지 완료 (PR #80, 2026-05-02)**
- **신 Phase 2 (만기별 비교 UI, task-2356): ✅ 머지 완료 (PR #81, 2026-05-02)**
- **신 Phase 3 (CRM 자동 연동, task-2359): ✅ 코드 작성 + 검증 PASS (2026-05-02, 브랜치 `task/task-2359-dev6`)**
- **신 Phase 4 (운영 안정화): Phase 3 머지 + 회장 검증 후 진행**
- **신 task-2356 후속 (분리): customers 운영 마이그레이션 + 증권분석/메디스캔 통합**
**작성일**: 2026-04-30
**최종 갱신**: 2026-05-02 (Phase 3 CRM 자동 연동 완료)
**근거**: 에이전트 미팅 6사이클 (`memory/meetings/2026-04-30-composite-design-calculator.md`) + task-2336 피벗 + 2026-05-02 brainstorming (본 문서 + context-notes.md)
**접근 제한**: 인카금융서비스 소속 FA 한정 (sideload Chrome Extension)

---

## ★ 중요 — Phase 1 (직접 크롤링) 폐기 결정

2026-04-30 task-2333에서 Phase 1 MVP(백엔드 ohmymanager 크롤링 + DB 캐시) 구현됨.
2026-05-01 task-2336에서 **Chrome Extension 자동 캡처로 전면 피벗**.

**피벗 사유**:
- ohmymanager 약관 "데이터 재활용 금지" 리스크 회피
- IP 차단 리스크 (3명 FA × 3일 분산 수집해도 결국 단방향 크롤링)
- 사용자 워크플로우와 정합 — FA가 ohmymanager에서 보고 있는 "실제 화면"이 곧 분석 대상

**기존 Phase 1 산출물 처리**:
- `ohmy_plans`/`ohmy_premiums`/`ohmy_coverages` 테이블 보존 (참고 데이터)
- 직접 크롤링 스크립트(`ohmy_premium_collector.py`)는 운영 정지 (코드는 보존)
- `/api/insuro/composite-plans` 엔드포인트 deprecation

---

## 1. 목표

보험 FA가 ohmymanager에서 고객별 보험료 비교를 진행하면, **InsuRo Helper Chrome Extension이 매트릭스 데이터를 캡처**하여 InsuRo가:
1. 1사/2사/3사 최적 조합 분석
2. 같은 고객의 만기별/조건별 history 누적 비교
3. CRM에 자동 고객 등록/매칭

**가치 제안**: 수작업 30분~1시간 분석 → **사용자 1클릭 → InsuRo 즉시 분석**.

---

## 2. 워크플로우 (회장 결정 — 2026-05-02 brainstorming)

```
[FA] ohmymanager 로그인 + 1차 입력 (이름/생년월일/성별/생손보유형/상품유형/만기) + 조회
   ↓
[FA] 플랜 드롭다운 선택 (선택) → 담보 자동 체크 OR 수동 체크
   ↓
[FA] 담보별 가입한도 입력 + 보험사별 보험료 자동 로딩
   ↓
[FA] 만족스러운 결과 → InsuRo Helper "📊 InsuRo로 분석" 플로팅 버튼 클릭
   ↓
[Extension] 매트릭스 + 1차입력 + 만기 + 플랜 + 담보 모두 DOM 스크래핑 → InsuRo POST
   ↓
[InsuRo 서버] history 저장 + CRM 자동 매칭/등록 (이름+생년월일 키)
   ↓
[InsuRo /composite-design] 새 탭 자동 활성화 → history 목록 + 1사/2사/3사 분석 표시
   ↓
[FA] 다른 만기로 다시 조회 → 플로팅 버튼 다시 클릭 → history N+1 추가
   ↓
[InsuRo] 같은 고객 만기별 비교 자동 활성화 (20년 vs 30년납 총액 차이)
```

---

## 3. 범위 (Phase별)

### Phase 0 — 사전 조사 (위임 첫 단계, 회장 승인 필요)
- 기존 InsuRo CRM 스키마 조사 (`/customer-management` + 관련 테이블)
- 매핑 설계: ohmymanager 캡처 → 기존 CRM 컬럼
- PII 암호화 처리 설계 (Supabase Vault 또는 컬럼 암호화)
- 매핑 문서 작성 + 회장 승인 게이트

### Phase 1 — 캡처 + history 기본 (필수)
- Extension content.js: 매트릭스 DOM 스크래핑 + 플로팅 버튼 (Z-①)
- 마이그레이션: `ohmy_capture_history` 테이블 신규
- API: POST `/api/insuro/ohmy-capture` (history INSERT + 암호화)
- UI: history 목록 + 선택 → 1사/2사/3사 분석 표시
- 인증: InsuRo 로그인 미상태 시 플로팅 버튼 미표시 (회장 결정 #10)

### Phase 2 — 비교 UI (만기별/조건별)
- 같은 고객 키(이름+생년월일) 자동 클러스터링
- 만기별 비교 카드 (20년/100세 vs 30년/100세 매트릭스 나란히)
- 20년납 vs 30년납 총 납입액 차이 분석 (회장 핵심 가치)
- ohmymanager "만기별 보험료 비교" 탭 캡처 활용 (옵션 3 + 옵션 2 결합)
- A안 미러링 (ohmymanager 담보/필터 = 진실의 원천, InsuRo는 표시만)

### Phase 3 — CRM 자동 연동 (NEW)
- 매칭 로직: 이름+생년월일 → 기존 고객 ID
- 매칭 시: confirm 다이얼로그 "이 고객 맞나요?" (동명이인 안전장치)
- 미매칭 시: 신규 고객 자동 등록 + UPSERT 패턴 (동시성 방어)
- CRM 페이지: 고객 상세에 "최근 ohmymanager 분석 history" 섹션
- 사후 수정: 잘못 매칭된 history를 다른 고객으로 이동 가능

### Phase 4 — 운영 안정화 (후속 task로 분리 가능)
- DOM 셀렉터 변경 취약성: API 응답 가로채기 백업 + 폴백
- 모바일 대응 (회장 확인 — ohmymanager 모바일 지원함)
- 시크릿 모드 가이드 추가
- history 정리: 사용자 수동 + 향후 GoogleDrive 백업 옵션

---

## 4. 캡처 데이터 구조

```ts
{
  // 1차 입력
  client: {
    name: string,           // 암호화 저장
    dob: string,            // 8자리, 암호화 저장
    gender: "M" | "F",
    age: number             // 자동 계산 (보험나이)
  },
  insurance_type: string,   // "손보 종합(무해지)" 등
  product_type: string,
  payment_term: string,     // "20년" — 만기 드롭다운에서 분리 파싱
  maturity_age: string,     // "100세" — 만기 드롭다운에서 분리 파싱
  plan: string,             // "실제비교" 또는 사용자 저장 플랜명

  // 매트릭스 (보험사 × 담보)
  insurers: [
    { name: "롯데손해보험", code: "LOTTE", total: 211279 },
    { name: "메리츠화재", code: "MERITZ", total: 225453 },
    ...
  ],
  coverages: [
    {
      cd: "상해후유장해(3~100%)",
      name: "상해후유장해",
      amount: 1000,         // 가입금액 (만원)
      premiums: { LOTTE: 470, MERITZ: 570, HYUNDAI: 526, ... }
    },
    ...
  ],

  // 메타
  captured_at: "2026-05-02T10:30:00+09:00",
  source: "trigger" | "tab_compare",  // 일반 클릭 vs "만기별 비교" 탭 캡처
  capture_hash: string                // 동일 매트릭스 중복 방지 hash
}
```

---

## 5. DB 스키마 (Phase 1+ 신규)

```sql
-- 캡처 history
CREATE TABLE IF NOT EXISTS ohmy_capture_history (
  id BIGSERIAL PRIMARY KEY,
  user_id UUID NOT NULL,                    -- InsuRo FA
  customer_id UUID,                         -- CRM 고객 ID (Phase 3 매칭 후)
  customer_key_hash TEXT NOT NULL,          -- SHA-256(name + dob + gender) — 동명이인 매칭용
  encrypted_name TEXT NOT NULL,             -- 암호화
  encrypted_dob TEXT NOT NULL,              -- 암호화
  gender TEXT NOT NULL CHECK (gender IN ('M','F')),
  insurance_type TEXT,
  product_type TEXT,
  payment_term TEXT,                        -- "20년"
  maturity_age TEXT,                        -- "100세"
  plan_name TEXT,
  matrix JSONB NOT NULL,                    -- 보험사 × 담보 매트릭스 전체
  capture_hash TEXT NOT NULL,               -- 중복 방지
  source TEXT DEFAULT 'trigger',
  captured_at TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX idx_ohmy_capture_user ON ohmy_capture_history(user_id, captured_at DESC);
CREATE INDEX idx_ohmy_capture_customer ON ohmy_capture_history(customer_id, captured_at DESC);
CREATE INDEX idx_ohmy_capture_keyhash ON ohmy_capture_history(customer_key_hash);
CREATE UNIQUE INDEX idx_ohmy_capture_dedupe ON ohmy_capture_history(user_id, capture_hash);
```

---

## 6. 보안 (회장 결정)

### PII 암호화 (필수, 회장 결정 #1)
- **이름 + 생년월일 8자리 = 민감 PII**
- 저장: Supabase Vault 또는 pgcrypto 컬럼 암호화
- 매칭 키: `customer_key_hash = SHA-256(name + dob + gender + salt)` — 동일인 식별 가능
- 표시용: 복호화 후 마스킹 (예: "홍**" / "850101")

### 인증 (회장 결정 #10)
- **InsuRo JWT 미로그인 상태 → Extension 플로팅 버튼 미표시**
- 만료 처리 자동 우회 (사용자가 InsuRo 로그인 안 되어있으면 버튼 자체가 없음)
- ohmymanager 세션은 ohmymanager 자체 처리 (InsuRo 책임 외)

### 동명이인 처리 (회장 결정 #3)
- (a) 매칭 시 confirm 다이얼로그: "기존 고객 홍길동(85.01.01) 맞나요? / 신규 등록"
- (b) 사후 수동 분리: CRM에서 잘못 매칭된 history를 다른 고객으로 이동

### CRM UPSERT (회장 결정 #15, #3과 별개 동시성 문제)
- 신규 자동 등록 시 UPSERT 패턴 (이름+생년월일+성별 컴포지트 키 유니크)
- 트랜잭션 처리로 동시 쓰기 중복 방지

### 금소법/개인정보 (향후 검토, 회장 결정 #2)
- 본 task는 기능 구현 우선
- 인카 내부 시스템이라 자체 활용으로 분류 가능
- 향후 법무 검토 + 동의 화면 추가 (Phase 5+)

### 디바운스 (회장 결정 #9)
- 매트릭스 hash 동일 체크 (시간 디바운스 X)
- 동일 매트릭스 재캡처 차단, 사용자 의도적 재확인 클릭은 허용

---

## 7. 알고리즘 (기존 task-2333/2346 정규화 로직 재활용)

- **방식**: C(N,3) 보험사 조합 열거 + 담보별 최저가 그리디
- **입력 변경**: 기존 (DB 조회) → 신규 (캡처 매트릭스 직접 분석)
- **응답 시간**: 분석은 즉시 (캐시 불필요, 캡처 데이터가 곧 입력)
- **20/30년납 비교**: 같은 고객 + 다른 payment_term history 클러스터링

---

## 8. UI 분기 (Phase 1+2)

### 단계 A: 확장 미설치
- 설치 가이드 + history 미표시

### 단계 B: 확장 설치 + history 0건
- "ohmymanager에서 보험료 비교 + 플로팅 버튼 클릭" 안내
- ohmymanager 외부 링크 버튼

### 단계 C: 확장 설치 + history 1건+
- history 목록 카드 (고객별 그룹 + 만기/조건/캡처시각)
- 항목 클릭 → 1사/2사/3사 분석 결과
- "이 고객 만기별 비교" 자동 카드 (같은 고객 + 다른 만기 2건+ 시 활성화)
- ohmymanager "만기별 보험료 비교" 탭 캡처 시 별도 표시

---

## 9. 모바일 + 시크릿 모드 (회장 결정 #8, #14)

- **모바일**: ohmymanager가 모바일 지원함 → Extension 모바일 호환 검증 (Phase 4)
- **시크릿 모드**: 확장이 시크릿 모드 권한 받아야 작동 → 가이드에 안내 추가
- **새 브라우저**: 사이드로드 재설치 안내 (이미 task-2349 완료)

---

## 10. 검증 시나리오

1. **첫 캡처 (신규 고객)**: ohmymanager 조회 → 플로팅 버튼 클릭 → InsuRo 자동 활성화 + history1 + CRM 신규 등록
2. **두 번째 캡처 (동일 고객 다른 만기)**: 30년/100세로 변경 후 클릭 → history2 + CRM 동일 고객 매칭 (confirm 다이얼로그)
3. **만기별 비교**: history 2건+ 시 InsuRo가 자동 비교 카드 표시 → 20년 vs 30년납 총액 차이
4. **ohmymanager "만기별 비교" 탭**: 그 탭에서 클릭 → 별도 source="tab_compare" history
5. **동명이인**: 같은 이름+생년월일 다른 고객 → confirm 다이얼로그에서 "신규 등록" 선택 → 별도 고객 카드
6. **중복 클릭**: 같은 매트릭스 재클릭 → hash 동일 → 저장 안 함
7. **InsuRo 미로그인**: ohmymanager 화면에 플로팅 버튼 미표시
8. **F12 Console 0건 + npm run build PASS**

---

## 11. 후속 task 후보 (Phase 5+)

- 금소법 동의 화면 + 법무 검토
- GoogleDrive 백업 (history 무한 성장 대응)
- ohmymanager DOM 셀렉터 변경 자동 감지 (선택)
- 모바일 UI 최적화

---

## 참조

- task-2333 (Phase 1 직접 크롤링 — 폐기)
- task-2336 (Chrome Extension 피벗)
- task-2346 (composite-calculate 정규화)
- task-2349 (UX A안 + 입력 폼 제거)
- task-2351 (버전 정책 (c) 분기)
- 2026-04-30 미팅: `memory/meetings/2026-04-30-composite-design-calculator.md`
- 2026-05-02 brainstorming: 본 문서 + context-notes.md
