---
task_id: "2917"
team: 2팀
level: 2
priority: P2
scope: task
status: completed
created_at: 2026-08-06
---

# task-2917 — 소식지 업로드 UI: "자동(권장)" 기본 + 수동 파싱모드 고급 접이식

## Situation (상황)
소식지 업로드 다이얼로그(`AdminNewsletters.tsx`)가 파싱 모드 라디오 4종(자동/표/Vision/MarkItDown)과 Vision 모델을 전면 동등 노출. 게다가 MarkItDown에 "(추천)" 문구가 붙어 실제 권장인 auto가 권장으로 보이지 않았음. 병행 백엔드(task-2916)가 텍스트/이미지를 자동 판별해 텍스트 소식지는 정확 추출하므로, 사용자가 모드를 고를 필요가 없고 텍스트 PDF에 Vision을 잘못 고르면 오인식이 발생(회장 사고 원인).

## Complication (문제)
- 기능·API 계약(`parse_mode`: auto/table/vision/markitdown, `vision_model`: sonnet/opus 전송값)은 **불변**이어야 함.
- src/ 만 수정, server·extension 불변, 기존 업로드 기능(파일선택·보험사·발행월·제목·수동텍스트) 회귀 0.

## Question (핵심 질문)
API 계약과 기능을 전혀 건드리지 않고, UI 배치/기본값/노출만 바꿔 "자동(권장)"을 전면에 세우고 수동 모드를 고급 접이식으로 강등할 수 있는가?

## Answer (해결)
파싱 모드 UI를 presentational 컴포넌트로 추출하여 격리 + 단위 테스트 가능하게 재편. 부모(`AdminNewsletters.tsx`)의 state(`parseMode`/`visionModel` 기본 "auto"/"sonnet")와 formData 전송 로직은 그대로 유지.

### UI 재편 내용
- **상단**: `자동 (권장)` 옵션 하나만 강조 카드(`border rounded-lg p-3 border-primary/50 bg-primary/5`)로 노출, 기본 선택. 설명: "시스템이 문서를 자동 분석해 최적 방식으로 추출합니다 (텍스트는 정확 추출, 이미지는 AI 분석)."
- **하단**: `고급 옵션` Collapsible(shadcn/radix, **기본 접힘**, `type="button"` trigger + 회전 ChevronDown). 안내 "자동 결과가 이상할 때만 사용하세요." + 수동 라디오 3종(table/vision/markitdown). markitdown "(추천)" 문구 제거.
- Vision 모델(Sonnet/Opus)은 `parseMode === "vision"` 일 때만 노출.

## 생성/수정 파일 (3개 — 그 외 무변경)
- **신규** `src/components/newsletter/UploadParseModeField.tsx` (141줄) — presentational 컴포넌트. default export + `ParseMode`/`VisionModel` 타입 export.
- **수정** `src/pages/AdminNewsletters.tsx` — import 1줄(line 28) 추가 + 인라인 파싱모드 블록(구 505~559) → `<UploadParseModeField .../>` 5줄로 교체. state·formData 불변.
- **신규** `src/components/newsletter/UploadParseModeField.test.tsx` (88줄) — vitest 5 케이스.

## 계약 불변 확인 (grep 실측 — 독립 검증)
```
77: const [parseMode, setParseMode] = useState<"auto" | "table" | "vision" | "markitdown">("auto");
78: const [visionModel, setVisionModel] = useState<"sonnet" | "opus">("sonnet");
130:          formData.append("parse_mode", parseMode);
131:          formData.append("vision_model", visionModel);
```
- 신규 컴포넌트 내 라디오 value: auto/table/vision/markitdown, sonnet/opus — 계약값 그대로.
- forbidden(.github/**, extension/**, server/**) 미변경.

## 테스트 결과 (팀장 독립 재실행)
- **tsc** `npx tsc --noEmit`: **TSC_EXIT=0** (에러 0)
- **vitest** `UploadParseModeField.test.tsx`: **5 passed (5)**
  1. 기본 선택 auto (value="auto" checked) ✓
  2. 고급 접이식 기본 접힘(닫힘 시 수동옵션 radix unmount) ✓
  3. 고급 펼치면 table·vision·markitdown 선택 가능 ✓
  4. vision 선택 시에만 Sonnet/Opus 모델 노출 ✓
  5. 각 라디오 value가 계약값과 정확 일치(회귀 방지) ✓
- **npm run build**: **BUILD_EXIT=0**, `✓ built in 12.90s`, dist 20:48 갱신 (500kB 청크 경고는 pre-existing 정보성, 실패 아님)

## L1 스모크테스트 결과
- **서버 재시작**: 해당없음 (프론트 UI-only, 백엔드 무변경)
- **API 응답 확인**: 해당없음 (API 계약·호출 로직 불변)
- **실렌더 검증**: RTL(@testing-library/react, jsdom **실제 렌더**)로 신규 컴포넌트의 수용기준을 실동작 검증 — auto 기본선택 / 고급 접힘 시 수동옵션 DOM 미존재 / 펼침 시 3옵션 렌더 / vision 시 모델옵션 렌더. 5/5 PASS. + 프로덕션 빌드 PASS.
- **스크린샷(라이브 다이얼로그 브라우저 육안)**: 미수행 — AdminNewsletters 전체 페이지는 admin 인증+supabase 세션이 필요해 헤드리스 환경에서 라이브 다이얼로그 진입 불가. task 명세가 라이브 실렌더 육안을 **ANU 독립검증에 명시 위임**("ANU 독립검증(실렌더)·머지·배포는 ANU")하므로 해당 항목은 ANU가 수행.

## 모델 사용 기록
- 프레이야(프론트엔드): **sonnet** — 컴포넌트 추출·재편·테스트 작성.
- 팀장(오딘, Opus): 설계/위임/독립검증만. 직접 코딩 없음.

## 발견 이슈 및 해결
- 없음. 기존 default가 이미 "auto"였으나 UI상 4개 동등노출+markitdown "(추천)"으로 auto가 권장으로 인지되지 않던 문제를 정보위계 재편으로 해소.

## 머지 판단
- **머지 필요**: Yes (Lv.2 → PR)
- **브랜치**: task/task-2917-dev2
- **워크트리 경로**: /home/jay/projects/InsuRo/.worktrees/task-2917-dev2
- **base**: origin/main 07f402a
- **머지 의견**: src/ 3파일 국소 변경, tsc 0·vitest 5/5·build PASS, API 계약 불변 grep 확인. 병행 task-2916(server/만)과 무중첩. 충돌 위험 낮음. 라이브 실렌더 육안 후 머지·배포 권장(ANU).

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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

