d3ro-voice/docs/REFACTOR_WAVE3_REPORT.md
Yun Chan e87ce63440 docs: record Wave 3 surface consolidation and fold NAS-only whisper into compose
- docs/REFACTOR_WAVE3_REPORT.md and the Wave 3 policy: canonical map,
  production changes, verification and remaining external steps.
- Gap backlog: GAP-BILL-01 resolved; new GAP-BILL-02 (Payple renewal never ran,
  Payple client key never set), GAP-WEB-01 (tunnel host for /app), GAP-OPS-01
  (NAS compose/.env drift), GAP-CI-01, GAP-I18N-02, GAP-TEAM-02.
- design.md: hero loop decision (numbers taken from the app capsule), pricing
  mismatch closed; feature catalog SHELL-11 updated.
- docs/map, release guide and mobile release docs no longer describe the
  deleted wwwroot, binaries, Dockerfile.admin, NAS site copy or .github CI.
- docker-compose.nas.yml gains the d3ro-whisper service that only existed in
  the NAS copy, so the repository file is the complete definition.
- refactor-wave skill: Wave 3 index and lessons P10-P12.
2026-09-26 16:02:44 +09:00

63 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Refactor Wave 3 — 표면·계약 단일화 (2026-09-26)
사용자 지시: "두 벌씩 만든 것을 전부 하나로 합쳐 정리." 정책은 `docs/REFACTOR_POLICY.md` Wave 3 절(W3-1~W3-8).
## 커밋
| 커밋 | WS | 내용 |
|---|---|---|
| `e689683` | 선행 | 랜딩 사이트를 코드로 확인한 사실 기준으로 재작성(접근성·10개 언어) |
| `88f24d8` | WS-A | 가격·쿼터·공개 URL 정본 `packages/core` 두 파일 + Deno 생성 사본(`contract:check`) |
| `b6fe588` | WS-B | 웹앱 `/app` basePath·standalone·NAS 컨테이너, 모든 결제 진입을 `billingUrl()`로, 브리지 워커 `/app` 라우팅 |
| `cd9d199` | WS-C | wwwroot·apps/web·바이너리 중복 삭제(바이너리 19개 ≈ 568 MiB), API 정적 서빙 제거 |
| `dc43884` | WS-D | `.github` 삭제, Forgejo로 CI·payple-renew 이식, 사이트 배포 워크플로 하나로 |
| `9297860` | 사이트 | 히어로 반복 예시(앱 녹음 캡슐 재현), 404.html, 법률 페이지 색 |
| (이 커밋) | 문서 | 보고서, 백로그, NAS compose 드리프트 병합(whisper) |
## 정본 지도 (전 → 후)
| 개념 | 전 | 후 |
|---|---|---|
| 요금 | Payple 9,900/29,900 · admin ₩12,900/₩24,900·$9.9/$19.9 · 사이트 2,900/8,900 | `plan-catalog.ts` 하나(2,900/8,900) → Deno 생성 사본 |
| 쿼터 | core `PREMIUM_MODEL_LIMITS` · Deno `quota.ts` · desktop `QUOTA_LIMITS` · 사이트 | `PLAN_QUOTA` 하나에서 파생 |
| 결제 URL | `d3ro.dev/billing`(해석 불가) · `d3ro.chanpaca.net/billing`(랜딩으로 폴백) | `billingUrl()` → `/app/billing` |
| 랜딩·다운로드·법률·초대·assetlinks | site · api-server wwwroot · apps/web | site 한 벌 |
| 설치 파일 | 저장소 3곳 추적 + Forgejo feed | Forgejo feed만 |
| CI | `.github`(한 번도 실행 안 됨) · `.forgejo` 2벌 사이트 배포 · GitLab | Forgejo(+ Android 서명 릴리스는 GitLab) |
| 사이트 배포 | Cloudflare Pages · GitHub Pages · NAS wwwroot 복사 | Cloudflare Pages |
| NAS compose | 운영본(whisper 포함) ≠ 저장소 | 저장소에 whisper 병합(교체는 GAP-OPS-01) |
## 운영 반영
- Supabase Edge Functions: `payple-checkout/renew/webhook/manage` v9, `billing-catalog` v5 — 청구 금액 ₩2,900/₩8,900.
새 CLI는 `functions/deno.json` 임포트 맵을 자동으로 쓰지 않아 `--import-map supabase/functions/deno.json`이 필요했다.
- Cloudflare Pages `d3ro`: 커밋 `9297860` 게시(`release-identity.json` 확인), 없는 경로 404.
- NAS: `d3ro_voice_web`(3002) 기동, `/app/login` 200. 공개는 터널 호스트 추가 대기(GAP-WEB-01).
## 검증
- `npm run typecheck`(모노레포) EXIT 0 · 변경 파일 eslint errors 0.
- `contract:check`·`version:check`·`release:metadata:test`(음성 14)·`release:mobile:boundary:test`·`security:secrets`·assetlinks GREEN.
- Deno `_shared/`+`payple-webhook/` 81/81. core vitest 131/131. api-server `dotnet test` 28/28.
- 데스크톱 vitest 1520/1522 — 실패 2건은 기록된 환경 의존 건(캡션 타임아웃, 임베딩 서버 부재).
**Node 23으로 돌리면 better-sqlite3 ABI 불일치(130/131)로 365건이 가짜 실패한다. Electron 런타임으로 실행해야 한다**:
`ELECTRON_RUN_AS_NODE=1 ../../node_modules/electron/dist/electron.exe ../../node_modules/vitest/vitest.mjs run --config vitest.config.ts`.
- web·admin 프로덕션 빌드 GREEN, web 이미지 로컬 스모크(`/app`→login, `/app/download`→사이트 308).
- 사이트: 10개 언어 × 320/390/1440 가로 넘침 0, axe(wcag2a·aa·21aa·22aa·best-practice) 위반 0, 감소 모션·일시정지 확인.
## SKIP / 남은 일
- 외부 설정(사용자): 터널 공개 호스트 + `WEB_APP_ORIGIN`, Supabase Redirect URLs `/app/**`, Forgejo 시크릿 `SUPABASE_URL`·`CRON_SECRET`,
Payple 클라이언트 키, Stripe·Play 가격, NAS `.env` 누락 변수 → `docs/map/11-gap-backlog.md` GAP-BILL-02, GAP-WEB-01, GAP-OPS-01.
- `packages/i18n`(12개 언어 JSON)과 `site/src/i18n`(10개 언어 TS)은 체계가 다르다. 사이트는 정적 번들 크기·독립 배포 때문에 유지 — 다음 wave 후보.
- 티어 타입(`'pro' | 'pro_plus'`) 정의가 core·api-client·Deno에 여러 벌 남아 있다 — 다음 wave 후보.
- 캡슐 CSS 값은 사이트가 앱 style.css를 옮겨 적었다(Electron 팝업 CSS 직접 import 불가). `design.md`에 두 곳 동시 수정 규칙을 적었다.
## 메타 학습
1. **"안 돌던 것"도 중복이다.** `.github`는 원격이 없어 실행된 적이 없었고, 그 안의 Payple 갱신 cron 때문에 정기 결제가 멈춰 있었다.
정리 전에 "이 파일은 실제로 실행되는가"를 원격·러너 기준으로 확인한다.
2. **운영 서버의 사본도 중복이다.** NAS compose가 저장소와 달라져 있었다(서비스 추가, 필수 변수 누락). 배포 전 운영본과 diff한다.
3. **Windows에서 Python 텍스트 쓰기는 CRLF를 만든다.** `version:check`가 바이트 비교라 깨졌다. `newline=''`로 쓰거나 Edit 도구를 쓴다.
비 raw 문자열의 `\\b`는 백스페이스 문자가 될 수 있다 — 정규식 편집은 Edit 도구로.
4. **Tailwind 내장 유틸리티와 이름이 같은 사용자 클래스는 무력화된다**(`break-keep`). 프로젝트 전용 이름을 쓴다(`keep-ko`).