# Google Drive OAuth2 설정 가이드

> 새 프로젝트에서 Google Drive 연동 시 참고. 삽질 방지용.

## 전체 흐름 요약

```
Google Cloud 프로젝트 설정 → OAuth 클라이언트 생성 → Drive API 활성화
→ OAuth 동의화면 테스터 등록 → OAuth2 Playground에서 Refresh Token 발급
→ .env.keys에 저장 → 서버에서 사용
```

## 1단계: Google Cloud Console 설정

### 1-1. OAuth 동의 화면
- Google Cloud Console → API 및 서비스 → OAuth 동의 화면
- 앱 이름, 지원 이메일 등 기본 정보 입력
- **테스트 사용자 추가**: "대상" 메뉴에서 본인 Gmail 추가 (안 하면 `403 access_denied`)

### 1-2. OAuth 클라이언트 생성
- API 및 서비스 → 사용자 인증 정보 → OAuth 2.0 클라이언트 ID 만들기
- 애플리케이션 유형: **웹 애플리케이션**
- **승인된 리디렉션 URI에 반드시 추가**:
  - `https://developers.google.com/oauthplayground` (Playground용)
  - 기타 필요한 redirect URI
- 생성 후 **Client ID**와 **Client Secret** 복사

### 1-3. Google Drive API 활성화
- API 및 서비스 → 라이브러리 → "Google Drive API" 검색 → **사용** 클릭
- 또는 직접 URL: `https://console.developers.google.com/apis/api/drive.googleapis.com/overview?project={프로젝트번호}`
- ⚠️ 활성화 후 1~2분 전파 시간 필요

## 2단계: Refresh Token 발급 (OAuth2 Playground)

### 2-1. Playground 접속
- URL: `https://developers.google.com/oauthplayground/`

### 2-2. ⚙ 설정 (★ 핵심! 반드시 먼저!)
- 우측 상단 **⚙ 톱니바퀴** 클릭
- **✅ "Use your own OAuth credentials"** 체크 (이거 안 하면 Playground 기본 클라이언트로 토큰이 발급되어 `unauthorized_client` 에러 발생!)
- OAuth Client ID: 1-2에서 만든 Client ID 입력
- OAuth Client secret: 1-2에서 만든 Client Secret 입력

### 2-3. Step 1: Authorize APIs
- 좌측 API 목록에서 선택하거나, **"Input your own scopes"** 입력란에 직접 입력:
  - `https://www.googleapis.com/auth/drive` (전체 Drive 접근)
  - 또는 `https://www.googleapis.com/auth/drive.file` (앱이 생성한 파일만)
- **Authorize APIs** 클릭
- Google 로그인 → "확인되지 않은 앱" 경고 시 **"계속"** 클릭 → 동의

### 2-4. Step 2: Exchange authorization code for tokens
- **"Exchange authorization code for tokens"** 클릭
- **Refresh Token** 복사 → `.env.keys`에 저장

## 3단계: 환경변수 설정

```bash
# .env.keys에 추가 (반드시 export 붙일 것!)
export {프로젝트}_GOOGLE_CLIENT_ID="xxx.apps.googleusercontent.com"
export {프로젝트}_GOOGLE_CLIENT_SECRET="GOCSPX-xxx"
export {프로젝트}_GOOGLE_REFRESH_TOKEN="1//04xxx"
export {프로젝트}_GOOGLE_DRIVE_FOLDER_ID="폴더ID"
```

⚠️ **`export` 빠뜨리면 서버 자식 프로세스에 전달 안 됨!** (shell 변수 vs 환경변수)

## 4단계: Python 서버 코드

```python
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

creds = Credentials(
    token=None,
    refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    token_uri="https://oauth2.googleapis.com/token",
)
service = build("drive", "v3", credentials=creds)
```

## 흔한 에러와 해결법

| 에러 | 원인 | 해결 |
|------|------|------|
| `unauthorized_client` | Playground 기본 클라이언트로 토큰 발급 | ⚙에서 "Use your own OAuth credentials" 체크 후 재발급 |
| `redirect_uri_mismatch` | OAuth 클라이언트에 Playground URI 미등록 | 승인된 리디렉션 URI에 `https://developers.google.com/oauthplayground` 추가 |
| `access_denied` (403) | OAuth 동의화면 테스트 사용자 미등록 | "대상"에서 본인 Gmail 추가 |
| `Drive API not enabled` | 프로젝트에서 Drive API 미활성화 | API 라이브러리에서 Google Drive API "사용" 클릭 |
| `invalid_scope` | 스코프 URL 잘못 선택 | "Input your own scopes"에 `https://www.googleapis.com/auth/drive` 직접 입력 |
| 환경변수 누락 | `.env.keys`에 `export` 없음 | 모든 변수에 `export` 접두어 필수 |
| `Argument list too long` | CLI에 긴 텍스트를 인수로 전달 | `subprocess.run(input=data)` stdin으로 전달 |
| `Nested Claude session` | CLAUDECODE 환경변수 상속 | `env.pop("CLAUDECODE", None)` |

## InsuWiki vs InsuRo 비교

- **InsuWiki**: 서비스 계정(Service Account) JSON 파일 사용 (`GOOGLE_DRIVE_CREDENTIALS_PATH`)
- **InsuRo**: OAuth2 사용자 인증 (Client ID + Secret + Refresh Token) 사용
- 서비스 계정은 공유 폴더 필요, 사용자 인증은 본인 Drive 직접 접근

---
작성: 2026-03-13 | InsuRo 금소법 PDF 업로드 구현 중 정리
