# task-2333 보고서 — InsuRo 복합설계 AI 계산기 Phase 1 (MVP)

**팀**: dev3-team (다그다 팀장, 루 백엔드, 브리짓 프론트)
**작업 레벨**: Lv.4 (한정승인)
**작업 시각**: 2026-04-30 15:37 ~ 16:18 KST (약 41분)
**워크트리**: `/home/jay/projects/InsuRo/.worktrees/task-2333-dev3` (브랜치: `task/task-2333-dev3`)

---

## S - Situation (배경)

InsuRo FA가 복합설계 시 보험사별 담보 보험료를 수작업(30분~1시간)으로 비교하여 최적 조합을 산출함. 현재 자동화 도구 부재.

## C - Complication (문제)

ohmymanager API는 모든 보험사 × 담보 × 나이별 보험료를 반환하지만, 1사/2사/3사 최적 조합 계산은 사람이 일일이 매트릭스를 작성해야 함. FA 1명당 일평균 5건 복합설계 → 2.5~5시간 소모.

## Q - Question (요구사항)

ohmymanager API 데이터를 캐시 → C(N,3) 보험사 조합 열거 + 담보별 그리디 최저가 배분 → 1사/2사/3사 결과를 3초 이내 반환하는 MVP 구축. 인카금융서비스 소속 FA 한정 접근.

## A - Answer (해결)

### 산출물 — 신규 파일

| 파일 | 설명 |
|---|---|
| `server/migrations/009_composite_design_tables.sql` | 5개 테이블 + idx_ohmy_prem_active 부분 unique 인덱스 |
| `server/config/ohmy_plans.json` | 1개 placeholder 플랜 (74개 별도 dispatch 필요) |
| `server/config/ohmy_target_coverages.json` | 32개 핵심 담보 실제 coverage_cd 매핑 |
| `server/scripts/ohmy_premium_collector.py` | 444줄. 포아송 9분 + 3패턴 + circuit breaker + UPSERT + 체크포인트 + SIGINT/SIGTERM 안전 종료 + --ignore-window 옵션 |
| `server/composite_calculator.py` | 1사/2사/3사 조합 알고리즘 (보험사당 1만원 제약, 가입금액 비례 환산) |
| `server/tests/test_composite_calculator.py` | 11개 단위 테스트 (test_triple_excludes_zero_allocation 포함) |
| `src/pages/CompositeDesign.tsx` | 536줄. 인카 가드 + 입력 폼 + 3열 결과 카드 + 면책 |
| `server/scripts/.gitignore` | collector 체크포인트 파일 제외 |

### 산출물 — 수정 파일

| 파일 | 변경 |
|---|---|
| `server/main.py` | Pydantic 모델 (CompositeCalculateRequest, CompositeCoverageInput) + `_verify_incar_member` 인카 가드 + API 3개 추가 |
| `src/config/routes.ts` | `/composite-design` 라우트 (lazy import) |
| `src/components/navigation/navigationConfig.ts` | "분석 & 도구" 하위 "복합설계 계산기" 메뉴 + GitMerge 아이콘 |

### Codex 게이트 사전 검증 대응 (G1)

5 risks 중:
- **Critical 1건** (파일 미생성) → 구현으로 해결
- **High 3건** → 설계 보강 (context-notes.md 결정 근거 1-3 참조):
  1. ohmy_premiums 부분 unique 인덱스 + UPSERT 전략
  2. 가입금액 1000만원 기준 + 비례 환산 (Phase 1 한정)
  3. API 레벨 인카 가드 (organization_id) — RLS 별도 미생성
- **Medium 1건** (문서 충돌) → Phase 1 단일 서버 + 403/429 circuit breaker 통일

### 발견 이슈 및 해결

#### 이슈 1: 3사 조합에서 0원 배분 보험사 포함됨 (Critical 버그)
- **재현**: plan=000000111041, age=60, gender=F → triple_best에 DB(0원) + 롯데(141050) + 삼성(18340) = 1만원 제약 위반
- **원인**: `composite_calculator.calc_multi()`에 `combo_size == 2` 조건부로만 0원/1만원 미만 필터 적용
- **해결**: 조건부 분기 제거 → 모든 조합 크기에 통합 적용 (`if any(t == 0 or 0 < t < 10000 for t in insurer_totals.values()): continue`)
- **회귀 테스트**: `test_triple_excludes_zero_allocation` 추가 (11/11 PASS)
- **사후 검증**: 동일 시나리오 → triple_best=None 반환 (1만원 제약 미충족 — 정상)

#### 이슈 2: ohmymanager API 실제 응답 구조 확인
- 임시 가정 (`raw["data"]["premiums"]`) → 실제 (`raw["coverage_premiums"][].detailList[]`) 갱신
- 0원 담보(`premium ≤ 0`)는 미가입으로 간주 → INSERT 제외

#### 이슈 3: 33개 담보 중 1개 매칭 안 됨
- "뇌출혈입원일자진단비" — ohmymanager API 89개 plan_coverages 중 매칭 항목 없음
- Phase 1 32개로 진행. 추후 제이회장님 확인 후 추가 (Phase 2 로드맵 명시)

#### 이슈 4: 가입금액 비례 환산 우선순위
- Phase 1 결정: 선형 비례 (`stored × amount / 1000`) + 면책 문구 "비선형 가능"
- Phase 2: 비선형 담보(암진단비 등) 가입금액별 별도 수집

### 단위 테스트 (composite_calculator)

