d3ro-voice/memory/project_status.md
yunchan8804 97eb886ec3 feat(V2-2): Supabase 인프라 — 스키마 + RLS + Edge Functions 스캐폴딩
server/supabase/ 신규 디렉토리:

config.toml — Supabase CLI 설정
  - 프로젝트 ID, DB 포트, auth providers (Google/GitHub/Apple),
    edge_runtime, functions.*.verify_jwt 설정

migrations/ (PostgreSQL DDL, 시간순):
  - 20260409000001_initial_schema.sql
    12개 테이블: profiles, teams, team_members, meetings,
    meeting_memos, meeting_documents, transcripts, history,
    dictionary, memo_tags, daily_usage, subscriptions
    + 인덱스 + FK CASCADE + transcripts Realtime publication
  - 20260409000002_rls_policies.sql
    개인 전용(history/dictionary): user_id = auth.uid()
    팀 공유(meetings 등): 본인 OR team_members 조회 subquery
    daily_usage: 읽기만, 쓰기는 service_role RPC
  - 20260409000003_auth_triggers.sql
    handle_new_user — auth.users INSERT → profiles+subscriptions 자동 생성
    moddatetime — 8개 테이블 updated_at 자동 갱신
    increment_daily_usage — service_role 전용 쿼터 RPC
  - 20260409000004_storage_buckets.sql
    audio/exports/avatars 3개 버킷 + 경로 기반 접근 정책
    (파일 경로가 {user_id}/...로 시작해야 쓰기 허용)

functions/ (Deno/TypeScript Edge Functions):
  - _shared/cors.ts — CORS 헤더 + preflight 핸들러
  - _shared/auth.ts — requireUser (JWT 검증 + User 반환)
  - _shared/quota.ts — 티어별 쿼터 체크 + consume + service role client
  - stt-proxy/index.ts — Google Cloud STT 래퍼 스캐폴딩
    placeholder 응답, 실제 API 호출 코드는 주석으로 포함
  - llm-proxy/index.ts — Anthropic Messages API 래퍼 스캐폴딩
    티어별 허용 모델 정책 (free=Haiku, pro=Sonnet, team=Opus)

설계 문서:
  - docs/v2/phase-V2-2.md — 상세 설계 (스키마/RLS/Edge Functions/Realtime)
  - docs/v2/phase-V2-2-setup.md — 사용자 액션 가이드 (Supabase 계정/
    OAuth 등록/CLI/배포/검증)

apps/desktop typecheck 통과 (server/는 Deno 런타임이라 별도).
실제 Supabase 프로젝트 배포는 사용자가 phase-V2-2-setup.md 따라 수행.
2026-04-08 15:21:08 +09:00

207 lines
12 KiB
Markdown

# D3RO-VOICE 프로젝트 현황
> 마지막 갱신: 2026-04-08
> 규칙 13: 작업 완료 즉시 이 파일 갱신 의무
## 현재 단계
**V1 (Electron) 완료****V2 (Monorepo) 진행 중**
- Phase V2-1 ✅ 완료 (Monorepo 전환 — a/b/c/d)
- Phase V2-2 🟡 로컬 작업 완료, 사용자 액션 대기
- SQL 마이그레이션 4개 파일 + Edge Functions 2개 스캐폴딩 작성 완료
- Supabase 프로젝트 생성/OAuth 등록/배포는 사용자가 직접 수행
- 가이드: `docs/v2/phase-V2-2-setup.md`
- 다음: Phase V2-3 (Web App MVP — Next.js)
## 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 독립성 확보**: `I18nStorage` interface 추가 (load/save 어댑터)
- 기존 `window.electronAPI.config` 직접 호출 → `storage` prop 주입 패턴
- 기본값은 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}.tsx`
- `src/index.ts` barrel
- `packages/ui/package.json`: React/MUI/Emotion은 **peerDependencies**, `@d3ro/core`만 직접 dep
- `apps/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`: `@shared` alias 제거, `@d3ro/ui` alias 추가, 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}.ts`
- `apps/desktop/src/main/utils/{meeting-markdown,markdown-to-docx}.ts`
- `packages/core/src/index.ts` barrel 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.ts` alias 3개 섹션에 `@d3ro/core` 추가 + **M1 수정**: `resolve('src/shared')``resolve(__dirname, 'src/shared')`
- `externalizeDepsPlugin` exclude에 `@d3ro/core` 추가 (main/preload 둘 다)
- `apps/desktop/vitest.config.ts``@d3ro/core` alias 추가
- **일괄 치환**: `@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`)
- `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.ts` 1개만 남음 (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 CASCADE
- `migrations/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/배포)
**사용자 액션 대기 중** (로컬 외 작업)
1. Supabase 프로젝트 생성 (https://supabase.com)
2. `supabase login && supabase link --project-ref <REF>`
3. `supabase db push` (4개 마이그레이션 적용)
4. Google/GitHub OAuth 앱 등록 + Supabase Auth Providers 설정
5. Google Cloud STT 키 + Anthropic API 키 발급
6. `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_id` nullable 추가.
- **RLS 이중 패턴**: 개인 전용(history/dictionary)은 단순 `user_id = auth.uid()`. 팀 공유(meetings)는 본인 OR `team_members` 조회 subquery.
- **쿼터 집계**: `increment_daily_usage` RPC는 `SECURITY DEFINER` + `service_role` only. 일반 유저는 읽기만.
- **Realtime**: `transcripts` 테이블만 `supabase_realtime` publication에 추가 (회의 중 세그먼트 동기화 용).
- **Edge Functions placeholder**: 인증/쿼터 파이프라인은 완성, 실제 AI API 호출은 주석 처리된 실제 구현 코드 포함. V2-3 (Web MVP) 진행 전까지 유지.
### Phase V2-3~V2-8: 미착수
Web App MVP, 데스크톱 동기화, Mac 빌드, Mobile App, 팀 기능, 결제/출시
## 알려진 차단/이슈
- **@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
```