구글드라이브 연동 설계
2026-07-26 · 집계 앱 v2 대상 · 제로-DB 원칙 유지 · v2(2026-07-27): 로그인만으로 연동
v2 — 구글 로그인만으로 연동 · 자동 동기화 (2026-07-27)
v1의 "사용자마다 자격 발급(BYO)"을 플랫폼 공용 자격 + 로그인 1회로 대체합니다. 사용자는 데이터 · 내보내기 → 🔗 구글 계정 연결만 누르면 드라이브 동기화가 켜집니다.
| 항목 | v2 결정 | 근거 |
|---|---|---|
| 자격 정보 | 플랫폼 공용 Client ID·API Key를 코드에 내장 (PLATFORM_GOOGLE) | 공개 가능 값(원본 도메인 제한). 사용자 발급 절차 제거 — 자체 자격 입력(BYO) 시 그것이 우선 |
| 권한 스코프 | drive.file + userinfo.email만 (비민감) | 비민감 스코프만 쓰면 Google 앱 검증 없이 프로덕션 게시 가능 → 누구나 로그인. drive.readonly는 제한 등급(심사 필요) → BYO 전용으로 격하 |
| 기본 폴더 고정 | BYO 모드 전용 | 폴더 고정 Picker 뷰는 drive.readonly 필요(v1에서 403 확인) |
| 동기화 | 자동 — 아래 흐름 | 수동 ☁ 정본 백업 버튼 대체 |
[연결] 로그인(동의 1회) → 드라이브에서 running_records.json 탐색
→ 있으면 다운로드·병합(복원), 없으면 현재 데이터로 생성
[저장] 기록 저장·수정·삭제·목표 변경 → 3초 디바운스 후 자동 업로드
[수신] 60초 폴링 + 탭 복귀 시 modifiedTime 비교 → 바뀌었으면 병합
[병합] record_id 합집합 · source_file_id 중복 스킵
같은 id 충돌은 updated_at이 더 최근인 수정본 채택(없으면 로컬 유지)
삭제는 tombstone(deleted_ids)으로 전파 — 다른 기기에서 부활 방지
목표(goals)는 updated_at이 더 최근인 쪽 채택
- 진짜 push(웹훅)는 서버가 필요해 제로-DB 원칙과 충돌 → 폴링으로 대체. 단일 사용자 기록 앱에는 충분.
- 토큰(≈1h)은 메모리에만 보관. 만료 시 팝업 없이 보류했다가 다음 클릭(제스처)에서 조용히 재발급 — 거부하면 10분간 재시도하지 않음.
- 상단 상태 pill이
드라이브 동기화 / 동기화 대기를 표시하며, 클릭하면 즉시 동기화. - 사진(IndexedDB)은 동기화하지 않음 — 원본 캡쳐가 이미 드라이브에 있고
source_file_id가 연결 고리.
🔧 개발자 1회 설정: ① Drive API + Picker API 사용 설정 ② OAuth 동의 화면에 비민감 스코프 2종만 등록(
drive.file, userinfo.email) 후 게시 상태를 '프로덕션'으로 전환(비민감만이라 검증 절차 없음) ③ 클라이언트 ID의 승인된 원본에 배포 도메인·localhost 추가 ④ 발급값을 index.html의 PLATFORM_GOOGLE에 입력 후 배포. 아래 §6은 BYO(고급) 사용자용으로 유지.1. 목표
드라이브의 러닝기록 폴더에 쌓이는 러닝 앱 캡쳐를 앱에서 직접 선택해 가져오고, 누적 정본(JSON)을 사용자의 드라이브에 백업한다. 서버·DB 없이 전 과정이 사용자 브라우저 ↔ 사용자 드라이브 사이에서만 동작한다.
2. 방식 결정
| 항목 | 결정 | 근거 |
|---|---|---|
| 인증 | Google Identity Services(GIS) 토큰 클라이언트 | 서버 불필요, 토큰은 메모리에만 보관(약 1시간) |
| 권한 스코프 | drive.file + drive.readonly | drive.file은 정본 JSON 쓰기용. 기본 폴더 고정·폴더 탐색 뷰는 drive.file만으로는 Picker가 403이라 읽기 스코프를 함께 요청 (전 과정이 사용자 브라우저 안에서만 동작) |
| 이미지 선택 | Google Picker (이미지 · 다중 선택 · 기본 폴더에서 바로 열림) | 폴더 자동 스캔(신규 감지)은 M2 |
| 자격 정보 | 설정 UI 입력 → localStorage | 코드에 하드코딩하지 않음(BYO). 재배포 없이 적용 |
| 정본 백업 | running_records.json 생성/갱신 | 파일 ID를 기억해 같은 파일을 계속 갱신 |
3. 이미지 선택 → 적용 → 집계 반영 흐름
[데이터 뷰: 드라이브에서 캡쳐 가져오기]
→ 토큰 확보(GIS, 최초 1회 동의) → Picker 열림(이미지 · 다중 선택)
→ 선택 완료 → 이미 가져온 파일(source_file_id 일치)은 자동 스킵
→ 나머지를 큐에 넣고 1장씩 순차 처리:
다운로드(files/{id}?alt=media) → 기록 추가 화면에 사진 표시
→ OCR 프리필 → 사용자 확인·수정 → 저장 → 다음 캡쳐 자동 진행
→ 저장된 레코드: source='drive', source_file_id=드라이브 파일 ID,
사진은 IndexedDB 보관 → 대시보드 집계·목표 달성도에 즉시 반영
- 수정 우선 원칙 유지 — OCR은 프리필일 뿐, 저장 전 사용자 확인·수정이 정식 경로.
- 중복 방지 — 드라이브 파일 ID 기준. 같은 캡쳐를 다시 선택해도 새 레코드가 생기지 않는다.
- 중단 자유 — 순차 처리 중 다른 화면으로 이동하면 남은 큐는 폐기(다시 가져오기로 재개).
- 기본 폴더 —
📁 기본 폴더로 캡쳐 폴더(예: 러닝기록)를 1회 지정하면, 이후 선택창이 매번 그 폴더 안에서 바로 열린다(해제는 ⚙ 설정에서).
4. 정본 백업 흐름
[데이터 뷰: 정본을 드라이브에 백업] → 최초: 드라이브에 running_records.json 생성 → 파일 ID 기억 → 이후: 같은 파일을 갱신(덮어쓰기) → 복원: 드라이브의 JSON을 'JSON 가져오기'로 불러오면 끝
사진 원본은 이미 드라이브(캡쳐)에 있으므로 정본 JSON에는 포함하지 않는다 — source_file_id가 연결 고리.
5. 보안 · 프라이버시
- 액세스 토큰은 메모리에만 보관, localStorage 저장 금지. 만료(≈1h) 시 재요청.
- Client ID·API Key는 공개 가능 값(원본 도메인 제한)이지만 코드에 넣지 않고 사용자 브라우저에만 저장.
drive.file스코프 특성상 이 앱은 사용자가 고른 파일과 자신이 만든 파일 외에는 아무것도 볼 수 없다.
6. 사전 준비 (BYO 고급 사용자용 — Google Cloud Console) — v2부터 일반 사용자는 불필요
- console.cloud.google.com → 새 프로젝트 (예:
habitree-run) - API 및 서비스 → 라이브러리:
Google Drive API,Google Picker API사용 설정 - OAuth 동의 화면: External → 앱 이름·이메일 입력 → 테스트 사용자에 본인 Gmail 추가
- 사용자 인증 정보:
· OAuth 클라이언트 ID(웹 애플리케이션) — 승인된 자바스크립트 원본에https://run-murex-theta.vercel.app과http://localhost:8000추가
· API 키 생성 (웹사이트 제한: 위 두 원본 권장) - 앱의 데이터 · 내보내기 → 드라이브 설정에 Client ID / API Key / 프로젝트 번호(선택) 입력
ℹ️ 설정값은 이 브라우저의 localStorage에만 저장되며 서버로 전송되지 않습니다.
7. 마일스톤
- M1 (이번 구현): Picker 다중 선택 가져오기 · 순차 검토 저장 · 중복 스킵 · 정본 백업/갱신 · 설정 UI
- M2: 폴더 자동 스캔(신규 캡쳐 감지,
drive.readonly동의 추가) · 자동 백업 옵션 - M3: BYOK 비전 모델로 OCR 정확도 향상 (케이던스·심박 등 격자 지표)
8. 한계 (M1)
- 토큰이 약 1시간마다 만료되어 재동의 팝업이 뜰 수 있다(같은 세션 내 자동 갱신 시도).
- 폴더를 통째로 감시하지 않는다 — 새 캡쳐는 Picker에서 직접 선택.
- OAuth 동의 화면이 '테스트' 상태면 지정한 테스트 사용자만 로그인 가능(개인 사용엔 충분).