# task-2833 보고서 — 정보성 키워드 분석 "거짓 실패(5분 timeout)" 수정

- 팀: dev2-team (오딘)
- 담당: 프레이야(프론트, sonnet)
- 프로젝트: InsuRo
- 브랜치: `task/task-2833-dev2`
- worktree: `/home/jay/projects/InsuRo/.worktrees/task-2833-dev2`
- merge_policy: **none** (PR OPEN 유지 · 머지=아누 Codex+독립검증)

## S (Situation)
InsuRo "AI마케팅 - AI키워드분석 - 정보성 키워드"에서 분석이 5분(300초) 지나면
"분석 시간이 초과되었습니다"(destructive toast)로 실패 표시됨.

## C (Complication)
백엔드 InfoKeyword 워커는 분석을 **정상 완료**(실측 343초, 키워드 5개)하는데,
프론트 폴링 상한이 **고정 300초**(`POLL_MAX_SECONDS = 300`)라 완료 전에 포기 →
사용자에겐 **거짓 실패**. 실제로는 성공 데이터가 존재.

## Q (Question)
프론트만 손대서(워커/서버/확장 불변) 폴링 상한을 실측 소요에 맞게 현실화하려면?

## A (Answer) — 프론트 폴링 상한 키워드 수 비례 동적화
`src/pages/InfoKeyword.tsx` 단일 파일 + 단위테스트만 수정.

### 수정 내역 (라인별)
| 위치 | Before | After |
|---|---|---|
| import 아래(신규 module-level) | 없음 | `export function computePollMaxSeconds(n)` + 4개 상수 |
| 컴포넌트 내 | `const POLL_MAX_SECONDS = 300;` | 삭제 |
| pollProgress 초기값 | `max: POLL_MAX_SECONDS` | `max: POLL_MIN_SECONDS`(=300) |
| analyze 함수 | `setPollProgress({...300}); pollStatus(id)` | `const pollMax = computePollMaxSeconds(selectedKeywords.size); setPollProgress({..pollMax}); pollStatus(id, pollMax)` |
| `pollStatus` 시그니처 | `(id: string)` | `(id: string, pollMaxSeconds: number)` |
| MAX_ATTEMPTS | `POLL_MAX_SECONDS / 5` | `Math.ceil(pollMaxSeconds / 5)` |
| poll 중 setPollProgress | `max: POLL_MAX_SECONDS` | `max: pollMaxSeconds` |
| 타임아웃 toast | "분석 시간이 초과되었습니다" / `destructive` | "분석이 예상보다 오래 걸립니다" / "잠시 후 다시 확인해주세요" / `default` (하드 실패 완화) |
| 진행 UI 문구 | `약 5분 한도`(하드코딩) | `약 {Math.round(pollProgress.max/60)}분 한도`(동적) |
| 90초 AbortController(개별 요청) | — | **미변경**(무관, 지시대로 유지) |

### 동적 상한 공식
`clamp(base 120 + perKeyword 120 × 키워드수, 하한 300, 상한 900)`

| 키워드 수 | raw | 최종 상한 |
|---|---|---|
| 0 | 120 | **300** (하한) |
| 1 | 240 | **300** (단일도 회귀 0) |
| 2 | 360 | **360** |
| 5 | 720 | **720** (실측 343초 대비 여유 377초, ≥700 요구 충족) |
| 7 | 960 | **900** (상한 cap) |

## 검증 결과
- `npx tsc --noEmit` → 타입 에러 **0건**
- `npx vitest run src/pages/__tests__/infokeyword-poll.test.ts` → **3 tests PASS**
- 전체 회귀 `npx vitest run` → **42 files / 616 tests 전부 PASS** (회귀 0)
- diff 직접 확인(팀장): 모든 참조부 정합 · AbortController 미변경 확인
- `git diff --stat`: `InfoKeyword.tsx`(+22/-10) + 신규 테스트 파일. 지시 2경로 외 변경 0.

## L1 스모크테스트 결과
- **서버 재시작**: 해당없음 (프론트 순수 폴링 상한 로직만 변경, 백엔드 워커·API 계약 불변)
- **API 응답 확인**: 해당없음 (API 요청/응답 스키마 미변경 — 클라이언트 폴링 반복 상한만 조정)
- **빌드(프로덕션 반영 검증)**: **성공** — `npm run build` ✓ built in 12.08s, dist/ 생성(sw.js·172 precache entries)
- **실동작 검증(vitest)**: `computePollMaxSeconds` 실제 함수 실행으로 0/1/2/5/7 경계값 통과 (계산 로직 실증)
- **스크린샷**: 해당없음 — 폴링 상한 문구/타임아웃은 실제 백엔드 343초 분석 진행 중에만 렌더되어 로컬 재현 비현실적. 순수 계산 로직이므로 vitest 실행 + 빌드로 관찰 가능 검증 대체.

## 발견 이슈 및 해결
- 하드코딩 "약 5분 한도" UI 문구 발견 → 동적(`pollProgress.max`)으로 함께 수정.
- 워커 결과 **인메모리 유실**(백엔드 재시작 시 완료된 분석 결과 소실)은 **별건 후속** — 이 task 범위 밖(프론트 상한만 조정). 후속 task 필요.

## 머지 판단
- **머지 필요**: Yes (단, merge_policy=none → 팀장/봇 직접 머지 금지)
- **브랜치**: `task/task-2833-dev2`
- **워크트리 경로**: `/home/jay/projects/InsuRo/.worktrees/task-2833-dev2`
- **머지 의견**: 프론트 단일 파일 저위험 변경, tsc/vitest 클린, 빌드 성공, 회귀 0.
  PR OPEN 유지 → **아누 Codex 반증리뷰 + 독립검증** 후 머지 결정.

## 모델 사용 기록
- 프레이야(프론트): **sonnet** — 일반 코딩/로직 구현. haiku 미사용.

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

