o2o-infinith-demo/doc/PIPELINE_IMPROVEMENT_PLAN.md

151 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 검색/분석 파이프라인 종합 개선 계획
**작성일**: 2026-04-04
**상태**: 계획 완료, 구현 대기
## Context
현재 파이프라인의 3-phase 아키텍처(discover → collect → generate)는 구조적으로는 괜찮으나, **실행 신뢰성**에 심각한 문제가 있음. 3개 병렬 코드 탐색 결과:
- **Silent failure 16건**: 모든 catch 블록이 에러를 삼킴
- **데이터 품질 8건**: 잘못된 URL, 동명이인 병원, 평점 스케일 혼동
- **에러 복구 0건**: 재시도 로직 없음, 새로고침 시 데이터 유실
- **API 불안정 6건**: 타임아웃, 쿼터 소진, 인증 실패 미감지
**목표**: 검색 정확도 + 에러 회복력 + UX 투명성을 대폭 개선
---
## 진행 상태 체크리스트
### Sprint 1: 데이터 품질 Quick Wins (~2.5h)
- [ ] **WP-1**. YouTube Channel ID 정규식 수정 (20min)
- 파일: `discover-channels/index.ts`, `verifyHandles.ts`
- 변경: `/^UC[a-zA-Z0-9_-]{20,}$/``/^UC[a-zA-Z0-9_-]{22}$/`
- 검증: 24자가 아닌 channel ID가 reject되는지 확인
- [ ] **WP-2**. Naver Place 동명이인 방지 (30min)
- 파일: `enrich-channels/index.ts` lines 319-343
- 변경: 카테고리 필터링 (성형/피부) + 검색어에 "성형외과" 추가
- 검증: "아이디병원" 검색 → "아이디마곡치과" 아닌 성형외과 반환
- [ ] **WP-3**. Google Maps URL 수정 (20min)
- 파일: `collect-channel-data/index.ts`, `enrich-channels/index.ts`
- 변경: `place.website`(병원 사이트) 대신 `maps.google.com/search` URL
- 검증: 기타 채널의 구글 지도 링크가 Google Maps로 이동
- [ ] **WP-4**. Naver Blog 공식 블로그 분리 (30min)
- 파일: `collect-channel-data/index.ts`
- 변경: verified_channels 핸들로 `blog.naver.com/{handle}` 생성, 검색 결과는 `blogMentions`
- 검증: 네이버 블로그 링크가 공식 블로그로 이동
- [ ] **WP-5**. 강남언니 평점 정규화 (30min)
- 파일: `collect-channel-data/index.ts`, `enrich-channels/index.ts`
- 변경: `rawRating` + `normalizedRating` 분리, ≤5.0이면 ×2
- 검증: 강남언니 평점이 `/10`으로 일관되게 표시
- [ ] **WP-6**. Perplexity 모델 상수화 (20min)
- 파일: 새 `_shared/config.ts`
- 변경: 환경변수 `PERPLEXITY_MODEL` 또는 기본값 `"sonar"`
- 검증: 모든 Edge Function에서 하드코딩 `"sonar"` 제거됨
- [ ] **WP-7**. Apify 타임아웃 증가 (10min)
- 파일: `discover-channels/index.ts`
- 변경: `waitForFinish=30``waitForFinish=45`
- 검증: Instagram 프로필 스크래핑 성공률 향상
---
### Sprint 2: 에러 가시성 (~2.25h)
- [ ] **WP-8**. 채널 수집 에러 추적 ⭐ 핵심 (1.5h)
- 파일: `collect-channel-data/index.ts`
- DB: `ALTER TABLE marketing_reports ADD COLUMN IF NOT EXISTS channel_errors JSONB DEFAULT '{}';`
- 변경:
- [ ] HTTP 상태 코드 체크 (429, 403, 500)
- [ ] `Promise.allSettled` 결과 순회 → `channelErrors` 기록
- [ ] 부분 성공이어도 항상 DB 저장 (unconditional save)
- [ ] status: `"collected"` vs `"partial"` vs `"collection_failed"`
- [ ] 응답: `{ channelData, channelErrors, partialFailure }`
- 검증: API 토큰 무효화 시 에러가 기록되는지 확인
- [ ] **WP-9**. Instagram/Facebook 검증 개선 (45min)
- 파일: `_shared/verifyHandles.ts`
- 변경:
- [ ] `verified` 타입: `boolean | "unverifiable"`
- [ ] Instagram: 로그인 리다이렉트 감지
- [ ] Facebook: HEAD → GET, 실패 시 `"unverifiable"`
- [ ] `collect-channel-data`에서 `"unverifiable"` 포함
- 검증: Facebook 핸들이 "unverifiable"로 표시되고 수집은 시도됨
---
### Sprint 3: 에러 회복 (~5.25h)
- [ ] **WP-10**. API 재시도 유틸리티 (2h)
- 파일: 새 `_shared/retry.ts`
- 변경: `fetchWithRetry()` — 지수 백오프, 429 존중, AbortController 타임아웃
- 검증: 429 응답 시 자동 재시도 후 성공
- [ ] **WP-11**. 부분 실패 복구 (1h)
- 파일: `collect-channel-data/index.ts`
- 변경: `Promise.allSettled` 직후 무조건 중간 DB 저장
- 검증: 일부 채널 실패해도 나머지 데이터 보존
- [ ] **WP-12**. 파이프라인 이어하기 (1.5h)
- 파일: `AnalysisLoadingPage.tsx`, `supabase.ts`
- 변경:
- [ ] `sessionStorage`에 reportId 저장
- [ ] URL에 reportId 포함
- [ ] DB status 폴링
- [ ] status별 이어하기 로직
- 검증: 분석 중 새로고침 → 이어서 진행
- [ ] **WP-13**. Enrichment 재시도 버튼 (45min)
- 파일: `useEnrichment.ts`, `EmptyState.tsx`
- 변경: `retry()` 함수 + "다시 시도" 버튼 (최대 2회)
- 검증: Enrichment 실패 후 버튼 클릭 → 재시도 성공
---
### Sprint 4: UX 마무리 (~50min)
- [ ] **WP-14**. EmptyState 상태별 UI (20min)
- 파일: `EmptyState.tsx`
- 변경: `loading | error | not_found` 상태별 다른 UI
- 검증: 각 상태에 맞는 아이콘/메시지/버튼 표시
- [ ] **WP-15**. Firecrawl Rate Limiting (30min)
- 파일: `_shared/retry.ts`에 추가
- 변경: 도메인별 500ms 간격 강제
- 검증: Firecrawl 429 에러 발생 안 함
---
## 핵심 파일 목록
| 파일 | Sprint | 설명 |
|------|--------|------|
| `supabase/functions/discover-channels/index.ts` | 1 | 채널 발견 |
| `supabase/functions/collect-channel-data/index.ts` | 1, 2, 3 | 데이터 수집 |
| `supabase/functions/enrich-channels/index.ts` | 1 | 레거시 enrichment |
| `supabase/functions/generate-report/index.ts` | - | 리포트 생성 (수정 없음) |
| `supabase/functions/_shared/verifyHandles.ts` | 1, 2 | 핸들 검증 |
| `supabase/functions/_shared/config.ts` (신규) | 1 | 공유 설정 |
| `supabase/functions/_shared/retry.ts` (신규) | 3 | 재시도 유틸 |
| `supabase/migrations/20260404_channel_errors.sql` (신규) | 2 | DB 마이그레이션 |
| `src/pages/AnalysisLoadingPage.tsx` | 3 | 로딩 페이지 |
| `src/hooks/useEnrichment.ts` | 3 | Enrichment 훅 |
| `src/components/report/ui/EmptyState.tsx` | 4 | 빈 상태 UI |
| `src/lib/supabase.ts` | 3 | API 클라이언트 |
## 총 예상 소요 시간
| Sprint | 시간 | 배포 범위 |
|--------|------|---------|
| Sprint 1: Quick Wins | ~2.5h | Edge Functions × 3 |
| Sprint 2: 에러 가시성 | ~2.25h | DB + Edge Functions |
| Sprint 3: 에러 회복 | ~5.25h | Edge Functions + Frontend + Vercel |
| Sprint 4: UX 마무리 | ~50min | Frontend + Vercel |
| **합계** | **~11h** | |