packages/api-client (@d3ro/api-client) 신규: - types.ts: 12개 테이블 Row/Insert/Update 타입 + Database 형식 - client.ts: createD3roSupabaseClient 팩토리 (placeholder fallback) - auth.ts: signInWithOAuth/signOut/getSession/onAuthStateChange - meetings.ts: 회의 + 메모 + 문서 + 전사 CRUD - history.ts: 음성 입력 이력 CRUD - usage.ts: 일일 쿼터 + 구독 조회 - 루트 barrel은 types만 re-export, 함수는 subpath import 전용 apps/web (@d3ro/web) 신규 — Next.js 15 App Router: - transpilePackages: @d3ro/core, ui, i18n, api-client - ThemeProvider (MUI + AppRouterCacheProvider) - I18nProvider (localStorage 어댑터) - AuthProvider (Supabase session Context) - 라우트 9개: - / (auth 상태 기반 리다이렉트) - /login (Google/GitHub OAuth, 미설정 경고) - /auth/callback (code -> session 교환) - /dashboard (요약 카드 + 최근 회의) - /meetings (카드 그리드 리스트) - /meetings/[id] (transcripts/memos/documents) - /record (getUserMedia + MediaRecorder + stt-proxy) - Sidebar, 인증 가드, 9바 웨이브폼, 레벨 미터 packages/ui 확장: - MetalCard가 BoxProps 상속 (sx/onClick 등 전달) - theme.ts에 typoSx(key) 헬퍼 추가 (d3roTypo -> MUI sx 변환) - d3roPalette.tag.blue 추가 (M5 정리 포함) - DS 컴포넌트 9개에 'use client' directive - MetalDial 미사용 import 제거 packages/i18n 확장: - ko.json에 7개 새 키 (nav.meetings/record/logout, login.*) 설계 결정: - Database 제네릭 현재는 기본 타입 (V2-4에서 supabase gen types로 자동화) - api-client barrel은 타입만 노출해서 컴파일 전파 차단 - DS 컴포넌트 client 경계 명시 - env 없어도 Next.js 빌드 성공 (placeholder URL/key) 검증: - web typecheck OK - desktop typecheck OK (회귀 없음) - web next build OK (9 라우트 정적/동적 생성) - desktop build OK (회귀 없음)
8.4 KiB
Phase V2-3: Web App MVP — 상세 설계
브라우저에서 로그인 → 녹음 → 전사 → 회의록 생성 → 편집을 수행하는 Next.js 앱. V2-1의
packages/{core,ui,i18n}과 V2-2의 Supabase 인프라를 소비하는 첫 consumer.
1. 목표
- 로그인 가능한 웹앱: Supabase Auth OAuth (Google/GitHub)
- 녹음 → 전사 파이프라인: 브라우저 마이크 → Edge Function(stt-proxy) → DB 저장
- 회의록 CRUD: 리스트, 상세, 편집, 삭제
- AI 문서 생성: 회의록 → llm-proxy 호출 → meeting_documents 저장
- UI 재사용:
@d3ro/uiDS 컴포넌트를 Next.js에서 사용 - Supabase 미설정 상태에서도 빌드 성공: env 없어도
next build통과 (런타임 placeholder)
2. 디렉토리 구조
apps/web/
├── package.json # @d3ro/web, Next.js 15, MUI 7, @d3ro/core/ui/i18n/api-client
├── next.config.mjs # transpilePackages: ['@d3ro/ui', '@d3ro/core', '@d3ro/i18n', '@d3ro/api-client']
├── tsconfig.json # extends ../../tsconfig.base.json
├── next-env.d.ts
├── .env.local.example # NEXT_PUBLIC_SUPABASE_URL / ANON_KEY 템플릿
├── public/
└── src/
├── app/
│ ├── layout.tsx # 루트 layout + ThemeProvider + I18nProvider
│ ├── page.tsx # / — 랜딩/로그인 리다이렉트
│ ├── login/
│ │ └── page.tsx # /login — OAuth 버튼
│ ├── auth/
│ │ └── callback/
│ │ └── route.ts # OAuth 콜백 처리
│ ├── dashboard/
│ │ ├── layout.tsx # 인증 가드 + 사이드바
│ │ └── page.tsx # /dashboard — 요약 카드
│ ├── meetings/
│ │ ├── page.tsx # /meetings — 리스트
│ │ └── [id]/
│ │ └── page.tsx # /meetings/[id] — 상세
│ └── record/
│ └── page.tsx # /record — 녹음 페이지
├── components/
│ ├── providers/
│ │ ├── theme-provider.tsx # MUI + @d3ro/ui theme
│ │ ├── i18n-provider.tsx # @d3ro/i18n + localStorage 어댑터
│ │ └── auth-provider.tsx # Supabase session context
│ ├── layout/
│ │ ├── sidebar.tsx
│ │ └── top-bar.tsx
│ └── record/
│ ├── mic-recorder.tsx # getUserMedia + MediaRecorder
│ └── waveform-bars.tsx # 웨이브 애니메이션 (DS 스타일)
└── lib/
├── supabase-server.ts # RSC/route handler용 서버 클라이언트
├── supabase-browser.ts # 클라이언트 컴포넌트용
└── route-guards.ts # requireUser() 헬퍼
3. packages/api-client 설계
별도 패키지로 만들어 web과 desktop이 공유.
packages/api-client/
├── package.json # @d3ro/api-client
├── tsconfig.json
└── src/
├── index.ts # barrel
├── client.ts # createSupabaseClient (url + key 주입형 팩토리)
├── types.ts # Database row 타입 (supabase gen types 대체)
├── auth.ts # signInWithOAuth, signOut, getSession
├── meetings.ts # CRUD 함수 (list, get, create, update, delete)
├── history.ts # history 테이블 CRUD
└── usage.ts # daily_usage 조회 (쿼터 표시용)
주요 설계 포인트
- 환경변수 주입형:
createClient(url, anonKey)를 factory로 제공. 각 앱(web/desktop)이 자체 env를 읽어 주입. - 타입: V2-2의 SQL 스키마와 동기화. Supabase CLI로
supabase gen types typescript실행 가능하지만 V2-3에서는 수동 타입 정의로 시작 (추후 자동화). - SSR/CSR 양립: Next.js RSC에서도 쓸 수 있도록 createClient는
SupabaseClient를 반환만 하고 상태를 들고 있지 않음.
4. Next.js 환경 설정 고려사항
4.1 transpilePackages
Monorepo의 .ts 소스를 Next.js가 직접 해석하도록:
// next.config.mjs
export default {
transpilePackages: ['@d3ro/core', '@d3ro/ui', '@d3ro/i18n', '@d3ro/api-client']
}
4.2 MUI + Emotion RSC 호환
Next.js 15 App Router는 기본 RSC. MUI 컴포넌트는 'use client' 필요.
→ 루트 layout은 서버 컴포넌트 유지, <ThemeProvider>를 client component로 래핑.
→ @mui/material-nextjs/v15-appRouter의 AppRouterCacheProvider 사용.
4.3 @d3ro/ui 컴포넌트 소비
DS 컴포넌트는 MUI 기반이라 그대로 사용 가능. 단, 컴포넌트마다 'use client'가 필요한지 확인.
→ packages/ui/src/components/ds/*.tsx는 MUI Box/Typography 사용 → client 필요
→ 해결: 각 컴포넌트 상단에 'use client' 추가하거나, Next.js 측에서 client wrapper 도입.
→ MVP는 page.tsx에 'use client' 지정하는 방식으로 단순 처리.
4.4 i18n
@d3ro/i18n은 React Context 기반이라 client 전용.
I18nProvider는 localStorage를 storage 어댑터로 주입.
5. 인증 흐름 (OAuth)
/login (버튼 클릭)
→ supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: '/auth/callback' } })
→ Supabase가 Google OAuth로 리다이렉트
→ 사용자 동의 후 → /auth/callback?code=...
→ route handler가 code를 session으로 교환
→ Supabase가 쿠키에 세션 저장
→ /dashboard로 리다이렉트
세션 관리
- 브라우저:
@supabase/ssr패키지의createBrowserClient(쿠키 기반) - RSC/route handler:
createServerClient+ Next.jscookies()
6. 페이지별 요구사항
/login
- 중앙 카드: "D3RO Voice" 로고 + OAuth 버튼 2개 (Google / GitHub)
- 이메일 로그인은 V2-3에서는 제외 (이메일 확인 플로우 복잡, OAuth만)
- 이미 로그인이면
/dashboard로 리다이렉트
/dashboard
- 사이드바: Dashboard / Meetings / Record / Settings / Logout
- 상단 카드 4개: 이번 주 회의 수, 총 녹음 시간, 사용 쿼터, 구독 티어
- 하단: 최근 회의 5건
/meetings
- 카드 그리드 (반응형)
- 각 카드: 제목, 날짜, 상태, duration, 첫 메모 1개
- 우상단: "새 회의" 버튼 →
/record - 검색/필터 (MVP에서는 생략)
/meetings/[id]
- 탭: Transcript / Memos / Documents
- Transcript: 세그먼트 리스트, 시간 스탬프, 화자 라벨
- Memos: 메모 리스트
- Documents: 생성된 문서 목록 + "새 문서 생성" 버튼 (llm-proxy 호출)
/record
- MicRecorder 컴포넌트
- 상태: idle | recording | processing | done
- 버튼: Start / Stop / Cancel
- 실시간 waveform (9개 바, Speakly 스타일)
- 녹음 완료 시:
- Blob → Edge Function
stt-proxy로 POST - 응답의 transcript 표시
- "회의로 저장" 버튼 →
meetingsINSERT
- Blob → Edge Function
7. Supabase 미설정 시 동작
빌드/dev가 항상 성공해야 함. env 없으면:
createSupabaseClient가 dummy client 반환 (모든 쿼리 에러 throw 대신 빈 배열)/login페이지에 "Supabase가 설정되지 않았습니다" 경고 배너- 사용자가
.env.local에 env 추가 후pnpm dev재시작
8. 빌드/실행
# 루트에서
npm install # apps/web 새 workspace 인식
# web dev 실행
npm run dev --workspace=@d3ro/web # http://localhost:3000
# web 빌드
npm run build --workspace=@d3ro/web
9. 검증 전략
V2-3는 빌드 통과 + RSC/client 경계 정리가 MVP 목표. 실제 Supabase 연결 + 녹음 동작 검증은 사용자가 V2-2 배포 후 수행.
typecheck— workspace 전체build— apps/desktop (회귀 없음) + apps/web (Next.js 빌드)- apps/web은 Supabase env 없이도 빌드 성공해야 함
10. 결제/Stripe
마스터 플랜에는 포함되지만 V2-3 범위 외로 미룸. → Stripe 연동은 V2-8 (결제 + 출시)에서 일괄 구현.
11. 다음 페이즈와의 관계
- V2-4 (데스크톱 동기화): 이번에 만든
@d3ro/api-client를 apps/desktop에서도 재사용 - V2-6 (Mobile): Expo 앱도 같은
@d3ro/api-client사용 - V2-7 (팀 기능): web의 /teams 페이지 추가
- V2-8 (결제): /billing 페이지 + Stripe webhook 핸들러 (Supabase Edge Function)