d3ro-voice/memory/handoff-latest.md
윤찬 817b4a2580 chore: 빅뱅 사이클 1차 wrap-up — 핸드오프 + Phase 5 체크리스트
빅뱅 Phase 1~6 완료 (Phase 5는 OAuth provider 연결 대기):
- Phase 1: 사용자별 SQLite 격리 (816f527)
- Phase 1.5: 로컬 모드 entry point (1a01cea)
- Phase 2: 온보딩 + Cloud Sync 티저 (f1321df)
- Phase 4: 티어 enforcement 로컬 해방 (46b77f1)
- Phase 3: 로컬 우선 sync + login_required UI (ceadf1b) [재정의]
- Phase 5: 3 클라이언트 일원화 검증 → OAuth provider 대기
- Phase 6: wrap-up (이 커밋)

handoff-latest 새로 작성, 이전 스냅샷은 archive로 이동.
project_status에 Phase 5 OAuth 연결 가이드 + 검증 체크리스트 기록.
2026-04-11 10:37:40 +09:00

9.8 KiB

Session Handoff — 2026-04-11 (빅뱅 사이클 완료)

From: macOS (yunchan-ui-MacBookPro) Last commit: ceadf1b — feat(desktop): SaaS [6] 로컬 우선 sync + login_required UI (빅뱅 Phase 3) Branch: main (origin 기준 19 commits ahead) Remote: https://gitlab.twentyoz.kr:8443/d3r0/voice.git

이번 세션에서 한 일 — 빅뱅 1차 완료

비즈니스 모델 확정

  • 무료 로컬 = 사용자 entry point (회원가입 없이 즉시 킬러 피처 체험, onboarding funnel 입구)
  • 유료 클라우드 SaaS = 수익화 (OAuth 로그인 + pro 구독 + premium LLM + 멀티 디바이스 sync)
  • 이전 세션 SaaS [2] OAuth 강제 게이트 철회

완료한 Phase (모든 커밋 로컬 main에 있음, push 미수행)

Phase 1 — 사용자별 SQLite 격리 (816f527)

  • db/index.ts 전면 재설계: openForUser(userId), closeCurrent(), getDatabase(), isLocalMode()
  • 경로: userData/users/${userId}/d3ro.db (uuid) 또는 _local (익명)
  • legacy d3ro-voice.db → silent archive rename
  • CloudSyncService _onAuthenticated(session, reason) + _onSignOut() 단일 진입점 (SSOT)
  • signOut 시퀀스: VoiceMode cancel → MeetingMode stopRecording → Caption stop → Realtime stop → auth.signOut → token clear → LicenseService.resetToFree → closeCurrent → openLocal → auth-changed(null) emit

Phase 1.5 — 로컬 모드 = entry point (1a01cea)

  • LOCAL_USER_ID = '_local' + openLocal() 헬퍼
  • bootstrap.ts: database step 복원. 앱 시작 시 openLocal() 호출 → LoginScreen 없이 즉시 메인 UI
  • App.tsx: AuthGate 상태머신 완전 제거, 항상 AppLayout 렌더
  • 로그아웃 시 closeCurrent()openLocal() — 앱은 계속 로컬 모드로 동작

Phase 2 — 온보딩 (f1321df)

  • AppConfig.onboardingCompleted: boolean 정식화 (types.ts + ConfigService defaults)
  • 타입 캐스팅 3곳 제거 (AppLayout/OnboardingModal/SettingsModal)
  • OnboardingModal 5 → 6 step: 환영 → 마이크 → 핫키 → Ollama → Cloud Sync(선택) → 완료
  • Cloud Sync step은 saasMode === true일 때만 노출. tagline + 3 benefit + "설정에서 언제든지 로그인". 로그인 버튼 미노출 (부담 zero, 유일 OAuth 진입점은 Settings→CloudSync)
  • i18n ko/en onboarding.cloud.* 6 키

