- 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.
63 lines
5.2 KiB
Markdown
63 lines
5.2 KiB
Markdown
# 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`).
|