```
server/tests/test_composite_calculator.py::test_single_best_returns_lowest_total PASSED
server/tests/test_composite_calculator.py::test_dual_best_lte_single_best PASSED
server/tests/test_composite_calculator.py::test_triple_best_lte_dual_best PASSED
server/tests/test_composite_calculator.py::test_all_keys_present PASSED
server/tests/test_composite_calculator.py::test_as_of_date_format PASSED
server/tests/test_composite_calculator.py::test_data_count PASSED
server/tests/test_composite_calculator.py::test_no_data_returns_none PASSED
server/tests/test_composite_calculator.py::test_single_insurer_only_returns_single_only PASSED
server/tests/test_composite_calculator.py::test_amount_scaling PASSED
server/tests/test_composite_calculator.py::test_saving_pct_positive PASSED
server/tests/test_composite_calculator.py::test_triple_excludes_zero_allocation PASSED
====== 11 passed in 0.65s ======
```

## L1 스모크테스트 결과 (필수)

- **서버 재시작**: 해당없음 (TestClient in-process 검증)
- **API 응답 확인**:
  - `composite-coverages, composite-plans, composite-calculate` 3개 엔드포인트 등록 확인
  - 인증 미적용 시 401 반환 확인 (인카 가드 정상 동작)
  - composite_calculator.calculate_composite() 실 DB 호출 결과:
    - 1사 최저가: 농협손해 145,938원 (5개 담보)
    - 2사 조합: 롯데 + 삼성 = 159,390원 (1사 대비 비쌈 — 1만원 제약 영향)
    - 3사 조합: None (모든 3사 조합이 1만원 제약 미충족)
- **빌드 결과**: `npm run build` 성공
  - dist/assets/CompositeDesign-C4piygRI.js 청크 생성 확인
  - 빌드 시각: 2026-04-30 16:11 KST
- **스크린샷**: `/home/jay/.cokacdir/workspace/F0573025/composite-design-redirect.png`
  - 라우팅 정상: 미인증 사용자 `/composite-design` → `/login` 리다이렉트
  - Console: 0 errors, 1 warning (unrelated PWA)
- **Playwright 정리**: browser_close 호출 완료, 포트 5173 freed

## 모델 사용 기록

- 다그다 (팀장, opus): 설계, 위임, 통합, L1 검증
- 루 (백엔드, sonnet): MT-A1~A5 + B1~B5 + C1~C4 + D1~D5 + E1~E4
- 브리짓 (프론트, sonnet): MT-F1~F3
- haiku 미사용

## 머지 결과

- **상태**: ✅ MERGED (2026-04-30 07:29 UTC / 16:29 KST)
- **PR**: https://github.com/JonghyukJeon/InsuRo/pull/70
- **머지 후 main**: `496833f`
- **빌드 검증 (main)**: 성공 (17.28s)
- **브랜치**: `task/task-2333-dev3` → 자동 삭제 + worktree 정리 완료

### Gemini PR 리뷰 대응 (5건 HIGH 모두 수정)

| # | 파일 | 이슈 | 해결 |
|---|---|---|---|
| 1 | CompositeDesign.tsx | Authorization 헤더 누락 (3개 fetch) | `getAuthHeader()` + supabase session token |
| 2 | ohmy_premium_collector.py | `/home/jay/...` 하드코딩 | `OHMY_ENV_KEYS_PATH` env + `Path.home()` 폴백 |
| 3 | CompositeDesign.tsx | 응답 shape 불일치 | `{plans: [...]}` 디스트럭처링 |
| 4 | CompositeDesign.tsx | amount 기본 10000000 (단위 오류) | 1000 (Man-won) |
| 5 | CompositeDesign.tsx | amount fallback 단위 오류 | 1000 (Man-won) |

### CI 사전 결함 (본 PR과 무관)

- `tests/test_admin_infokeyword.py::test_non_admin_returns_403` (401 vs 403)
- main 브랜치에서 2일째(2026-04-29~) 동일 실패 — task-2333은 해당 테스트 미수정
- PR 코멘트로 명시 후 머지 진행

### 작업 커밋 7개:
  ```
  1ca729e [task-2333] chore: collector 체크포인트 파일 gitignore
  640da0c [task-2333] Lugh: 3사 조합 1만원 제약 버그 수정 + 테스트 추가
  10d5019 [task-2333] Lugh: collector 파서 실제 API 구조 반영 + 32개 담보 시드 갱신
  d8bf0b2 [task-2333] Lugh: Step 3 ohmymanager 수집 스크립트 (Phase 1 단일 서버)
  8232413 [task-2333] Lugh: Step 4-5 계산 엔진 + API 엔드포인트 3개
  901a9da [task-2333] Brigid: Phase 1 프론트엔드 (CompositeDesign 페이지 + 라우트 + 메뉴)
  84a331d [task-2333] Lugh: Step 1-2 DB 마이그레이션 + 시드 데이터
  ```
- **머지 의견**:
  - 단위 테스트 11/11 PASS, 빌드 성공, 라우팅 검증 완료
  - 한정승인(Lv.4) 작업이므로 Gemini PR 리뷰 후 자동 머지 진행 (worktree finish --action pr)
  - 충돌 가능성 낮음 (모두 신규 파일 + main.py 단일 영역 추가)

## 후속 (Phase 2 로드맵)

- 74개 전체 플랜 등록 + 실제 ohmymanager 메타데이터 수집
- 1세 단위 수집 전환
- Tailscale exit node 3개 IP 분산
- 비선형 담보 별도 가입금액 수집
- PDF 출력 (A4 1장 고객 대면용)
- 33번째 담보("뇌출혈입원일자진단비") 매칭 확정
- 1438건 전수 수집 (3명 FA × 3일 분산)

---

**status**: completed

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

