feat(V2-3): Web App MVP — Next.js 15 + @d3ro/api-client

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 (회귀 없음)
This commit is contained in:
yunchan8804 2026-04-09 02:52:31 +09:00
parent 9742b2109a
commit d0c33ca259
190 changed files with 6167 additions and 18 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<!DOCTYPE html><html><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><title data-next-head="">500: Internal Server Error</title><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-c1c45c0a5396db8e.js" defer=""></script><script src="/_next/static/chunks/framework-62cd8852d1df20ee.js" defer=""></script><script src="/_next/static/chunks/main-00c1270eb89db337.js" defer=""></script><script src="/_next/static/chunks/pages/_app-552893fa4c6cf2f7.js" defer=""></script><script src="/_next/static/chunks/pages/_error-19ea2e6a6da76d0f.js" defer=""></script><script src="/_next/static/borqQ3kbNhV8uztMrJ99R/_buildManifest.js" defer=""></script><script src="/_next/static/borqQ3kbNhV8uztMrJ99R/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{},"buildId":"borqQ3kbNhV8uztMrJ99R","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>

View file

@ -0,0 +1 @@
"use strict";(()=>{var a={};a.id=636,a.ids=[636],a.modules={8732:a=>{a.exports=require("react/jsx-runtime")},23573:(a,b,c)=>{Object.defineProperty(b,"__esModule",{value:!0}),Object.defineProperty(b,"default",{enumerable:!0,get:function(){return i}});let d=c(88485),e=c(8732),f=d._(c(82015)),g=c(40231);async function h(a){let{Component:b,ctx:c}=a;return{pageProps:await (0,g.loadGetInitialProps)(b,c)}}class i extends f.default.Component{render(){let{Component:a,pageProps:b}=this.props;return(0,e.jsx)(a,{...b})}}i.origGetInitialProps=h,i.getInitialProps=h,("function"==typeof b.default||"object"==typeof b.default&&null!==b.default)&&void 0===b.default.__esModule&&(Object.defineProperty(b.default,"__esModule",{value:!0}),Object.assign(b.default,b),a.exports=b.default)},40231:(a,b)=>{Object.defineProperty(b,"__esModule",{value:!0}),!function(a,b){for(var c in b)Object.defineProperty(a,c,{enumerable:!0,get:b[c]})}(b,{DecodeError:function(){return o},MiddlewareNotFoundError:function(){return s},MissingStaticPage:function(){return r},NormalizeError:function(){return p},PageNotFoundError:function(){return q},SP:function(){return m},ST:function(){return n},WEB_VITALS:function(){return c},execOnce:function(){return d},getDisplayName:function(){return i},getLocationOrigin:function(){return g},getURL:function(){return h},isAbsoluteUrl:function(){return f},isResSent:function(){return j},loadGetInitialProps:function(){return l},normalizeRepeatedSlashes:function(){return k},stringifyError:function(){return t}});let c=["CLS","FCP","FID","INP","LCP","TTFB"];function d(a){let b,c=!1;return function(){for(var d=arguments.length,e=Array(d),f=0;f<d;f++)e[f]=arguments[f];return c||(c=!0,b=a(...e)),b}}let e=/^[a-zA-Z][a-zA-Z\d+\-.]*?:/,f=a=>e.test(a);function g(){let{protocol:a,hostname:b,port:c}=window.location;return a+"//"+b+(c?":"+c:"")}function h(){let{href:a}=window.location,b=g();return a.substring(b.length)}function i(a){return"string"==typeof a?a:a.displayName||a.name||"Unknown"}function j(a){return a.finished||a.headersSent}function k(a){let b=a.split("?");return b[0].replace(/\\/g,"/").replace(/\/\/+/g,"/")+(b[1]?"?"+b.slice(1).join("?"):"")}async function l(a,b){let c=b.res||b.ctx&&b.ctx.res;if(!a.getInitialProps)return b.ctx&&b.Component?{pageProps:await l(b.Component,b.ctx)}:{};let d=await a.getInitialProps(b);if(c&&j(c))return d;if(!d)throw Object.defineProperty(Error('"'+i(a)+'.getInitialProps()" should resolve to an object. But found "'+d+'" instead.'),"__NEXT_ERROR_CODE",{value:"E394",enumerable:!1,configurable:!0});return d}let m="undefined"!=typeof performance,n=m&&["mark","measure","getEntriesByName"].every(a=>"function"==typeof performance[a]);class o extends Error{}class p extends Error{}class q extends Error{constructor(a){super(),this.code="ENOENT",this.name="PageNotFoundError",this.message="Cannot find module for page: "+a}}class r extends Error{constructor(a,b){super(),this.message="Failed to load static file for page: "+a+" "+b}}class s extends Error{constructor(){super(),this.code="ENOENT",this.message="Cannot find the middleware module"}}function t(a){return JSON.stringify({message:a.message,stack:a.stack})}},82015:a=>{a.exports=require("react")},88485:(a,b)=>{b._=function(a){return a&&a.__esModule?a:{default:a}}}};var b=require("../webpack-runtime.js");b.C(a);var c=b(b.s=23573);module.exports=c})();

