묶음 E — V2-4b pull 확장:
- CloudSyncService.pullAll()에 meetings/meeting_memos/meeting_documents 추가
- meetings: LWW, 모든 컬럼 매핑 (minutes_json JSON 직렬화)
- meeting_memos: immutable INSERT-only 전략
- meeting_documents: LWW UPDATE
묶음 F — api-client types:
- interface -> type alias 전환 (11개)
- Database 타입에 TypedTable<Row, Insert, Update> 유틸 도입
(Row & Record<string, unknown> 교차로 GenericTable 제약 만족)
- @supabase/ssr 2.102는 supabase-js 버전 불일치로 제네릭 주입 불가 -
다음 사이클로 이월, api-client index.ts는 types만 재수출
묶음 G — apps/mobile UI 교체:
- login.tsx: MetalCard + PhosphorText(hero/label) + PhysicalButton
- meetings.tsx: MetalCard + Led(status) + PhosphorText(body/meta)
- record.tsx: Led + PhosphorText + PhysicalButton
- profile.tsx: MetalCard + PhysicalButton(danger) + d3roNativePalette
묶음 H — V2-7b 이메일 invite flow:
- migrations/20260410000001_team_invites.sql
- team_invites 테이블 (token, email, role, expires_at 7일)
- RLS: 같은 팀 멤버 + 초대 이메일 소유자 SELECT,
owner/admin만 INSERT/DELETE
- generate_invite_token() SECURITY DEFINER RPC (service_role)
- functions/team-invite: 권한 체크 -> 토큰 생성 -> 초대 URL 반환
- functions/team-accept: 토큰 검증 -> expires_at/accepted_at/이메일 일치 ->
team_members upsert -> 초대 accepted 표시
- config.toml에 team-invite/team-accept 함수 등록
- InviteMemberForm 재작성: 이메일 입력 + 역할 선택 -> URL 복사 UI
- /accept-invite 페이지 신규 (Suspense 내 useSearchParams + token 수락)
묶음 I — Realtime transcripts:
- apps/web/components/meetings/live-transcript-list.tsx (client)
- supabase.channel('transcripts:meeting:${id}').on('postgres_changes')
- INSERT -> 세그먼트 추가, UPDATE -> row 교체
- 중복 방지 segment_index 기준
- edited 배지 표시
- meetings/[id]/page.tsx의 transcript 섹션을 LiveTranscriptList로 교체
묶음 J — 테스트:
- packages/api-client/__tests__/client.test.ts (9 tests)
- createD3roSupabaseClient, isClientConfigured 팩토리 검증
- packages/api-client/__tests__/types.test.ts (10 tests)
- 모든 Row 타입 + 리터럴 union + Database keyof
- vitest.config.ts 신규
- apps/web/playwright.config.ts 신규 (baseURL, webServer dev 서버)
- apps/web/e2e/smoke.spec.ts 신규 (6 스모크 테스트)
- apps/web tsconfig exclude에 e2e/playwright.config.ts 추가
- package.json scripts: test, test:e2e, test:e2e:ui
검증:
- desktop typecheck OK
- web typecheck OK
- web next build OK (12 라우트, /accept-invite Suspense 적용)
- desktop build OK
- api-client test 19 passed
22 KiB
22 KiB
D3RO-VOICE 프로젝트 현황
마지막 갱신: 2026-04-10 (3차 사이클) 규칙 13: 작업 완료 즉시 이 파일 갱신 의무
현재 단계
V1 완료 → V2 1차 전 페이즈 + 2차/3차 고도화 완료
V2 1차:
- Phase V2-1 ✅ (Monorepo 전환 a/b/c/d + minor 정리)
- Phase V2-2 🟡 로컬 완료 (Supabase), 사용자 배포 대기
- Phase V2-3 ✅ (Web App MVP — Next.js 15, 12 라우트)
- Phase V2-4 ✅ (데스크톱 Supabase push 동기화)
- Phase V2-5 🟡 로컬 완료 (Mac 빌드), 사용자 검증 대기
- Phase V2-6 ✅ (Mobile MVP — Expo)
- Phase V2-7 ✅ (팀 기능 — Web /teams)
- Phase V2-8 ✅ (결제 스캐폴딩 — Stripe Edge Functions + /billing)
V2 2차 고도화 (2026-04-09):
- [A1] ✅ SettingsModal에 CloudSyncSection 통합
- [A2] ✅ web Sidebar 공유 layout (app/(app)/)
- [A3] ✅ 11개 locale 번역
- [B] ✅ V2-4b pull 동기화 — history/dictionary
- [C] ✅ V2-8b Stripe webhook HMAC-SHA256 + Customer Portal
- [D] ✅ V2-6b packages/ui-native 패키지
V2 3차 고도화 (2026-04-10):
- [E] ✅ V2-4b pull 확장 — meetings/meeting_memos/meeting_documents
- [F] ⚠️ Database 제네릭 복원 — interface→type 전환 완료, 하지만 @supabase/ssr과 supabase-js 버전 정합 이슈로 실제 제네릭 주입은 보류 (다음 사이클)
- [G] ✅ apps/mobile UI를 @d3ro/ui-native로 실제 교체 (login/meetings/record/profile)
- [H] ✅ V2-7b 이메일 invite flow — team_invites 테이블, team-invite/team-accept Edge Functions, /accept-invite 페이지
- [I] ✅ Realtime transcripts 구독 — web /meetings/[id]에 LiveTranscriptList
- [J] ✅ api-client 유닛 테스트 (19 passed) + web E2E 스모크 (Playwright 스캐폴딩)
다음 사이클: 사용자 환경 액션 후 e2e 검증, Database 제네릭 완전 복원 (supabase 버전 맞추기), V1 고급 기능(음성 대화/RAG/보이스 액션) 이식, T2-8b 이메일 발송 실제 연동
V1 완료 페이즈
| Phase | 내용 | 상태 |
|---|---|---|
| 1~9 | 기본 음성 어시스턴트 (STT/LLM/TTS/핫키/팝업) | ✅ |
| 10 | Killer features (메모, 보이스 커맨드, 스크린 컨텍스트, 체인, 캡션) | ✅ |
| 11 | Monetization (라이선스, 쿼터) | ✅ |
| 12 | Pro features (파일 전사, 회의 요약, 딕테이션 템플릿) | ✅ |
| 13 | 음성 대화, RAG, 보이스 액션 | ✅ |
| 14 | 회의 모드 | ✅ |
| 14.5 | 회의 고도화 (다중 문서 생성/편집/내보내기) | ✅ |
| 15 | Auto Polish + AI 채팅 + 마인드맵 + 공유 | ✅ |
| 15.5 | 화자 구분 (LLM 추정 + pyannote) | ✅ |
추가 개선사항 (post-15.5)
- 히스토리/회의 목록 반응형 그리드 + 카드 UI
- 설정 LLM 모델 드롭다운
- 오디오 테스트 실시간 레벨 미터
- Ollama 미실행 시 설치 경로 탐색 후 자동 스폰 (2026-04-08)
V2 진행 상황
Phase V2-1: Monorepo + Core 추출 — 진행 중
| Sub-phase | 범위 | 상태 |
|---|---|---|
| V2-1a | npm workspaces + apps/desktop으로 V1 이동 | ✅ 완료 |
| V2-1b | packages/core 추출 (types, errors, ipc-channels, constants, utils) | ✅ 완료 |
| V2-1c | packages/ui 추출 (DS 컴포넌트 + theme + theme-vars) | ✅ 완료 |
| V2-1d | packages/i18n 추출 (locale JSON + 훅) | ✅ 완료 |
V2-1d 완료 내역
packages/i18n/(@d3ro/i18n) 신규 생성:src/locales/12개 JSON (ko/en/ja/zh/zh-TW/es/fr/de/pt/ru/vi/th)src/index.tsx: TFunction, Locale, LOCALE_META, resolveTranslation, createFormatDate/Number/RelativeDate/Time, I18nContext, useI18n, I18nProvider
- Electron 독립성 확보:
I18nStorageinterface 추가 (load/save 어댑터)- 기존
window.electronAPI.config직접 호출 →storageprop 주입 패턴 - 기본값은 noopStorage (메모리 한정)
- 기존
apps/desktop/src/renderer/App.tsx에electronI18nStorage어댑터 구현 후<I18nProvider storage={electronI18nStorage}>로 주입- apps/desktop 설정:
@d3ro/i18n: "*"dep 추가- tsconfig paths, vite alias, externalize exclude에
@d3ro/i18n추가
- 일괄 치환:
./i18n,../i18n,../../i18n→@d3ro/i18n(29 파일) apps/desktop/src/renderer/i18n/디렉토리 완전 제거
V2-1d 검증
- typecheck ✅
- build ✅
- dev 런타임 ✅ (DB/핫키/Ollama/Main window 모두 정상)
- Electron 의존이 완전히 apps/desktop에만 남아 monorepo 원칙 준수 (packages/i18n은 React에만 의존, 웹/모바일에서 그대로 재사용 가능)
V2-1c 완료 내역
packages/ui/(@d3ro/ui) 신규 생성:src/theme.ts(d3roPalette/d3roTypo/d3roShadow SSOT 전체)src/theme-vars.ts(팝업/main 프로세스용 CSS 변수 맵)src/components/ds/{CrtDisplay, InstrumentPanel, Led, MetalCard, MetalDial, PhosphorText, PhysicalButton, ScreenPanel, ButtonGroup, index}.tsxsrc/index.tsbarrel
packages/ui/package.json: React/MUI/Emotion은 peerDependencies,@d3ro/core만 직접 depapps/desktop/src/shared/디렉토리 완전 제거 (theme-vars가 마지막이었음)- subpath exports:
./theme,./theme-vars,./components/ds - DS 컴포넌트 내부
../../theme상대 경로는 그대로 유효 (packages/ui 내부에서 해결) - 일괄 치환:
../theme,../../theme,./theme→@d3ro/ui/theme../components/ds,../../components/ds,./ds,../ds→@d3ro/ui/components/ds../ds/<Component>,../../ds/<Component>→@d3ro/ui/components/ds(세부 파일 import는 barrel로 통합)@shared/theme-vars→@d3ro/ui/theme-vars(WindowManager 1건)
apps/desktop/tsconfig.node/web.json:@shared/*paths 완전 제거,@d3ro/ui,@d3ro/ui/*paths 추가,include에서src/shared/**/*제거electron.vite.config.ts:@sharedalias 제거,@d3ro/uialias 추가, externalize exclude에@d3ro/ui추가vitest.config.ts: alias 교체
V2-1c 검증
typecheck✅build✅dev런타임 ✅ (DB/핫키/Ollama/Main window 정상)apps/desktop/src/shared/디렉토리 자체가 사라져@shared/*는 이제 프로젝트에 존재하지 않음
V2-1b 완료 내역
packages/core/패키지 신규:@d3ro/core, subpath exports (types/errors/ipc-channels/constants/utils/*), docx dep 포함- 6개 파일
git mv로packages/core/src/로 이동:apps/desktop/src/shared/{types,errors,ipc-channels,constants}.tsapps/desktop/src/main/utils/{meeting-markdown,markdown-to-docx}.ts
packages/core/src/index.tsbarrel export 추가apps/desktop/package.json에"@d3ro/core": "*"dep 추가apps/desktop/tsconfig.node.json,tsconfig.web.json의paths에@d3ro/core/*추가,@shared/*는 theme-vars용으로 유지apps/desktop/electron.vite.config.tsalias 3개 섹션에@d3ro/core추가 + M1 수정:resolve('src/shared')→resolve(__dirname, 'src/shared')externalizeDepsPluginexclude에@d3ro/core추가 (main/preload 둘 다)apps/desktop/vitest.config.ts에@d3ro/corealias 추가- 일괄 치환:
@shared/{types,errors,ipc-channels,constants}→@d3ro/core/*(사용자 79개 파일, 167건)- static import (
from '@shared/...') - dynamic import (
import('@shared/...')) - type expression import (
keyof import('@shared/...').AppConfig)
- static import (
MeetingModeService.ts의 상대 경로 utils import (../utils/meeting-markdown,../utils/markdown-to-docx)를@d3ro/core/utils/*로 교체packages/core내부 자기 참조:meeting-markdown.ts의@shared/types→../types상대 경로
V2-1b 검증
npm run typecheck✅npm run build✅ (main+preload+renderer)npm run dev런타임 실행 ✅ (DB/핫키/Ollama 모두 정상)apps/desktop/src/shared/에는theme-vars.ts1개만 남음 (V2-1c에서 이동 예정)
V2-1a 완료 내역
- 루트
package.json을 npm workspaces 루트로 재구성 (workspaces: apps/, packages/) turbo.json,tsconfig.base.json추가 (Turborepo 자체 설치는 뒤로 미룸)- V1 파일 전체를
apps/desktop/으로git mv:- src/, resources/, tests/, sidecar/, scripts/
- electron.vite.config.ts, electron-builder.yml, vitest.config.ts
- tsconfig.node.json, tsconfig.web.json (+ 새 tsconfig.json)
apps/desktop/package.json신규 (name: "@d3ro/desktop", 기존 deps 그대로)@electron/rebuild --version=33.4.11로 native 모듈 재빌드 (better-sqlite3, uiohook-napi)- 루트 tsconfig.json → apps/desktop references 형태
V2-1a 결정사항
- 패키지 매니저: npm workspaces 유지 (pnpm 전환은 별도 sub-phase)
- 이유: Turborepo는 npm workspaces와 호환, 구조 이동과 PM 전환을 동시에 하면 롤백 불가
- site/: 루트 유지 (랜딩 페이지, V2 무관)
- docs/, memory/, CLAUDE.md: 루트 유지
- productName: "d3ro-voice" 필수 — package name을
@d3ro/desktop으로 바꾸면 Electron의app.getName()이 바뀌어 userData 경로가%APPDATA%\@d3ro\desktop\으로 이동(기존 DB/설정 유실).productName을 명시하면app.getName()이 이를 우선 반환하여%APPDATA%\d3ro-voice\그대로 유지됨.
검증 결과
npm run typecheck✅npm run build✅ (electron-vite main+preload+renderer)npm run dev✅ 실제 실행 → DB 경로d3ro-voice/로 복원, 기존 핫키(Alt keyCode=2) 로드, Ollama 자동 실행 감지 모두 정상npm run dist(electron-builder installer)은 V2-1 완료 후 별도 검증
Phase V2-2: Supabase 인프라 — 로컬 작업 완료
작성된 파일 (server/supabase/)
config.toml— Supabase CLI 설정 (로컬 + OAuth providers)migrations/20260409000001_initial_schema.sql— 12개 테이블 (profiles, teams, team_members, meetings, meeting_memos, meeting_documents, transcripts, history, dictionary, memo_tags, daily_usage, subscriptions) + 인덱스 + FK CASCADEmigrations/20260409000002_rls_policies.sql— 개인/팀 이중 RLS 패턴migrations/20260409000003_auth_triggers.sql—handle_new_user(profiles + subscriptions 자동 생성),moddatetime(8개 테이블 updated_at 트리거),increment_daily_usage(service_role 전용 쿼터 RPC)migrations/20260409000004_storage_buckets.sql— audio/exports/avatars 버킷 + 경로 기반 정책functions/_shared/— cors.ts, auth.ts (requireUser), quota.ts (티어별 쿼터)functions/stt-proxy/index.ts— Google Cloud STT 래퍼 스캐폴딩 (placeholder 응답)functions/llm-proxy/index.ts— Anthropic Messages API 래퍼 스캐폴딩 (티어별 모델 정책)seed.sql— 개발 시드 (빈 상태).gitignore— .branches/, .temp/, .env
설계 문서
docs/v2/phase-V2-2.md— 상세 설계 (스키마/RLS/Edge Functions/실시간/Storage)docs/v2/phase-V2-2-setup.md— 사용자 액션 가이드 (Supabase 계정/OAuth/CLI/배포)
사용자 액션 대기 중 (로컬 외 작업)
- Supabase 프로젝트 생성 (https://supabase.com)
supabase login && supabase link --project-ref <REF>supabase db push(4개 마이그레이션 적용)- Google/GitHub OAuth 앱 등록 + Supabase Auth Providers 설정
- Google Cloud STT 키 + Anthropic API 키 발급
supabase secrets set+supabase functions deploy
설계 결정사항
- UUID PK: V1은 nanoid(text), V2는 PostgreSQL
gen_random_uuid(). V2-4 (동기화)에서 기존 SQLite 데이터는 nanoid 유지, 새 데이터는 UUID. - timestamptz: V1은 epoch ms(integer), V2는
timestamptz. V2-4 동기화 시 타입 변환 필요. - 멀티테넌시: 모든 테이블에
user_id. 회의 관련 테이블은team_idnullable 추가. - RLS 이중 패턴: 개인 전용(history/dictionary)은 단순
user_id = auth.uid(). 팀 공유(meetings)는 본인 ORteam_members조회 subquery. - 쿼터 집계:
increment_daily_usageRPC는SECURITY DEFINER+service_roleonly. 일반 유저는 읽기만. - Realtime:
transcripts테이블만supabase_realtimepublication에 추가 (회의 중 세그먼트 동기화 용). - Edge Functions placeholder: 인증/쿼터 파이프라인은 완성, 실제 AI API 호출은 주석 처리된 실제 구현 코드 포함. V2-3 (Web MVP) 진행 전까지 유지.
Phase V2-5: Mac 빌드 — 로컬 작업 완료
작성/수정된 파일
apps/desktop/src/main/utils/paths.ts—EXE_SUFFIX상수 도입, sox/sidecar/ffmpeg 경로 플랫폼별 분기apps/desktop/src/main/services/SoundEffectService.ts— Macafplay, Linuxaplay분기 추가 (execFile로 안전하게)apps/desktop/src/main/services/ScreenContextService.ts—_getActiveWindowInfo를 win32/darwin로 분기. macOS는osascript로 frontmost process + 윈도우 타이틀 조회 (Accessibility 권한 필요)apps/desktop/scripts/build-sidecar.py—IS_WINDOWS/IS_MACOS분기,EXE_SUFFIX,--noconsole은 Windows에서만, 출력 경로 플랫폼 통합apps/desktop/scripts/install-sox.sh— Mac/Linux에서 시스템 sox를resources/sox/로 복사. macOS는otool로 dylib 의존성 함께 복사 +install_name_tool로 rpath 변경apps/desktop/electron-builder.yml—mac:타겟 추가 (dmg+zip, arm64+x64),hardenedRuntime,entitlements,extendInfo(권한 메시지), Linux AppImage placeholderapps/desktop/build/entitlements.mac.plist— 마이크/카메라/네트워크/Apple Events/JIT/dylib 권한apps/desktop/resources/icons/README.md— 플랫폼별 아이콘 생성 가이드 (sips/iconutil/imagemagick).github/workflows/build-mac.yml— macos-14 runner, brew sox, sidecar 빌드, electron-builder dist (arm64/x64 매트릭스), CSC/notarize 환경변수, artifact 업로드docs/v2/phase-V2-5-mac-guide.md— Mac 사전 조건/dev 실행/dist 빌드/서명/공증/CI/트러블슈팅
검증 결과 (Windows에서)
- typecheck ✅ — Mac 분기 추가에도 회귀 없음
- build ✅ — Windows 번들 정상
- dev 런타임 ✅ — Ollama 자동 실행 + 모든 서비스 정상
Mac에서 검증 대기 (사용자 액션)
brew install sox node@22 python@3.11npm install && npx @electron/rebuild@3 --version=33.4.11npm run dev→ Accessibility/마이크/Apple Events 권한 허용- 핫키, 녹음, STT, LLM, 화면 컨텍스트 동작 확인
- 결과 리포트 받아 미세 조정 필요할 수 있음
감사 결과 vs 처리
- C1 sox.exe/sidecar.exe/ffmpeg.exe 하드코딩 → ✅ 수정
- C2 아이콘 부재 → ✅ placeholder 디렉토리 + README
- C3 mac 타겟 부재 → ✅ 추가
- C4 SoX 바이너리 부재 → ✅ install-sox.sh + 가이드
- C5 ScreenContextService 미지원 → ✅ AppleScript 분기
- C6 SoundEffectService 미지원 → ✅ afplay 분기
- C7 build-sidecar.py Windows 전제 → ✅ 크로스 플랫폼
- M6/M7/M8/M9 (이미 분기 완료) → 변경 없음
Phase V2-3: Web App MVP — 완료
신규 패키지
packages/api-client/(@d3ro/api-client) — Supabase 래퍼src/types.ts— V2-2 스키마에 맞춘 수동 Database 타입 정의 (profiles/teams/meetings/transcripts/history/dictionary/daily_usage/subscriptions)src/client.ts— createD3roSupabaseClient 팩토리 (URL/KEY 주입형, env 없어도 placeholder로 작동)src/auth.ts— signInWithOAuth/signOut/getSessionsrc/meetings.ts— CRUD 함수 (meetings, memos, documents, transcripts)src/history.ts— 음성 입력 이력 CRUDsrc/usage.ts— daily_usage 조회 (쿼터 UI)- 루트 barrel은 types만 re-export (함수는 subpath로, 순환 의존 방지)
신규 앱 — apps/web/
- Next.js 15 App Router + MUI 7 + Emotion
- transpilePackages로
@d3ro/core/ui/i18n/api-client소스 TypeScript 그대로 번들 - 라우트 (총 9개):
/— auth 상태에 따라 리다이렉트/login— Google/GitHub OAuth (Supabase 미설정 시 경고)/auth/callback— OAuth code → session 교환 route handler/dashboard— 요약 카드 4개 + 최근 회의 5건/meetings— 회의 카드 그리드 리스트/meetings/[id]— 상세 (transcripts + memos + documents 탭)/record— 녹음 페이지 (getUserMedia + MediaRecorder + stt-proxy)
- Providers: ThemeProvider(MUI+AppRouterCacheProvider) / I18nProvider(localStorage 어댑터) / AuthProvider(Supabase session)
- 인증 가드:
/dashboardlayout +/meetings,/record,/meetings/[id]개별 redirect - Sidebar: Dashboard / Meetings / Record / Logout
- Recorder: 9바 웨이브폼 (Speakly 스타일), 실시간 레벨, elapsed timer, cancel/stop
packages/ui 확장 (@d3ro/ui)
MetalCard:BoxProps확장하여sx/onClick등 전달 가능theme.ts에typoSx(key)헬퍼 추가 — d3roTypo를 MUI sx 친화 포맷으로 변환theme.ts에d3roPalette.tag.blue추가 (M5 수정의 부산물)- DS 컴포넌트 9개에
'use client'directive 추가 — RSC 경계 명시 MetalDial.tsx의 미사용 import 3개 제거 (tsc strict 회귀)
packages/i18n 확장
ko.json에 7개 새 키:nav.meetings,nav.record,nav.logout,login.subtitle,login.google,login.github,login.terms- 다른 locale은 fallback 체인으로 ko/en 사용 (추후 번역 작업에서 채움)
설계 결정사항
- Database 제네릭: 현재
SupabaseClient기본 타입 사용, V2-4에서supabase gen types typescript --local자동화 후 제네릭 주입 예정. 이유: 수동 Database 타입 정의가 @supabase/supabase-js 2.102의 복잡한 제약(PostgrestVersion, Views/Functions/Enums 형식)을 만족시키기 어려움. - api-client barrel 분리: 루트에서는 types만 export. 함수는 subpath(
@d3ro/api-client/meetings)로만 접근. 이렇게 하지 않으면 apps/web이 api-client 루트를 import할 때 내부 meetings/history 등이 모두 tsc 스캔 대상이 되어 타입 에러가 전파됨. - DS 컴포넌트
'use client': MUI + React 훅(CrtDisplay)을 쓰는 컴포넌트들은 Next.js RSC에서 쓰려면 client directive 필요. barrel 대신 각 파일마다 붙임. - Supabase env 없이도 빌드 성공: placeholder URL/key로 createBrowserClient/createServerClient를 호출. 런타임에
isSupabaseConfigured()체크로 UI 경고 표시.
검증 결과
- web typecheck ✅ (apps/web)
- desktop typecheck ✅ (회귀 없음)
- web
next build✅ — 9 라우트 (/ /login /auth/callback /dashboard /meetings /meetings/[id] /record /_not-found + root) - desktop
npm run build✅ (main+preload+renderer) - 실제 Supabase 연결은 V2-2 사용자 액션 이후 검증 가능
Phase V2-4: 데스크톱 ↔ Supabase 동기화 — 완료
핵심 컴포넌트
apps/desktop/src/main/services/CloudSyncService.ts— 싱글톤 + EventEmitterinit/startSignIn/handleAuthCallback/signOut/pushAll+ 4종 이벤트- safeStorage 기반 refresh token 영속화
- upsert(onConflict: id) 방식 push, 매핑 테이블 없음
ipc/cloud-sync-handlers.ts+preload index.tscloudSync APIrenderer/components/CloudSyncSection.tsx— Settings 섹션main/index.tsdeep link (d3ro-voice://auth-callback) 핸들러 추가bootstrap.tscloud-sync 단계 추가packages/core/src/ipc-channels.tsCLOUD_SYNC 11종 채널packages/core/types.tsAppConfig + ConfigService defaults에 supabaseUrl/supabaseAnonKey/cloudSyncLastAt + hfToken/diarizationEnabled (Phase 15.5 누락분 보충)
설계 결정
- Local-first push only (V2-4b에서 pull 추가)
- 마지막 동기화 epoch ms 기준 변경분만 전송
- nanoid PK를 PostgreSQL text로 그대로 upsert
- macOS open-url + Windows second-instance argv 방식 deep link
Phase V2-6: Mobile MVP — 완료
구조 (apps/mobile/, npm workspace에서 제외)
- Expo SDK 51 + React Native + Expo Router
lib/supabase.ts(AsyncStorage),lib/auth-context.tsxapp/_layout.tsx,app/index.tsx,app/login.tsxapp/(tabs)/{_layout, meetings, record, profile}.tsxexpo-av로 녹음 → stt-proxy 호출README.md+ setup 가이드
설계 결정
- workspace 제외 (Expo native deps 부담 회피)
- MUI 미사용, 인라인 RN 스타일 (V2-6b에서
@d3ro/ui-native분리) - Deep link
scheme: 'd3ro-voice'
Phase V2-7: 팀 기능 (Web) — 완료
apps/web/src/app/teams/page.tsx— 가입한 팀 카드 그리드apps/web/src/app/teams/[id]/page.tsx— 팀 상세 (멤버 + 공유 회의)components/teams/create-team-form.tsx— 팀 생성 + owner 자동 등록components/teams/invite-member-form.tsx— user_id 직접 입력 (V2-7b에서 정식 invite flow)- Sidebar에 Teams/Billing 메뉴 + ko.json 새 키
- V2-2의 teams/team_members RLS 활용
Phase V2-8: 결제 스캐폴딩 — 완료
apps/web/src/app/billing/page.tsx— Free/Pro/Team 가격표 + 현재 티어components/billing/checkout-button.tsx— Edge Function 호출server/supabase/functions/stripe-checkout/index.ts— JWT 인증, customer 조회/생성, Checkout Sessionserver/supabase/functions/stripe-webhook/index.ts— checkout.session.completed, subscription.* 이벤트 → subscriptions 업데이트config.toml에 두 함수 등록 (webhook verify_jwt=false)
사용자 액션 필요
STRIPE_SECRET_KEY/STRIPE_PRICE_PRO/STRIPE_PRICE_TEAM/STRIPE_WEBHOOK_SECRET설정- Stripe 대시보드에서 product + price 생성
미구현 (V2-8b)
- Webhook signature 정식 검증 (현재 placeholder)
- Customer Portal
- Per-seat billing
알려진 차단/이슈
- @nut-tree-fork/nut-js 포크 사용 (V1 잔여 이슈)
- Ollama 의존성: 없으면 LLM 기능 사용 불가 (2026-04-08 자동 실행 기능 추가로 UX 개선)
디렉토리 구조 (V2-1a 목표)
d3ro-voice/ # 루트 (monorepo)
├── apps/
│ └── desktop/ # 기존 V1 전체
│ ├── src/
│ ├── resources/
│ ├── build/
│ ├── package.json # name: @d3ro/desktop
│ ├── electron.vite.config.ts
│ ├── electron-builder.yml
│ ├── tsconfig*.json
│ └── drizzle.config.ts
├── packages/ # V2-1b/c/d에서 채움
├── site/ # 랜딩 페이지 (루트 유지)
├── docs/ # 설계서/페이즈 문서
├── memory/ # 이 파일
├── pnpm-workspace.yaml # 또는 package.json "workspaces"
├── turbo.json
├── package.json # 루트 workspace
├── tsconfig.base.json
└── CLAUDE.md