- 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.
5.2 KiB
5.2 KiB
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/managev9,billing-catalogv5 — 청구 금액 ₩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/login200. 공개는 터널 호스트 추가 대기(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-serverdotnet test28/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.mdGAP-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에 두 곳 동시 수정 규칙을 적었다.
메타 학습
- "안 돌던 것"도 중복이다.
.github는 원격이 없어 실행된 적이 없었고, 그 안의 Payple 갱신 cron 때문에 정기 결제가 멈춰 있었다. 정리 전에 "이 파일은 실제로 실행되는가"를 원격·러너 기준으로 확인한다. - 운영 서버의 사본도 중복이다. NAS compose가 저장소와 달라져 있었다(서비스 추가, 필수 변수 누락). 배포 전 운영본과 diff한다.
- Windows에서 Python 텍스트 쓰기는 CRLF를 만든다.
version:check가 바이트 비교라 깨졌다.newline=''로 쓰거나 Edit 도구를 쓴다. 비 raw 문자열의\\b는 백스페이스 문자가 될 수 있다 — 정규식 편집은 Edit 도구로. - Tailwind 내장 유틸리티와 이름이 같은 사용자 클래스는 무력화된다(
break-keep). 프로젝트 전용 이름을 쓴다(keep-ko).