# 검색/분석 파이프라인 종합 개선 계획 **작성일**: 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** | |