# task-2215 완료 보고서: InsuRo 소식지/보험료 파일 저장을 Google Drive로 전환

## SCQA

**S**: InsuRo의 소식지/보험료 PDF·PPTX 파일이 Supabase Storage(무료 1GB)에 저장되고 있으며, 프론트엔드에서 직접 Supabase Storage에 업로드 후 별도 API로 AI 파싱을 호출하는 2단계 플로우로 운영 중이다.

**C**: 매월 수백 MB 업로드 시 무료 플랜 1GB 한도를 수 개월 내 초과할 위험이 있고, 프론트→Storage→DB→파싱 API의 4단계 과정이 복잡하여 에러 발생 시 데이터 불일치 위험이 존재한다.

**Q**: Google Drive(15GB 무료)로 전환하고, 서버 경유 단일 API로 업로드→DB저장→AI파싱을 일괄 처리할 수 있는가?

**A**: 서비스 계정 기반 Google Drive 업로드 함수를 구현하고, `/api/insuro/upload-to-drive` 통합 엔드포인트를 추가하여 프론트엔드에서 단일 API 호출로 전체 플로우를 처리하도록 전환 완료. 4개 파일 수정, 빌드 성공, 엔드포인트 정상 등록 확인.

## 수정 파일 목록

| 파일 | 변경 내용 | grep 검증 | 상태 |
|------|-----------|-----------|------|
| server/gdrive.py:149 | get_drive_service_sa() 서비스 계정 함수 추가 | grep "get_drive_service_sa" OK | verified |
| server/gdrive.py:160 | upload_file_to_drive() 범용 업로드 함수 추가 | grep "upload_file_to_drive" OK | verified |
| server/main.py:35 | fastapi import에 Form 추가 | grep "Form" OK | verified |
| server/main.py:2941 | POST /api/insuro/upload-to-drive 엔드포인트 추가 | grep "upload-to-drive" OK | verified |
| server/main.py:2997 | insert_result.data type: ignore[index] 추가 | grep "type: ignore" OK | verified |
| src/pages/AdminNewsletters.tsx:80~130 | handleUpload: Supabase Storage → upload-to-drive API 호출로 전환 | grep "upload-to-drive" OK, grep "supabase.storage" 0건 | verified |
| src/pages/AdminNewsletters.tsx:132~142 | handleDelete: Storage 삭제 로직 제거 | grep "supabase.storage" 0건 | verified |
| src/pages/AdminNewsletters.tsx:253 | "원본 PDF" → "원본 파일" 텍스트 변경 | grep "원본 파일" OK | verified |
| src/pages/AdminPremiumData.tsx:91~141 | handleUpload: Supabase Storage → upload-to-drive API 호출로 전환 | grep "upload-to-drive" OK, grep "supabase.storage" 0건 | verified |
| src/pages/AdminPremiumData.tsx:143~153 | handleDelete: Storage 삭제 로직 제거 | grep "supabase.storage" 0건 | verified |

## 발견 이슈 및 해결

1. **Pyright type 에러 (server/main.py:2997)**: `insert_result.data[0]["id"]`에서 Supabase SDK의 반환 타입이 복잡한 유니온이라 Pyright가 subscript 에러 발생. 기존 코드 패턴(라인 653)과 동일하게 `# type: ignore[index]` 추가로 해결.

2. **포트 8001 자동 재시작**: 메인 브랜치의 uvicorn이 supervisor로 관리되어 kill 후 자동 재시작. 대체 포트 8099에서 worktree 서버를 실행하여 L1 테스트 수행.

3. **미사용 import 정리**: 프론트엔드 두 파일에서 `API_REQUEST_DELAY_MS` import 제거 (서버 API 단일 호출로 전환되어 불필요).

## L1 스모크테스트 결과