View file

@ -0,0 +1 @@
{"version":1,"files":["../../../../../node_modules/next/dist/pages/_app.js","../../../../../node_modules/react/cjs/react-jsx-runtime.development.js","../../../../../node_modules/react/cjs/react-jsx-runtime.production.js","../../../../../node_modules/react/cjs/react.development.js","../../../../../node_modules/react/cjs/react.production.js","../../../../../node_modules/react/index.js","../../../../../node_modules/react/jsx-runtime.js","../../../../../node_modules/react/package.json","../../../../../package.json","../../package.json","../webpack-runtime.js"]}

View file

@ -0,0 +1 @@
"use strict";(()=>{var a={};a.id=220,a.ids=[220],a.modules={8732:a=>{a.exports=require("react/jsx-runtime")},33873:a=>{a.exports=require("path")},40361:a=>{a.exports=require("next/dist/compiled/next-server/pages.runtime.prod.js")},82015:a=>{a.exports=require("react")}};var b=require("../webpack-runtime.js");b.C(a);var c=b.X(0,[383],()=>b(b.s=50383));module.exports=c})();

View file

@ -0,0 +1 @@
{"version":1,"files":["../../../../../node_modules/client-only/index.js","../../../../../node_modules/client-only/package.json","../../../../../node_modules/next/dist/client/components/app-router-headers.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../../node_modules/next/dist/compiled/jsonwebtoken/index.js","../../../../../node_modules/next/dist/compiled/jsonwebtoken/package.json","../../../../../node_modules/next/dist/compiled/next-server/pages.runtime.prod.js","../../../../../node_modules/next/dist/lib/client-and-server-references.js","../../../../../node_modules/next/dist/lib/constants.js","../../../../../node_modules/next/dist/lib/interop-default.js","../../../../../node_modules/next/dist/lib/is-error.js","../../../../../node_modules/next/dist/lib/semver-noop.js","../../../../../node_modules/next/dist/pages/_document.js","../../../../../node_modules/next/dist/server/app-render/async-local-storage.js","../../../../../node_modules/next/dist/server/app-render/work-async-storage-instance.js","../../../../../node_modules/next/dist/server/app-render/work-async-storage.external.js","../../../../../node_modules/next/dist/server/app-render/work-unit-async-storage-instance.js","../../../../../node_modules/next/dist/server/app-render/work-unit-async-storage.external.js","../../../../../node_modules/next/dist/server/lib/cache-handlers/default.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/memory-cache.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/shared-cache-controls.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/tags-manifest.external.js","../../../../../node_modules/next/dist/server/lib/lru-cache.js","../../../../../node_modules/next/dist/server/lib/router-utils/instrumentation-globals.external.js","../../../../../node_modules/next/dist/server/lib/router-utils/instrumentation-node-extensions.js","../../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../../node_modules/next/dist/server/load-manifest.external.js","../../../../../node_modules/next/dist/server/response-cache/types.js","../../../../../node_modules/next/dist/shared/lib/deep-freeze.js","../../../../../node_modules/next/dist/shared/lib/invariant-error.js","../../../../../node_modules/next/dist/shared/lib/is-plain-object.js","../../../../../node_modules/next/dist/shared/lib/is-thenable.js","../../../../../node_modules/next/dist/shared/lib/server-reference-info.js","../../../../../node_modules/next/package.json","../../../../../node_modules/react-dom/cjs/react-dom-server-legacy.browser.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js","../../../../../node_modules/react-dom/cjs/react-dom-server.browser.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server.browser.production.js","../../../../../node_modules/react-dom/cjs/react-dom-server.edge.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server.edge.production.js","../../../../../node_modules/react-dom/cjs/react-dom.development.js","../../../../../node_modules/react-dom/cjs/react-dom.production.js","../../../../../node_modules/react-dom/index.js","../../../../../node_modules/react-dom/package.json","../../../../../node_modules/react-dom/server.browser.js","../../../../../node_modules/react-dom/server.edge.js","../../../../../node_modules/react/cjs/react-jsx-runtime.development.js","../../../../../node_modules/react/cjs/react-jsx-runtime.production.js","../../../../../node_modules/react/cjs/react.development.js","../../../../../node_modules/react/cjs/react.production.js","../../../../../node_modules/react/index.js","../../../../../node_modules/react/jsx-runtime.js","../../../../../node_modules/react/package.json","../../../../../node_modules/styled-jsx/dist/index/index.js","../../../../../node_modules/styled-jsx/index.js","../../../../../node_modules/styled-jsx/package.json","../../../../../package.json","../../package.json","../chunks/383.js","../webpack-runtime.js"]}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"version":1,"files":["../../../../../node_modules/client-only/index.js","../../../../../node_modules/client-only/package.json","../../../../../node_modules/next/dist/client/components/app-router-headers.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../../../../../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../../../../../node_modules/next/dist/compiled/jsonwebtoken/index.js","../../../../../node_modules/next/dist/compiled/jsonwebtoken/package.json","../../../../../node_modules/next/dist/compiled/next-server/pages.runtime.prod.js","../../../../../node_modules/next/dist/lib/client-and-server-references.js","../../../../../node_modules/next/dist/lib/constants.js","../../../../../node_modules/next/dist/lib/interop-default.js","../../../../../node_modules/next/dist/lib/is-error.js","../../../../../node_modules/next/dist/lib/semver-noop.js","../../../../../node_modules/next/dist/server/app-render/async-local-storage.js","../../../../../node_modules/next/dist/server/app-render/work-async-storage-instance.js","../../../../../node_modules/next/dist/server/app-render/work-async-storage.external.js","../../../../../node_modules/next/dist/server/app-render/work-unit-async-storage-instance.js","../../../../../node_modules/next/dist/server/app-render/work-unit-async-storage.external.js","../../../../../node_modules/next/dist/server/lib/cache-handlers/default.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/memory-cache.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/shared-cache-controls.external.js","../../../../../node_modules/next/dist/server/lib/incremental-cache/tags-manifest.external.js","../../../../../node_modules/next/dist/server/lib/lru-cache.js","../../../../../node_modules/next/dist/server/lib/router-utils/instrumentation-globals.external.js","../../../../../node_modules/next/dist/server/lib/router-utils/instrumentation-node-extensions.js","../../../../../node_modules/next/dist/server/lib/trace/constants.js","../../../../../node_modules/next/dist/server/lib/trace/tracer.js","../../../../../node_modules/next/dist/server/load-manifest.external.js","../../../../../node_modules/next/dist/server/response-cache/types.js","../../../../../node_modules/next/dist/shared/lib/deep-freeze.js","../../../../../node_modules/next/dist/shared/lib/invariant-error.js","../../../../../node_modules/next/dist/shared/lib/is-plain-object.js","../../../../../node_modules/next/dist/shared/lib/is-thenable.js","../../../../../node_modules/next/dist/shared/lib/no-fallback-error.external.js","../../../../../node_modules/next/dist/shared/lib/server-reference-info.js","../../../../../node_modules/next/package.json","../../../../../node_modules/react-dom/cjs/react-dom-server-legacy.browser.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js","../../../../../node_modules/react-dom/cjs/react-dom-server.browser.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server.browser.production.js","../../../../../node_modules/react-dom/cjs/react-dom-server.edge.development.js","../../../../../node_modules/react-dom/cjs/react-dom-server.edge.production.js","../../../../../node_modules/react-dom/cjs/react-dom.development.js","../../../../../node_modules/react-dom/cjs/react-dom.production.js","../../../../../node_modules/react-dom/index.js","../../../../../node_modules/react-dom/package.json","../../../../../node_modules/react-dom/server.browser.js","../../../../../node_modules/react-dom/server.edge.js","../../../../../node_modules/react/cjs/react-jsx-runtime.development.js","../../../../../node_modules/react/cjs/react-jsx-runtime.production.js","../../../../../node_modules/react/cjs/react.development.js","../../../../../node_modules/react/cjs/react.production.js","../../../../../node_modules/react/index.js","../../../../../node_modules/react/jsx-runtime.js","../../../../../node_modules/react/package.json","../../../../../node_modules/styled-jsx/dist/index/index.js","../../../../../node_modules/styled-jsx/index.js","../../../../../node_modules/styled-jsx/package.json","../../package.json","../chunks/383.js","../webpack-runtime.js"]}