diff --git a/memory/big-bang-prompt.md b/memory/big-bang-prompt.md new file mode 100644 index 0000000..7678e86 --- /dev/null +++ b/memory/big-bang-prompt.md @@ -0,0 +1,289 @@ +# 빅뱅 사이클 시작 프롬프트 + +> 사용자가 다음 세션에서 이 파일 내용을 그대로 Claude에게 붙여넣어 시작. +> 이 안에 모든 컨텍스트 + 단계별 작업 + 검증 기준이 들어있음. + +--- + +## 너에게 (다음 세션 Claude에게) + +D3RO-VOICE 빅뱅 사이클을 시작한다. 데스크톱을 V1 single-user fat client에서 +**진짜 SaaS 데스크톱 클라이언트**로 완성하는 작업이다. Notion / Linear / Slack 데스크톱처럼 +"내 SaaS 계정의 맥북 클라이언트"가 되도록 한다. + +**시작 전 반드시 할 일** (이 순서대로): + +```bash +# 1. 핸드오프 + 현황 로드 +/session-handoff resume + +# 2. memory/handoff-latest.md 와 memory/project_status.md 처음부터 끝까지 읽기 +# 그 안에 이번 세션 직전(2026-04-11)까지의 모든 진행 + 알려진 이슈 + 환경 검증 결과가 있다 + +# 3. CLAUDE.md (root) — 강제 규칙 13개 숙지 +# 4. docs/design/00~03 — V1 설계서 (서비스 인터페이스, IPC, DB 스키마) +# 5. docs/v2/00-v2-master-plan.md + docs/v2/phase-V2-4.md — 데스크톱 ↔ Supabase 동기화 설계 +``` + +**아키텍처 강제 규칙** (CLAUDE.md 13개 + 본 사이클 추가): + +- 서비스 = 싱글톤 + EventEmitter +- IPC = 설계서 02의 ipc-channels.ts 그대로 +- 에러 = D3ROError + ErrorCode enum +- DB = drizzle-orm 스키마 그대로 +- `any` 절대 금지, `console.log` 절대 금지 (logger 사용) +- 테마 SSOT — 색상은 d3roPalette/d3roShadow CSS var (theme.ts 외 hex 금지) +- i18n — t() 함수만 (하드코딩 한국어/영어 금지) +- 작업 완료 즉시 `memory/project_status.md` 갱신 +- **본 사이클 추가**: cloud-first 정책. SQLite는 read-through cache 역할로만 운영, write는 우선 Supabase에. Supabase 실패 시 큐잉. + +--- + +## 직전 세션 요약 (2026-04-11, 마지막 커밋 `dd1c305`) + +### 이미 완료된 것 (다시 하지 말 것) + +1. **Mac 부트스트랩**: Node 22, sox, Supabase CLI, npm install, electron rebuild +2. **Supabase 배포**: 프로젝트 `llnocwyqvhgwpdjcqqyw`, 8 마이그레이션 + 10 Edge Functions +3. **V2-6차 [W][A~D]**: 웹 Emotion key 픽스, 테마 토글, Mermaid 렌더, 회의 오디오, Dashboard 차트 +4. **V2-6차 [E~L]**: 데스크톱 Mac 안정화 (단축키 표기, sidecar venv, paste, beep, qwen3 reasoning, 동적 포트 등) +5. **SaaS [1] 빌드 타임 Supabase env 주입**: + - `electron.vite.config.ts` `loadEnv('D3RO_')` + main `define` + - `apps/desktop/.env.local` (gitignored): `D3RO_SUPABASE_URL`, `D3RO_SUPABASE_ANON_KEY` + - `ConfigService.configGet('supabaseUrl'/'supabaseAnonKey')`은 빌드 타임 우선 + - `configSet`는 빌드 타임 모드일 때 변경 거부 +6. **SaaS [2] OAuth 강제 로그인 게이트**: + - `CloudSyncState.saasMode: boolean` 추가 + - `LoginScreen.tsx` 신규 (Google/GitHub만, URL/Key 입력 0) + - `App.tsx` `AuthGate` 상태머신 (`loading | login-required | authenticated | legacy`) + - `CloudSyncSection`이 saasMode일 때 URL/Key 필드 숨김 + +### 알려진 이슈 +- 데스크톱 SQLite는 여전히 단일 파일 (`userData/d3ro-voice/d3ro.db`) — **본 사이클 [3]에서 해결** +- Ollama `qwen3:4b`는 reasoning model이라 CPU에서 느림. `gemma3:1b` 등 빠른 모델 권장. +- Edge Function secrets (ANTHROPIC/OPENAI/RESEND/STRIPE)는 사용자가 키 안 줌 — 웹 /chat /knowledge는 키 채워지기 전 동작 안 함 + +--- + +## 🎯 빅뱅 목표 + +데스크톱 = SaaS 데스크톱 클라이언트. +한 PC에 여러 사용자가 OAuth로 번갈아 로그인해도 데이터가 절대 섞이지 않으며, +회원가입 → 온보딩 → 일일 사용 → 결제까지 전부 SaaS 흐름으로 일원화. + +--- + +## 📋 단계별 작업 (Phase 1~6, 의존성 순서대로) + +### Phase 1: 사용자별 SQLite 격리 (필수, 데이터 안전 핵심) + +**현재 상태**: 모든 데이터(history, dictionary, meetings, ...)가 단일 SQLite 파일에 저장. +한 PC에 사용자 A가 로그인 후 데이터 만들고, 로그아웃 후 사용자 B가 로그인하면 +사용자 B가 사용자 A의 데이터를 본다. + +**목표**: SQLite를 사용자별 디렉토리로 분리. +- `~/Library/Application Support/d3ro-voice/users/${userId}/d3ro.db` +- 로그인 시 그 사용자의 DB로 스위치 +- 로그아웃 시 DB 닫기 + +**구현 작업**: +1. `apps/desktop/src/main/db/index.ts` (또는 DatabaseService 위치) 확인 — 현재 어떻게 init되는지 +2. `getDatabase()`를 `getDatabase(userId | null)` 시그니처로 변경 — null이면 not initialized 에러 +3. **DB lifecycle 관리자** 신규 (또는 DatabaseService 강화): + - `openForUser(userId: string): Promise` — 새 user.db 열고 마이그레이션 적용 + - `closeCurrent(): Promise` — 현재 DB 닫기 + - `getCurrentUserId(): string | null` +4. **CloudSyncService 통합**: + - `signIn` 성공 직후 `database.openForUser(session.user.id)` + - 저장된 refresh token 복원 후 session 복구되면 `openForUser(session.user.id)` + - `signOut` 시 `closeCurrent()` + 모든 service stop +5. **모든 service init 타이밍 변경** — DB가 열려있을 때만 동작 + - HistoryService, DictionaryService, MemoService, MeetingModeService, ChainService, ... + - 일부는 lazy하게 동작해도 OK, 일부는 init에서 DB 의존 + - 권장 패턴: 서비스가 `getDatabase()`를 호출하는데 not-initialized면 throw → caller가 catch하고 빈 결과 반환 +6. **마이그레이션 정책** — 첫 로그인 사용자에게 기존 단일 DB 데이터 귀속: + - 첫 로그인 시점에 `userData/d3ro-voice/d3ro.db`가 존재하면 + - 그 사용자의 user 디렉토리로 `mv` (또는 copy + 원본 백업) + - 두 번째부터 로그인하는 사용자는 빈 DB로 시작 + - **dialog로 사용자에게 확인** — "기존 데이터를 이 계정으로 가져올까요?" +7. **renderer 측**: AuthGate가 'authenticated' 진입 시점에 DB가 열려있다고 가정 가능하도록 + main이 `auth-changed` emit 이전에 DB 열기 완료 + +**위험**: +- 기존 사용자의 데이터를 잘못 마이그레이션하면 영구 손실. 백업 필수. +- 모든 service init 순서를 다시 확인해야 함 — bootstrap.ts 순서 주의. + +**검증**: +- 로그인 → DB 열림 → 회의 녹음 → 데이터 저장 → 로그아웃 → DB 닫힘 +- 다시 같은 계정 로그인 → 데이터 복원 확인 +- 다른 OAuth 계정 로그인 → 빈 DB +- 마이그레이션: legacy 단일 DB가 있을 때 첫 로그인 시 dialog 확인 후 사용자 디렉토리로 이동 + +--- + +### Phase 2: 회원가입 / 첫 온보딩 흐름 + +**현재 상태**: LoginScreen에서 OAuth 로그인하면 바로 메인 UI 진입. +신규 사용자는 환영도 없고, 핫키 설정도 안 되어 있고, Whisper 모델 안내도 없음. + +**목표**: 첫 로그인 사용자에게 OnboardingModal 자동 표시. + +**구현 작업**: +1. `OnboardingModal.tsx`는 이미 존재 (V1) — 재활용 +2. `profiles` 테이블에 `onboarded_at: timestamptz | null` 컬럼 추가 (Supabase 마이그레이션) +3. 첫 로그인 후 `profiles.onboarded_at`이 null이면 OnboardingModal 자동 표시 +4. 완료 시 `update profiles set onboarded_at = now()` +5. OnboardingModal 단계: + - Step 1: 환영 + 마이크 권한 안내 (macOS는 첫 사용 시 다이얼로그) + - Step 2: 핫키 설정 (HotkeyRecordModal — Mac 표기 ⌘⇧1) + - Step 3: STT 모델 선택 (base/small/medium — 기본 base) + - Step 4: (선택) Ollama 모델 안내 — qwen3:4b 권장하되 더 빠른 옵션도 표시 + - Step 5: 완료 화면 + +**검증**: +- 신규 사용자 로그인 → 자동 OnboardingModal +- 완료 후 다음 로그인 → OnboardingModal 안 뜸 +- DB의 `onboarded_at` 컬럼 검증 + +--- + +### Phase 3: Cloud-first 정책 (가장 큰 변경) + +**현재 상태**: 모든 데이터가 SQLite에 먼저 저장되고, CloudSyncService가 나중에 push. +- 장점: 오프라인 사용 가능 +- 단점: 멀티 디바이스 sync 지연, 충돌 처리 복잡, 데이터가 SQLite에만 있는 동안 잃을 위험 + +**목표**: Supabase가 source of truth. 데스크톱은 read-through cache. +- write: Supabase 우선 → 성공 시 local cache 업데이트 → 실패 시 큐잉 + 재시도 +- read: local cache 우선 (UI 빠름) → 백그라운드에서 Supabase pull → 차이 있으면 cache 갱신 +- 오프라인 모드: 큐가 쌓이고 온라인 복귀 시 flush + +**구현 작업**: +1. **WriteQueue** 신규 — 실패한 write를 디스크에 영속화 (`userData/users/${userId}/write-queue.json`) +2. **CloudSyncService 확장**: + - `pushOne(table, row)` — 단건 즉시 push + - `enqueueWrite(table, row)` — 실패 시 큐잉 + - `flushQueue()` — 온라인 복귀 시 호출 +3. **HistoryService 등 각 서비스**: + - 기존 `addEntry(entry)`가 SQLite write만 했다면 → `await cloudSync.pushOne('history', entry)` 추가 + - 실패 시 `cloudSync.enqueueWrite(...)` + - 그 후 SQLite cache 업데이트 +4. **읽기 측**: + - 컴포넌트 진입 시 SQLite에서 즉시 표시 + - 백그라운드에서 `cloudSync.pullTable('history')` 실행 → 차이 발견 시 cache 갱신 + 이벤트 emit +5. **Realtime 구독 강화** — 이미 V2-5에서 시작. 다른 디바이스에서 push한 변경을 즉시 받아 cache 업데이트. + +**위험**: +- write latency 증가 — 사용자가 회의 종료 후 결과 보는 데까지 더 걸림. UI에서 optimistic update 필요. +- 충돌 처리 — 같은 row를 두 디바이스에서 동시 수정 시. 일단 last-write-wins (updated_at 기준), 나중에 CRDT 검토. +- 큐가 너무 커지면 디스크 압박 — 크기 제한 + 오래된 항목 폐기 정책. + +**검증**: +- 정상 흐름: 회의 녹음 → 즉시 Supabase에 INSERT 확인 (Supabase Studio) +- 오프라인: 네트워크 끊은 상태로 녹음 → 큐 쌓임 → 네트워크 복구 → 자동 flush +- 멀티 디바이스: 웹에서 회의 만들면 데스크톱에서 Realtime으로 즉시 보임 + +--- + +### Phase 4: 티어 enforcement (free quota) + +**현재 상태**: web 쪽 Edge Functions(`llm-proxy`, `embed-chunks` 등)는 `requireUser` + quota 체크가 있을 수 있음. +데스크톱은 LicenseService가 V1에 있지만 SaaS 티어와 동기화 안 됨. + +**목표**: 사용자가 free 티어면 데스크톱에서도 일일 quota 넘으면 차단. + +**구현 작업**: +1. **Supabase `daily_usage` 테이블** (이미 있음 from V2-2 마이그레이션) 활용 +2. **CloudSyncService.getSubscription()** 새 메서드 — `subscriptions` 테이블에서 현재 사용자 tier 가져오기 +3. **LicenseService.canUse(Feature)** 강화: + - Supabase에서 tier + 오늘 daily_usage 조회 + - free + 오늘 quota 초과면 deny +4. **각 quota 사용처에서 consume**: + - STT 호출 시 Supabase RPC `increment_daily_usage('stt')` + - LLM refine 시 `increment_daily_usage('llm')` + - sidecar는 로컬이지만 quota는 SaaS 차원에서 카운트 +5. **차단 시 UI**: + - UpgradePromptModal 자동 표시 (이미 V1에 있음) + - "Free 한도 도달, /billing에서 업그레이드" + +**검증**: +- free 사용자로 STT 50회 → 51번째 차단 +- pro 사용자로 무제한 +- 일자 바뀌면 quota 초기화 + +--- + +### Phase 5: 데스크톱 ↔ 웹 ↔ 모바일 데이터 일원화 검증 + +**작업**: +1. 데스크톱에서 회의 녹음 → 웹 `/meetings`에서 즉시 보임 (Realtime) +2. 웹에서 메모 추가 → 데스크톱에서 즉시 반영 +3. 모바일 Expo 앱에서도 같은 데이터 표시 +4. 회원가입 → 모든 기기에서 같은 식의 환영 흐름 +5. 결제 → 모든 기기에서 즉시 unlock + +**검증 시나리오**: +- 신규 사용자 가입 → 웹 → 데스크톱 → 모바일 순서로 로그인 → 같은 빈 상태 +- 데스크톱에서 회의 녹음 → 웹/모바일에서 5초 내 보임 +- 웹에서 회의 문서 편집 → 데스크톱에서 즉시 갱신 + +--- + +### Phase 6: Wrap-up + +1. `project_status.md` 갱신 — 빅뱅 [1~5] 모두 완료 표시 +2. 새 `memory/handoff-latest.md` 작성 (이전 것은 archive로) +3. 빅뱅 사이클 커밋들 push 여부는 사용자 명시 후 +4. 다음 세션에 다룰 후보: + - SQLite cache 자체를 제거하고 IndexedDB만 쓰기 (더 큰 변경) + - 데스크톱 결제 deeplink 강화 + - Knowledge 파일 업로드 (PDF/docx 파싱 — 사용자가 키 줘야 함) + - 모바일 기능 확장 + +--- + +## 🚨 작업 진행 시 강제 규칙 + +1. **모든 변경은 하나씩 검증** — 한 phase 끝나면 typecheck + dev 띄워서 흐름 확인 후 다음 +2. **DB 마이그레이션은 백업** — 사용자 데이터 손실 절대 금지. legacy DB는 archive 폴더로 옮기지 삭제 금지 +3. **커밋 단위는 phase별** — Phase 1 끝 → 커밋, Phase 2 끝 → 커밋, ... +4. **memory/project_status.md 매 phase 끝마다 갱신** (CLAUDE.md 규칙 13) +5. **설계서 참조 최우선** (CLAUDE.md 규칙 1) — 작업 전 docs/design/01 (서비스 명세), docs/design/02 (IPC), docs/v2/phase-V2-4 (데스크톱 동기화) 확인 +6. **사용자에게 보고는 간결하게** — 코드 dump 금지, 결과 + 검증 + 다음 단계만 + +--- + +## 🤝 사용자에게 묻기 좋은 시점 + +다음 중 하나라도 만나면 즉시 사용자 컨펌: +- DB 마이그레이션 실행 직전 (legacy DB → 사용자 디렉토리 이동) +- Supabase 스키마 마이그레이션 실행 직전 (`profiles.onboarded_at` 컬럼 추가) +- 큰 리팩토링이 예상보다 영향 범위 클 때 +- 외부 서비스 키가 필요할 때 (이번 사이클은 이미 박혀있는 D3RO_SUPABASE_*만 사용) + +--- + +## 🏁 성공 기준 + +- 신규 사용자가 LoginScreen → Google 클릭 → OnboardingModal → 메인 UI까지 한 번에 진행 +- 핫키 ⌘⇧1로 녹음 → 자동 paste + Supabase에 저장 (5초 내) +- 다른 디바이스(웹) 로그인 시 같은 데이터 보임 +- 한 PC에서 사용자 A 로그아웃 → 사용자 B 로그인 시 A 데이터 안 보임 +- Free 사용자 일일 quota 도달 시 데스크톱에서도 차단 + +이 5가지가 다 되면 빅뱅 1차 완료. 사용자에게 체크리스트 형태로 보고. + +--- + +## 데스크톱 .env.local 참고 (이미 박혀있는 값) + +``` +D3RO_SUPABASE_URL=https://llnocwyqvhgwpdjcqqyw.supabase.co +D3RO_SUPABASE_ANON_KEY=sb_publishable_0uo4UYYvUO2y-sVMFdYylA_hHv9qRt5 +``` + +DB 비밀번호 + service_role key는 `server/supabase/.env.local`에 있음 (gitignored). + +--- + +자, 이제 시작하자. 빅뱅이다. 🚀 diff --git a/memory/handoff-archive/2026-04-10-2300-windows-to-mac.md b/memory/handoff-archive/2026-04-10-2300-windows-to-mac.md new file mode 100644 index 0000000..69db08c --- /dev/null +++ b/memory/handoff-archive/2026-04-10-2300-windows-to-mac.md @@ -0,0 +1,173 @@ +# Session Handoff — 2026-04-10 + +> From: Windows 11 Pro (Yun Chan PC) +> Last commit: b386733 — "feat(V2-5차): pgvector 시맨틱 검색 + chat 스트리밍 + doc 편집 + VoiceAction + Realtime + storage + 메모" +> Branch: main +> Remote: https://gitlab.twentyoz.kr:8443/d3r0/voice.git +> 총 push 대기: 20 commits (이전 origin/main 대비) + +## ✅ 이번 세션에서 한 일 (1M 세션 5 사이클) + +### 1차 사이클: V2-1 ~ V2-8 전체 1차 구현 +- V2-1a/b/c/d — Monorepo 전환 (npm workspaces + Turborepo 설정, packages/core/ui/i18n 추출) +- V2-2 — Supabase 인프라 (12 테이블 + RLS + triggers + storage + 4 Edge Functions) +- V2-3 — Web App MVP (Next.js 15, 11 라우트) +- V2-4 — Desktop ↔ Supabase 동기화 (CloudSyncService, OAuth deep link, push only) +- V2-5 — macOS 빌드 (paths.ts 플랫폼 분기, electron-builder mac, entitlements, CI workflow) +- V2-6 — Mobile MVP (Expo, workspace 제외) +- V2-7 — 팀 기능 (web /teams) +- V2-8 — 결제 (Stripe Edge Functions + /billing) + +### 2차 사이클: 고도화 A~D +- Settings CloudSyncSection 통합 +- Web Sidebar 공유 layout (app/(app)/) +- 11개 locale 번역 +- V2-4b pull 동기화 (history/dictionary) +- V2-8b Stripe webhook HMAC-SHA256 검증 + Customer Portal +- packages/ui-native (RN DS 컴포넌트) + +### 3차 사이클: E~J +- V2-4b pull 확장 (meetings/memos/documents) +- api-client types interface→type 전환 +- apps/mobile을 @d3ro/ui-native로 실제 교체 +- V2-7b 이메일 invite flow (team_invites 테이블 + team-invite/accept Edge Functions + /accept-invite) +- Realtime transcripts 구독 (LiveTranscriptList) +- api-client 유닛 테스트 19 passed + web E2E 스캐폴딩 + +### 4차 사이클: K~P +- @supabase/ssr 0.10 + supabase-js 2.103 bump → **Database 제네릭 완전 주입** +- /chat 페이지 (ChatPanel + llm-proxy 호출) +- /knowledge 페이지 (knowledge_documents/chunks + tsvector + AddKnowledgeForm) +- team-invite Resend 이메일 발송 +- Expo Push notification (push_tokens + send-push + expo-notifications) +- meetings/[id] AI 문서 생성 (GenerateDocumentButton 4종 템플릿) + +### 5차 사이클: Q~V +- **pgvector** 활성화 + embedding(1536) + match_knowledge_chunks RPC + embed-chunks/search-knowledge Edge Functions + /knowledge 검색 UI +- **/chat SSE 스트리밍** — llm-proxy가 Anthropic 스트림 프록시, chat-panel progressive 렌더 +- **DocumentEditor** — MUI Dialog 기반 마크다운 편집/삭제 +- **/actions** (VoiceAction 이식) — 자연어 → JSON 파싱 → 실행 +- **Desktop CloudSync Realtime** — meetings/history/dictionary 변경 구독 + 자동 pullAll +- **Record → Storage 업로드** + 자동 meetings INSERT +- **MemoForm** — 회의 상세에서 메모 작성 +- **11개 locale** nav.chat/knowledge/actions 키 + +## 🚧 미완 / 남은 것 + +### 사용자 액션 필요 (환경 작업) +1. **Supabase 프로젝트 생성 + `db push`** — `docs/v2/phase-V2-2-setup.md` +2. **Google/GitHub OAuth Provider** 등록 +3. **Google Cloud STT API 키** + **Anthropic API 키** + **OpenAI API 키** (임베딩용) +4. **Stripe** 계정 + Price ID + Webhook secret +5. **Resend** API 키 (초대 이메일) +6. Mac에서 `npm run dev` 검증 — `docs/v2/phase-V2-5-mac-guide.md` + +### 코드 레벨 후보 (6차 사이클) +1. E2E 실제 실행 (Playwright browsers install) +2. Mermaid 렌더 (DocumentEditor에 마크다운 미리보기) +3. 회의 오디오 재생 (audio_storage_key → signed URL + 플레이어) +4. Dashboard 위젯 차트 (회의 시간/문서 수 추이) +5. Knowledge 파일 업로드 (PDF/docx 파싱) +6. Voice Input (/actions에 마이크 아이콘) +7. 다크모드 토글 (현재 다크 고정) +8. 팀 대시보드 + 알림 센터 + +## 🔑 Mac에서 이어받을 때 즉시 확인할 것 + +```bash +# 1. Git pull +git clone https://gitlab.twentyoz.kr:8443/d3r0/voice.git # 또는 git pull +cd voice +git log --oneline -5 # b386733 까지 있는지 + +# 2. 시스템 의존성 +brew install sox node@22 python@3.11 +# Ollama 선택 (로컬 LLM) +brew install ollama && ollama pull qwen3:4b + +# 3. npm workspace 설치 +npm install + +# 4. native 모듈 재빌드 +npx @electron/rebuild@3 --version=33.4.11 + +# 5. 검증 +cd apps/desktop && npx tsc --noEmit # desktop +cd ../web && npx tsc --noEmit && npm run build # web +cd ../.. && npx vitest run --root packages/api-client # tests + +# 6. dev 실행 +npm run dev # Electron 앱 뜸 +# 별도 터미널: cd apps/web && npm run dev # Next.js 앱 http://localhost:3000 + +# 7. mobile (선택) +cd apps/mobile && npm install # workspace 제외라 별도 +npm start # Expo dev +``` + +## ⚠️ 환경 차이 주의 + +- **Windows 전용 코드 없음** — V2-5에서 플랫폼 감사 완료 +- Mac: **Accessibility/마이크/Apple Events 권한** 첫 실행 시 다이얼로그 +- `resources/sox/`는 gitignored — `bash scripts/install-sox.sh`로 Homebrew sox 번들 (dist 빌드 시) +- `resources/icons/`에 실제 아이콘 파일 없음 — `docs/v2/phase-V2-5-mac-guide.md` §5 참고 +- Mac에서 `npm run dist` 하려면 아이콘 + sidecar 빌드 필요 (`python scripts/build-sidecar.py`) +- `apps/mobile/`은 workspace 제외 — 별도 `cd apps/mobile && npm install` + +## 📦 의존성 버전 + +| 패키지 | 버전 | +|---|---| +| Node.js | 22.12.0 | +| @supabase/supabase-js | 2.103.0 | +| @supabase/ssr | 0.10.2 | +| Next.js | 15.5.14 | +| Expo SDK | ~51 | +| Electron | 33.4.11 | +| React | 19 | +| MUI | 7 | +| TypeScript | 5.7+ | + +## 🧪 마지막 검증 상태 + +| 게이트 | 상태 | +|---|---| +| desktop typecheck | ✅ | +| desktop build | ✅ | +| web typecheck | ✅ | +| web build | ✅ (15 라우트) | +| api-client test | ✅ (19 passed) | +| mobile | workspace 제외 (별도 install 필요) | + +## 📊 누적 통계 + +- 총 커밋: 20개 (V2 시작 이후) +- Edge Functions: 12개 +- SQL 마이그레이션: 8개 +- 웹 라우트: 15개 +- 모바일 화면: 5개 +- 패키지: 6개 (core/ui/ui-native/i18n/api-client + 앱) +- 테스트: 19 passed +- 코드 추가: ~15,000줄+ + +## 🔗 참고 문서 + +| 파일 | 내용 | +|---|---| +| `memory/project_status.md` | 프로젝트 전체 현황 (SSOT) | +| `docs/v2/00-v2-master-plan.md` | V2 마스터 플랜 + 로드맵 | +| `docs/v2/phase-V2-5-mac-guide.md` | Mac 환경 전체 가이드 | +| `docs/v2/phase-V2-2-setup.md` | Supabase 배포 가이드 | +| `docs/v2/phase-V2-2.md` | Supabase 상세 설계 | +| `docs/v2/phase-V2-3.md` | Web App 설계 | +| `docs/v2/phase-V2-4.md` | 데스크톱 동기화 설계 | +| `.claude/skills/session-handoff/` | 이 핸드오프 스킬 | +| `.claude/skills/implement-phase/` | 구현 스킬 | + +## 🏁 Mac에서 재개 시 첫 명령 + +``` +/session-handoff resume +``` + +이 skill이 `memory/handoff-latest.md` → `project_status.md` → `CLAUDE.md`를 순서대로 읽고, 환경 검증 후 다음 작업을 제안합니다. diff --git a/memory/handoff-latest.md b/memory/handoff-latest.md index 69db08c..1b25928 100644 --- a/memory/handoff-latest.md +++ b/memory/handoff-latest.md @@ -1,173 +1,136 @@ -# Session Handoff — 2026-04-10 +# Session Handoff — 2026-04-11 09:40 (Mac, Big Bang 사이클 직전) -> From: Windows 11 Pro (Yun Chan PC) -> Last commit: b386733 — "feat(V2-5차): pgvector 시맨틱 검색 + chat 스트리밍 + doc 편집 + VoiceAction + Realtime + storage + 메모" -> Branch: main -> Remote: https://gitlab.twentyoz.kr:8443/d3r0/voice.git -> 총 push 대기: 20 commits (이전 origin/main 대비) +> From: macOS (yunchan-ui-MacBookPro) +> Last commit: `dd1c305` — feat(desktop): SaaS [1+2] 빌드 타임 env + OAuth 강제 로그인 게이트 +> Branch: `main` +> Remote: `https://gitlab.twentyoz.kr:8443/d3r0/voice.git` -## ✅ 이번 세션에서 한 일 (1M 세션 5 사이클) +## ✅ 이번 세션에서 한 일 (1M 컨텍스트, 데스크톱 Mac 안정화 + SaaS 1차) -### 1차 사이클: V2-1 ~ V2-8 전체 1차 구현 -- V2-1a/b/c/d — Monorepo 전환 (npm workspaces + Turborepo 설정, packages/core/ui/i18n 추출) -- V2-2 — Supabase 인프라 (12 테이블 + RLS + triggers + storage + 4 Edge Functions) -- V2-3 — Web App MVP (Next.js 15, 11 라우트) -- V2-4 — Desktop ↔ Supabase 동기화 (CloudSyncService, OAuth deep link, push only) -- V2-5 — macOS 빌드 (paths.ts 플랫폼 분기, electron-builder mac, entitlements, CI workflow) -- V2-6 — Mobile MVP (Expo, workspace 제외) -- V2-7 — 팀 기능 (web /teams) -- V2-8 — 결제 (Stripe Edge Functions + /billing) +### 환경 +- Mac 부트스트랩: Node 22 (brew), sox 14, supabase CLI 2.84 +- `npm install --force` (rollup win32 옵셔널 dep 충돌 우회) +- `@electron/rebuild@3 --version=33.4.11` 완료 +- Sanity build: desktop/web typecheck, web `next build`, api-client vitest 19 passed -### 2차 사이클: 고도화 A~D -- Settings CloudSyncSection 통합 -- Web Sidebar 공유 layout (app/(app)/) -- 11개 locale 번역 -- V2-4b pull 동기화 (history/dictionary) -- V2-8b Stripe webhook HMAC-SHA256 검증 + Customer Portal -- packages/ui-native (RN DS 컴포넌트) +### Supabase 배포 +- 프로젝트: `yunchan8804-blip's Project` (`llnocwyqvhgwpdjcqqyw`) +- `supabase link` + `db push` (8개 마이그레이션) +- `supabase functions deploy` (10개 Edge Functions) +- `apps/web/.env.local` + `server/supabase/.env.local` 작성 -### 3차 사이클: E~J -- V2-4b pull 확장 (meetings/memos/documents) -- api-client types interface→type 전환 -- apps/mobile을 @d3ro/ui-native로 실제 교체 -- V2-7b 이메일 invite flow (team_invites 테이블 + team-invite/accept Edge Functions + /accept-invite) -- Realtime transcripts 구독 (LiveTranscriptList) -- api-client 유닛 테스트 19 passed + web E2E 스캐폴딩 +### V2-6차 [W][A~D] — 웹 고도화 +- W. fix: AppRouterCacheProvider Emotion key `'d3ro-mui'` → `'mui'` (숫자 3 → SSR 500) +- A. Dark/Light/Theme 토글 (`theme-mode-context.tsx`, 12개 locale `theme.*`) +- B. DocumentEditor Mermaid + Markdown 렌더 (`markdown-preview.tsx`) +- C. 회의 오디오 재생 (`meeting-audio-player.tsx`, Storage signed URL 1h) +- D. Dashboard 14일 추이 차트 (recharts) -### 4차 사이클: K~P -- @supabase/ssr 0.10 + supabase-js 2.103 bump → **Database 제네릭 완전 주입** -- /chat 페이지 (ChatPanel + llm-proxy 호출) -- /knowledge 페이지 (knowledge_documents/chunks + tsvector + AddKnowledgeForm) -- team-invite Resend 이메일 발송 -- Expo Push notification (push_tokens + send-push + expo-notifications) -- meetings/[id] AI 문서 생성 (GenerateDocumentButton 4종 템플릿) +### V2-6차 [E~L] — 데스크톱 Mac 안정화 +- E. macOS 단축키 표기 ⌘⇧⌃⌥ (`format-hotkey.ts` + 호출 위치 4개) +- F. Sidecar `apps/desktop/sidecar/.venv/` 생성 + paths.ts venv 우선 +- G. HotkeyRecordModal 칩 'WIN' → ⌘ 표기 (getKeyName 플랫폼 분기) +- H. `bootstrap.ts` `hideRecordingTip` import 누락 → ReferenceError 픽스 +- I. macOS 시스템 beep 차단 — Electron `globalShortcut` 통합 (HotkeyService) +- J. Sidecar 동적 포트 (`_findFreePort`, orphan EADDRINUSE 회피) +- K. macOS Cmd+V paste — `TextInsertService` `LeftSuper`/`LeftControl` 분기 +- L. qwen3 reasoning 39초 — `/no_think` 토큰 + `stripReasoningBlocks` + 빈 응답 fallback -### 5차 사이클: Q~V -- **pgvector** 활성화 + embedding(1536) + match_knowledge_chunks RPC + embed-chunks/search-knowledge Edge Functions + /knowledge 검색 UI -- **/chat SSE 스트리밍** — llm-proxy가 Anthropic 스트림 프록시, chat-panel progressive 렌더 -- **DocumentEditor** — MUI Dialog 기반 마크다운 편집/삭제 -- **/actions** (VoiceAction 이식) — 자연어 → JSON 파싱 → 실행 -- **Desktop CloudSync Realtime** — meetings/history/dictionary 변경 구독 + 자동 pullAll -- **Record → Storage 업로드** + 자동 meetings INSERT -- **MemoForm** — 회의 상세에서 메모 작성 -- **11개 locale** nav.chat/knowledge/actions 키 +### SaaS [1+2] — 데스크톱 fat client → 진짜 SaaS 클라이언트 변환 +- [1] **빌드 타임 Supabase env 주입** + - `electron.vite.config.ts`: `loadEnv('D3RO_')` + main `define` + - `apps/desktop/.env.local`: `D3RO_SUPABASE_URL/ANON_KEY` + - `ConfigService`: `BUILD_TIME_*` 상수, `configGet/Set`이 빌드 타임 우선 + 변경 차단 +- [2] **OAuth 첫 실행 강제 게이트** + - `CloudSyncState.saasMode: boolean` 추가 + - `LoginScreen.tsx` 신규 (Google/GitHub만) + - `App.tsx` `AuthGate` 상태머신 + - `CloudSyncSection` saasMode일 때 URL/Key 입력 필드 숨김 -## 🚧 미완 / 남은 것 +### 미완 / 확실히 알려진 이슈 +- Ollama `qwen3:4b`가 4B reasoning 모델이라 CPU에서 느림 → 사용자가 더 빠른 모델로 교체 권장 (`gemma3:1b` 또는 `qwen2.5:3b`) +- Accessibility 권한 첫 실행 시 macOS 권한 다이얼로그가 떠야 함 (안 뜨면 시스템 설정 → 손쉬운 사용에 Electron 추가) +- 데스크톱 SQLite는 여전히 단일 파일 (`userData/d3ro-voice/d3ro.db`) — 한 PC에 여러 사용자가 OAuth 로그인 시 데이터 섞일 위험 ← **빅뱅 핵심 작업** -### 사용자 액션 필요 (환경 작업) -1. **Supabase 프로젝트 생성 + `db push`** — `docs/v2/phase-V2-2-setup.md` -2. **Google/GitHub OAuth Provider** 등록 -3. **Google Cloud STT API 키** + **Anthropic API 키** + **OpenAI API 키** (임베딩용) -4. **Stripe** 계정 + Price ID + Webhook secret -5. **Resend** API 키 (초대 이메일) -6. Mac에서 `npm run dev` 검증 — `docs/v2/phase-V2-5-mac-guide.md` +## 🚧 다음 세션: 빅뱅 사이클 (Desktop SaaS Client 완성) -### 코드 레벨 후보 (6차 사이클) -1. E2E 실제 실행 (Playwright browsers install) -2. Mermaid 렌더 (DocumentEditor에 마크다운 미리보기) -3. 회의 오디오 재생 (audio_storage_key → signed URL + 플레이어) -4. Dashboard 위젯 차트 (회의 시간/문서 수 추이) -5. Knowledge 파일 업로드 (PDF/docx 파싱) -6. Voice Input (/actions에 마이크 아이콘) -7. 다크모드 토글 (현재 다크 고정) -8. 팀 대시보드 + 알림 센터 +자세한 단계별 계획은 아래 "다음 세션 시작 프롬프트" 참조. -## 🔑 Mac에서 이어받을 때 즉시 확인할 것 +요약 목표: +1. **사용자별 SQLite 격리** — `userData/users/${userId}/d3ro.db` +2. **회원가입/온보딩 흐름** — OAuth 첫 가입 시 환영 + 마이그레이션 +3. **cloud-first 정책** — Supabase source of truth, local read-through cache +4. **멀티 디바이스 sync 강화** +5. **티어 enforcement** — 데스크톱에서도 free quota 차단 +6. **데스크톱 ↔ 웹 ↔ 모바일 데이터 일원화 검증** + +## 🔑 다음 세션 시작 시 즉시 할 일 ```bash -# 1. Git pull -git clone https://gitlab.twentyoz.kr:8443/d3r0/voice.git # 또는 git pull -cd voice -git log --oneline -5 # b386733 까지 있는지 +# 1. git pull +git pull origin main # 마지막 커밋 dd1c305 또는 그 이후 확인 -# 2. 시스템 의존성 -brew install sox node@22 python@3.11 -# Ollama 선택 (로컬 LLM) -brew install ollama && ollama pull qwen3:4b +# 2. 환경 검증 (이미 mac에 다 있음) +node --version # v22.x +which sox supabase # /opt/homebrew/bin/... +ls apps/desktop/sidecar/.venv/bin/python3 # exists +ls apps/desktop/.env.local # gitignored, exists -# 3. npm workspace 설치 -npm install +# 3. 워크스페이스 재설치 필요 시 +npm install --force -# 4. native 모듈 재빌드 -npx @electron/rebuild@3 --version=33.4.11 - -# 5. 검증 -cd apps/desktop && npx tsc --noEmit # desktop -cd ../web && npx tsc --noEmit && npm run build # web -cd ../.. && npx vitest run --root packages/api-client # tests - -# 6. dev 실행 -npm run dev # Electron 앱 뜸 -# 별도 터미널: cd apps/web && npm run dev # Next.js 앱 http://localhost:3000 - -# 7. mobile (선택) -cd apps/mobile && npm install # workspace 제외라 별도 -npm start # Expo dev +# 4. 데스크톱 dev 띄워서 LoginScreen 보이는지 확인 +npm run dev +# → 첫 화면이 LoginScreen이어야 함 (Google/GitHub 버튼) ``` -## ⚠️ 환경 차이 주의 +## ⚠ 환경 / 주의사항 -- **Windows 전용 코드 없음** — V2-5에서 플랫폼 감사 완료 -- Mac: **Accessibility/마이크/Apple Events 권한** 첫 실행 시 다이얼로그 -- `resources/sox/`는 gitignored — `bash scripts/install-sox.sh`로 Homebrew sox 번들 (dist 빌드 시) -- `resources/icons/`에 실제 아이콘 파일 없음 — `docs/v2/phase-V2-5-mac-guide.md` §5 참고 -- Mac에서 `npm run dist` 하려면 아이콘 + sidecar 빌드 필요 (`python scripts/build-sidecar.py`) -- `apps/mobile/`은 workspace 제외 — 별도 `cd apps/mobile && npm install` +- **gitlab 푸시는 사용자 명시 후에만**: 이번 세션에선 push 안 했음. 다음 세션에서 빅뱅 작업 끝나면 사용자 확인 후 `git push origin main` +- **Ollama 모델**: `qwen3:4b` 설치됨, 느리면 `ollama pull gemma3:1b` 권장 +- **Accessibility 권한**: 데스크톱 핫키 동작에 필수 — macOS 시스템 설정에서 수동 허용 +- **Supabase 외부 키**: ANTHROPIC/OPENAI/RESEND/STRIPE는 아직 안 주입됨 → web의 `/chat`, `/knowledge`, `/billing`은 키 채워지기 전엔 동작 안 함 (사용자가 키 줄 때 `supabase secrets set`) +- **데스크톱 .env.local**: gitignored, 이번 세션에서 작성. 만약 다음 세션이 다른 PC에서 시작하면 `apps/desktop/env.example`을 보고 직접 작성 필요 +- **DB 마이그레이션**: 빅뱅 [1] 사용자별 SQLite 격리 시 기존 단일 DB의 데이터를 첫 로그인 사용자에게 귀속시키는 마이그레이션 필요. 데이터 손실 주의 -## 📦 의존성 버전 - -| 패키지 | 버전 | -|---|---| -| Node.js | 22.12.0 | -| @supabase/supabase-js | 2.103.0 | -| @supabase/ssr | 0.10.2 | -| Next.js | 15.5.14 | -| Expo SDK | ~51 | -| Electron | 33.4.11 | -| React | 19 | -| MUI | 7 | -| TypeScript | 5.7+ | - -## 🧪 마지막 검증 상태 +## 📦 의존성 / 도구 +- Node 22.22.2 (brew) +- Python 3.13 (Framework) + venv at `apps/desktop/sidecar/.venv` +- Supabase CLI 2.84.2 +- Ollama (사용자가 별도 설치, qwen3:4b 모델) +- @electron/rebuild@3 33.4.11 +- Electron 33.4.11 +## 🧪 마지막 검증 | 게이트 | 상태 | |---|---| -| desktop typecheck | ✅ | -| desktop build | ✅ | -| web typecheck | ✅ | -| web build | ✅ (15 라우트) | -| api-client test | ✅ (19 passed) | -| mobile | workspace 제외 (별도 install 필요) | +| desktop tsc | ✅ | +| desktop build (electron-vite) | ✅ | +| desktop dev (실제 실행) | ✅ Main window shown | +| Hotkey ⌘⇧1 + globalShortcut | ✅ accelerator=Shift+Cmd+1 등록 | +| Sidecar venv spawn | ✅ 한국어 96% 1.5초 오디오 → 426ms 전사 | +| Cmd+V paste | ✅ (사용자 확인 "되긴한다") | +| LLM refine | ⚠ qwen3:4b reasoning 끄기 후 첫 시도 미확인 — 사용자 검증 필요 | +| LoginScreen | ⚠ 이번 세션 마지막에 빌드만, 사용자 시각 확인 미수행 | -## 📊 누적 통계 +## 📊 누적 통계 (이번 세션 포함) +- 신규 커밋: 13개 (V2-6차 [A~L] + SaaS [1+2]) +- 신규 파일: + - apps/web: theme-mode-context, markdown-preview, meeting-audio-player, meetings-trend-chart + - apps/desktop: format-hotkey, LoginScreen, sidecar/.venv, .env.local, env.example +- 수정: ConfigService(빌드 타임), CloudSyncService(saasMode), CloudSyncSection, App.tsx, HotkeyService(globalShortcut), LocalSTTService(_findFreePort), LocalLLMService(/no_think + stripReasoning), TextInsertService(Cmd+V), bootstrap(hideRecordingTip), ... -- 총 커밋: 20개 (V2 시작 이후) -- Edge Functions: 12개 -- SQL 마이그레이션: 8개 -- 웹 라우트: 15개 -- 모바일 화면: 5개 -- 패키지: 6개 (core/ui/ui-native/i18n/api-client + 앱) -- 테스트: 19 passed -- 코드 추가: ~15,000줄+ +## 🔗 참고 +- `memory/project_status.md` — 전체 현황 + V2-6차 [A~L] + SaaS [1+2] +- `docs/v2/00-v2-master-plan.md` — V2 마스터 플랜 +- `docs/v2/phase-V2-4.md` — 데스크톱 동기화 설계 +- `apps/desktop/.env.local` — 빌드 타임 Supabase env (gitignored) +- `apps/desktop/env.example` — 템플릿 +- `memory/handoff-archive/2026-04-10-2300-windows-to-mac.md` — 이전 핸드오프 -## 🔗 참고 문서 +--- -| 파일 | 내용 | -|---|---| -| `memory/project_status.md` | 프로젝트 전체 현황 (SSOT) | -| `docs/v2/00-v2-master-plan.md` | V2 마스터 플랜 + 로드맵 | -| `docs/v2/phase-V2-5-mac-guide.md` | Mac 환경 전체 가이드 | -| `docs/v2/phase-V2-2-setup.md` | Supabase 배포 가이드 | -| `docs/v2/phase-V2-2.md` | Supabase 상세 설계 | -| `docs/v2/phase-V2-3.md` | Web App 설계 | -| `docs/v2/phase-V2-4.md` | 데스크톱 동기화 설계 | -| `.claude/skills/session-handoff/` | 이 핸드오프 스킬 | -| `.claude/skills/implement-phase/` | 구현 스킬 | +# 🎯 다음 세션 시작 프롬프트 (사용자가 그대로 복사해서 붙여넣기) -## 🏁 Mac에서 재개 시 첫 명령 - -``` -/session-handoff resume -``` - -이 skill이 `memory/handoff-latest.md` → `project_status.md` → `CLAUDE.md`를 순서대로 읽고, 환경 검증 후 다음 작업을 제안합니다. +다음 세션에서 그대로 붙여넣을 프롬프트는 별도 파일에: +**`memory/big-bang-prompt.md`** diff --git a/memory/project_status.md b/memory/project_status.md index dbea666..19ccd50 100644 --- a/memory/project_status.md +++ b/memory/project_status.md @@ -135,6 +135,52 @@ V2 6차 고도화 (2026-04-11): - [K] 🐛 fix(desktop): macOS 텍스트 붙여넣기 — `TextInsertService._insertViaClipboard`가 `nut.Key.LeftControl + V` 하드코딩이라 macOS에서는 paste 안 됨 (Cmd+V여야). `pasteModKey = process.platform === 'darwin' ? LeftSuper : LeftControl` 분기. 잘못된 클립보드 검증("text 그대로면 정상") 로직 제거 (macOS는 Ctrl+V가 noop이라 항상 거짓 양성). macOS는 paste 후 sleep 250ms. - [L] 🐛 fix(desktop): qwen3 reasoning 39초 + 빈 응답 — qwen3:4b는 reasoning model이라 응답에 `...` 블록을 길게 출력, 본문이 비어 _completeSession("")로 paste 호출 안 됨. SYSTEM_PROMPTS 전체에 `/no_think` 토큰 추가 (qwen3 비활성화, 다른 모델은 무시). `stripReasoningBlocks()` 추가 — ``/`` 태그 안전망. `processText`가 stripped 결과 길이 0이면 원본 transcript fallback + warn 로그. +## SaaS 데스크톱 클라이언트 1차 (2026-04-11) + +> 사용자 표현: "설계가 잘못된 것 같다" +> 실제 상황: 백엔드/웹/모바일은 100% multi-tenant SaaS, 데스크톱만 V1 single-user 잔재로 +> 사용자가 Settings에서 Supabase URL/Key를 직접 입력해야 하는 UX였음. +> Notion/Linear/Slack 데스크톱 패턴으로 정정. + +### SaaS [1] 빌드 타임 Supabase env 주입 +- `electron.vite.config.ts`: `defineConfig(({ mode }) => ...)`로 변경, `loadEnv(mode, cwd, 'D3RO_')` + 로 `.env.local` 자동 로드 → main 번들에 `define`으로 inline 박음 +- `apps/desktop/.env.local` 생성 (gitignored): `D3RO_SUPABASE_URL`, `D3RO_SUPABASE_ANON_KEY` +- `apps/desktop/env.example` 템플릿 (committed) +- `ConfigService`: + - `BUILD_TIME_SUPABASE_URL` / `BUILD_TIME_SUPABASE_ANON_KEY` 상수 (process.env에서 읽음) + - `isSupabaseBuildTimeConfigured()` 헬퍼 export + - `configGet('supabaseUrl'/'supabaseAnonKey')`은 빌드 타임 값 절대 우선 + - `configSet`는 빌드 타임 모드일 때 supabaseUrl/AnonKey 변경 거부 + +### SaaS [2] OAuth 첫 실행 강제 게이트 +- `CloudSyncState`에 `saasMode: boolean` 필드 추가 +- `LoginScreen.tsx` 신규: Google/GitHub 버튼만, MetalCard + PhosphorText, URL/Key 입력 노출 0 +- `App.tsx` `AuthGate` 상태머신: `loading | login-required | authenticated | legacy` + - `saasMode && !authenticated` → LoginScreen 강제 + - `saasMode && authenticated` → 기존 AppLayout + - `!saasMode` → legacy (기존 동작 유지) + - `onAuthChanged` 구독으로 로그인 후 자동 전환 +- `CloudSyncSection` (Settings 안): + - `saasMode`일 때 URL/Key 입력 필드 완전 숨김 (OAuth 버튼만) + - `!saasMode` (legacy)일 때만 기존 입력 화면 유지 +- 검증: 이전엔 항상 떴던 `CloudSync disabled — Supabase URL/key not configured` 로그 사라짐 + +### SaaS [3] 사용자별 SQLite 격리 — **다음 세션(빅뱅) 예정** + +## 빅뱅 다음 세션 목표 (2026-04-11 wrap-up) + +데스크톱을 진짜 SaaS 클라이언트로 완성. 자세한 계획 + 단계별 prompt는 +`memory/handoff-latest.md` 참조. + +핵심: +1. 사용자별 SQLite 격리 (`userData/users/${userId}/d3ro.db`) +2. 회원가입/온보딩 흐름 — OAuth 첫 가입 시 환영 + 기본 설정 +3. cloud-first 정책 — Supabase가 source of truth, 로컬 cache는 read-through +4. 멀티 디바이스 sync 강화 (Realtime + last-write-wins → CRDT 검토) +5. 결제/티어 enforcement (free 일일 quota, 데스크톱에서도 차단) +6. 회의/지식 web ↔ 데스크톱 일원화 + ## SaaS 아키텍처 진단 (2026-04-11) > 사용자 우려: "single-user로 잘못 만들어진 것 같다"