# Plan: task-127.1 — InsuWiki Drive API injection 수정 + docId 중복 제거

## 작업 분석

### 문제 1: Drive API Query Injection
- `route.ts:104` — `file.name` (사용자 업로드 파일명)이 Drive API 쿼리에 이스케이핑 없이 직접 삽입됨
  - `q: '${companyFolderId}' in parents and name = '${file.name}' and trashed = false`
  - 파일명에 싱글쿼트(`'`)가 포함되면 쿼리가 깨지거나 인젝션 가능
- `seed-insurance-metadata.ts:76` — `folderId`가 Drive API 결과에서 오지만, 방어적 이스케이핑 필요

### 문제 2: docId 중복 (companyId 이중 포함)
- 두 파일 모두 동일 패턴:
  ```
  productId = `${companyId}_${productName}_${effectiveDate}`
  docId = `${companyId}_${productId}`   ← companyId가 2번 들어감!
  ```
- 예: companyId="삼성life" → docId="삼성life_삼성life_퍼펙트종신_202403" (오류)
- 올바른 docId: `productId` 자체 (이미 companyId 포함)

## 서브태스크 분해 및 팀원 배정

### ST-1: 백엔드 수정 — 토르
- `route.ts`: Drive API 쿼리 이스케이핑 함수 추가 + docId 수정
- 대상: line 104 (injection), line 124 (docId)

### ST-2: 스크립트 수정 — 토르
- `seed-insurance-metadata.ts`: Drive API 쿼리 이스케이핑 + docId 수정
- 대상: line 76 (injection), line 117 (docId)

### ST-3: 테스트 검증 — 헤임달
- 수정된 코드의 이스케이핑 함수 단위 테스트
- docId 생성 로직 검증

## 검토한 대안과 기각 사유

1. **docId를 완전히 새로운 포맷으로 변경** → 기각: 기존 Firestore 데이터와 호환성 문제. productId를 그대로 쓰는 것이 최소 변경.
2. **Drive API 대신 파일 ID로만 검색** → 기각: 동일 파일명 덮어쓰기 기능이 이름 기반 검색에 의존. 이스케이핑이 올바른 해결책.
3. **productId에서 companyId 제거** → 기각: productId의 고유성이 companyId에 의존하므로 제거 불가. docId 쪽을 수정하는 것이 맞음.

## 실행 순서
1. 토르: 두 파일 동시 수정 (독립적이므로 병렬 가능)
2. 헤임달: 수정 결과 검증

## 실패 시나리오 체크리스트

### 1. 비정상 입력/상태
- 파일명에 싱글쿼트, 백슬래시, 특수문자 포함 시 → 이스케이핑 함수로 처리. `\`를 `\\`로, `'`를 `\'`로 변환.
- 파일명이 빈 문자열이면 → 기존 파일명 파싱 regex에서 걸러짐 (null 반환).

### 2. 동시성/경쟁 조건
- Drive API 쿼리는 읽기 전용 검색 → 경쟁 조건 해당 없음.
- docId 변경 후 기존 중복 docId 데이터가 Firestore에 남아있을 수 있음 → merge:true이므로 새 데이터로 덮어씀. 오래된 중복 docId 문서는 수동 정리 필요 (이번 작업 범위 외).

### 3. 비정상 종료/타임아웃
- 이스케이핑은 순수 함수, 상태 변경 없음 → 해당 없음.

### 4. 스테일 데이터
- docId 포맷 변경으로 기존 잘못된 docId의 문서가 고아 문서로 남음 → 다음 시딩/업로드 시 올바른 docId로 새 문서 생성됨. 고아 정리는 별도 마이그레이션 작업 필요.

### 5. 통합 시 충돌
- 두 파일은 서로 독립적 (route.ts는 API, seed는 스크립트). 충돌 없음.
- 이스케이핑 함수를 각 파일에 로컬로 추가 (공유 유틸리티 불필요 — 두 파일 사이 import 관계 없음).