- 서버 재시작: 성공 (포트 8099에서 worktree 코드로 기동)
- API 응답 확인:
  - `GET /api/status` → `{"status":"ok"}` (200)
  - `GET /openapi.json` → `/api/insuro/upload-to-drive` 엔드포인트 등록 확인
  - `POST /api/insuro/upload-to-drive` (인증 없이) → 401 "Unauthorized" (JWT 보호 정상)
  - `POST /api/insuro/upload-to-drive` (잘못된 JWT) → 401 "Invalid token" (인증 검증 정상)
- 스크린샷: 해당없음 (백엔드 API + 프론트엔드 빌드 검증)
- npm run build: 성공 (12.30초, dist/ 154 entries)

## 빌드 결과

- 빌드: 성공
- 타임스탬프: 2026-04-26 22:53
- dist/ 생성 확인

## 머지 판단

- **머지 필요**: Yes → **머지 완료**
- **PR**: https://github.com/JonghyukJeon/InsuRo/pull/45 (MERGED)
- **브랜치**: task/task-2215-dev4
- **Gemini PR 리뷰**: High 1건 수정 (SA 키 하드코딩 제거), Medium 2건 기각 (기존 패턴 일관성)
- **메인 빌드**: 머지 후 빌드 성공

## 모델 사용 기록

| 팀원 | 역할 | 모델 | 작업 |
|------|------|------|------|
| 카르티케야 | 백엔드 | sonnet | gdrive.py SA 함수 + main.py 엔드포인트 추가 |
| 비슈누(팀장) | 검토/수정 | opus | Pyright 타입 에러 수정, 통합 검증 |
| 사라스바티 | 프론트엔드 | sonnet | AdminNewsletters.tsx, AdminPremiumData.tsx 전환 (병렬) |

## Gemini PR 리뷰 대응

| 심각도 | 파일 | 코멘트 | 대응 |
|--------|------|--------|------|
| HIGH | gdrive.py:153 | SA 키 경로 하드코딩 | 수정: 환경변수 필수화, 하드코딩 제거 |
| MEDIUM | main.py:2973 | 예외 메시지 클라이언트 노출 | 기각: 기존 패턴 일관성, 관리자 전용 API |
| MEDIUM | main.py:3040 | 파싱 로직 중복 | 기각: 입력 경로 상이, 리팩토링은 별도 작업 |

## 커밋 로그

1. `3ef4625` - [task-2215] 카르티케야: gdrive.py SA 함수 + upload-to-drive 엔드포인트 추가
2. `9dcf92a` - [task-2215] 비슈누: pyright type ignore 수정
3. `c53fdc5` - [task-2215] 사라스바티: AdminNewsletters.tsx Supabase Storage → Drive API 전환
4. `5be09fb` - [task-2215] 사라스바티: AdminPremiumData.tsx Supabase Storage → Drive API 전환
5. `31a97a7` - [task-2215] 비슈누: Gemini High 수정 - SA 키 경로 하드코딩 제거
6. `4a52f5d` - [task-2215] 비슈누: get_drive_service_sa + upload_file_to_drive 테스트 추가 (5건)

## QC 셀프 체크

- [x] 1. 영향 파일: 4개 (gdrive.py, main.py, AdminNewsletters.tsx, AdminPremiumData.tsx)
- [x] 2. 엣지 케이스: 빈 파일명 → fallback "upload_{timestamp}", 잘못된 JWT → 401
- [x] 3. 작업 지시와 정확히 일치 확인 (Drive 전환, 폴더 구조, 통합 API)
- [x] 4. 에러 처리: Drive 업로드 실패 500, DB 저장 실패 500, JWT 401
- [x] 5. 관련 테스트: 해당 파일에 기존 테스트 없음
- [x] 6. 발견 이슈 3건 모두 직접 해결
- [x] 7. SOLID/DRY 준수: 기존 ensure_folder() 재사용, 서비스 계정 분리
- [x] 8. 인터페이스 변경: 새 엔드포인트 추가 (기존 API 미변경)
- [x] 13. L1 스모크테스트: 서버 기동 + API 호출 확인 완료

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


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


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


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


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


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


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


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


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