Phase 4 — 티어 enforcement (46b77f1) — 로컬 해방 + 클라우드 gate

  • 기존 정책 폐기: DICTATION 20/일, LLM_PROCESS 10/일, LIVE_CAPTION/VOICE_MEMO/SCREEN_CONTEXT/MEETING_SUMMARY 등의 pro/pro_plus lock 전부 삭제
  • 신규 feature: Feature.PREMIUM_LLM, Feature.CLOUD_SYNC
  • CLOUD_FEATURES Set + canUse()isLocalMode() 감지: 익명 로컬에서 cloud feature → login_required
  • syncFromCloud(tier) + resetToFree() — CloudSyncService가 호출
  • _fetchSubscriptionTier(userId) — Supabase subscriptions 테이블 조회

정책 매트릭스

Feature 익명 _local 로그인 free pro pro_plus
로컬 16종 (DICTATION~OS_AUTOMATION) 무제한 무제한 무제한 무제한
PREMIUM_LLM login_required 🔸 5/일 🔸 500/일 무제한
CLOUD_SYNC login_required

Phase 3 — 로컬 우선 sync + login_required UI (ceadf1b) — 재정의

  • 원래 "Supabase = source of truth" 방향 철회. 로컬이 SoT, 클라우드 mirror.
  • _initialSync(reason) 신규 — _onAuthenticated 끝에 fire-and-forget pushAll → pullAll
  • FeatureAccess.reason / UpgradePromptEvent.reason'login_required' 추가
  • UpgradePromptModal login_required 케이스: "로그인 필요" title + "로그인하기" 버튼 → d3ro:open-settings custom event (tab=cloud)
  • i18n ko/en license.loginRequired.* 3 키

Phase 5 — 3 클라이언트 일원화 검증OAuth provider 대기 (미완)

Phase 6 — wrap-up (이 파일)

🚧 남은 작업 / 다음 세션 우선순위

즉시 해야 할 것 (사용자 수동)

  1. Supabase Google/GitHub OAuth provider 연결:
  2. Edge Function secrets 주입 (사용자가 키 제공 시):
    • supabase secrets set ANTHROPIC_API_KEY=... OPENAI_API_KEY=...
    • 이게 없으면 PREMIUM_LLM 호출 경로 완성해도 실제 동작 안 함
  3. 커밋 push 여부 결정:
    • 이번 세션 신규 커밋 5개 (816f527..ceadf1b) + 이전 세션 커밋 포함 총 19 ahead
    • 사용자 확인 후 git push origin main

빅뱅 후속 (코드 작업, 다음 세션 이후)

  • PREMIUM_LLM 호출 경로 구현 (Phase 3.2):
    • LocalLLMService에 cloud mode 추가 또는 새 PremiumLLMService
    • llm-proxy Edge Function(Anthropic/OpenAI) 호출
    • canUse(PREMIUM_LLM) gate + consumeQuota hook
    • ConfigService에 "premium LLM 사용" 스위치 추가
  • auto push on write (Phase 3.3):
    • VoiceMode session-completed → pushOne('history', row) 자동 호출
    • MeetingMode 종료 → pushOne('meetings', row)
    • 실패 시 queue에 enqueue
  • 오프라인 WriteQueue (Phase 3.4):
    • userData/users/${userId}/write-queue.json 디스크 영속화
    • 온라인 복귀 시 자동 flush
  • Phase 5 실증: OAuth provider 연결 후 데스크톱 ↔ 웹 ↔ 모바일 데이터 일원화 검증 (체크리스트는 memory/project_status.md SaaS [7] 섹션)
  • 온보딩 10개 locale 번역 — 현재 ko/en만. ja/zh/zh-TW/es/fr/de/pt/ru/vi/th 추가
  • Database 마이그레이션 — legacy d3ro-voice.db가 있는 환경 실측 — 이번 세션에서 archiveLegacyDbIfExists는 로그인 시점에만 발동하므로, 테스트 데이터(d3ro-voice.db)가 있는 사용자가 실제 로그인하기 전까지는 정상 상황 아래 트리거되지 않음

🔑 다음 세션 시작 시 즉시 할 일

# 1. git pull (혹시 push 했다면)
git pull origin main   # 마지막 커밋 ceadf1b 또는 그 이후 확인

# 2. 환경 확인 (이미 mac에 다 설치됨)
node --version                                      # v22.22.2
ls apps/desktop/sidecar/.venv/bin/python3           # exists
ls apps/desktop/.env.local                          # gitignored, D3RO_SUPABASE_*

# 3. dev 재시작
npm run dev
# → LoginScreen 없이 바로 메인 UI 진입
# → 모든 기존 기능 (LIVE_CAPTION, VOICE_MEMO, MEETING_SUMMARY 등) pro lock 해제됨
# → ⌘⇧1 녹음 quota 제한 없음
# → Settings → About → "온보딩 다시 보기" 로 새 6 step 플로우 확인 가능

⚠ 환경 / 주의사항

  • 모든 기존 사용자 데이터는 users/_local/d3ro.db에 있음. 이번 세션 dev 실행 중 생성됨.
  • Legacy d3ro-voice.db는 archiveLegacyDbIfExists()가 첫 openForUser 시점에서만 rename → 로그인 전까지는 원본 위치에 남아있을 수 있음 (bootstrap의 openLocal()openForUser('_local')이므로 실제로는 archive 트리거됨. 실측 필요).
  • OAuth provider 미연결: _onAuthenticated 코드는 준비됐지만 로그인 버튼 누르면 Supabase가 400 에러 반환할 것. Phase 5 실증 전 provider 연결 필수.
  • 기존 pro 락이 걸려있던 테스트들: LicenseService 단위 테스트가 있다면 회귀 가능. 일단 typecheck/build는 통과.

📦 의존성 / 도구

  • Node 22.22.2 (brew)
  • Python 3.13 + venv at apps/desktop/sidecar/.venv
  • Supabase CLI 2.84.2
  • Ollama (사용자 설치, qwen3:4b 모델)
  • Electron 33.4.11

🧪 마지막 검증

게이트 상태
desktop tsc --noEmit
desktop npm run build
dev 런타임 cold start local database opened: users/_local/d3ro.dbMain window shown
파일시스템 users/_local/d3ro.db 생성 확인
온보딩 UI 새 플로우 사용자 확인 "잘 나온다"
OAuth 로그인 실측 provider 미연결
Phase 4 로컬 해방 런타임 실측 ⚠ 사용자가 dev 재시작 후 기존 pro lock 기능 써봐야 확정
커밋 push 사용자 컨펌 대기

📊 누적 통계 (이번 세션)

  • 신규 커밋: 5개 (816f527, 1a01cea, f1321df, 46b77f1, ceadf1b)
  • 총 변경: 16 파일 (코드 10 + i18n 4 + memory 1 + types 1)
  • 로컬 19 commits ahead of origin/main

🔗 참고

  • memory/project_status.md — 전체 현황 + 빅뱅 Phase 1~6 상세
  • memory/big-bang-prompt.md — 원래 빅뱅 계획 (일부 철회됨 — SaaS [2] OAuth 강제 게이트 철회, Phase 3 재정의)
  • memory/handoff-archive/2026-04-11-0940-prior-bigbang.md — 이번 세션 시작 시점 스냅샷
  • docs/v2/phase-V2-4.md — 데스크톱 sync 원래 설계 (Phase 3 재정의 이전)
  • apps/desktop/.env.local — 빌드 타임 Supabase env (gitignored)

🎯 성공 기준 진행률

# 기준 상태
1 신규 사용자: LoginScreen 없이 바로 메인 UI → 로컬 기능 즉시 사용 (Phase 1.5)
2 온보딩 자동 표시 + Cloud Sync 티저 (로그인 부담 zero) (Phase 2)
3 핫키 ⌘⇧1 녹음 → 로컬 DB 저장, quota 제한 없음 (Phase 4 정책) — 실측 대기
4 로컬 기능 16종 전부 free 사용 가능 (pro/pro_plus lock 해제) (Phase 4 정책) — 실측 대기
5 다른 디바이스 로그인 시 같은 데이터 OAuth provider 대기
6 사용자 A → signOut → 사용자 B 로그인 시 A 데이터 안 보임 OAuth provider 대기
7 PREMIUM_LLM / CLOUD_SYNC 접근 시 login_required UI (Phase 3) — 실측 대기
8 Free 사용자 PREMIUM_LLM 5/일 quota 초과 차단 ⚠ feature 정의만, 호출부 미구현