---
title: "핸드오프 — 코치노트 AI RC2 spec05(플랜·구독·사용량) 완료 → spec06(프론트 UX) 인계"
category: "handoff"
document_type: "핸드오프"
source_status: "generated"
knowledge_group: "03_history"
priority: "High"
purpose: "코치노트 AI RC2 5/6(spec_rc2_05 plan_billing+05a) 완료 후 spec06 담당이 즉시 이어받기 위한 인계. 차감(턴=회 비율)·플랜·OWNER/BETA/VIP·결제 Phase 구조와 spec06 화면 연결 지점(사용량 표시·캡 모달·소진 UX·플랜 쇼핑·consume_turn 호출 wiring), 건드리면 안 되는 결정, 사용자 의도·정서, 이월 컨펌을 담는다."
read_when: ["코치노트AI","플랜·과금·사용량","최신상태복구","후속작업·우선순위","spec06연결"]
updated: "2026-06-06"
work_timestamp: "20260606_181528"
context: "달록본레포CC (D:\\dallog\\dallog_git)"
source_of_truth: "https://dallog-tools.hansbridge.co.kr/knowledge/"
---

# 핸드오프 — 코치노트 AI RC2 spec05_plan_billing(+05a) → spec06_frontend_ux

> 다음 Claude Code 스레드/작업자가 **즉시 이어받기 위한 실행 컨텍스트**. 작업보고서는 같은 작업분 `작업보고_코치노트AI-RC2-spec05`(work_timestamp 20260606_181206) 참조.
> 용어 병기: Plan(구독/이용 등급)·Entitlement(사용권·부여 권한)·Usage(사용량)·Coaching turn(질문+답변 1회)·thread(대화방)·storageGate(저장 활성화 게이트).

## 목차
- [0. 현재 상태](#0-현재-상태) · [1. 반드시 알아야 할 것](#1-반드시-알아야-할-것) · [2. 구현된 내용](#2-구현된-내용)
- [3. 검증 결과](#3-검증-결과) · [4. 건드리면 안 되는 결정](#4-건드리면-안-되는-결정) · [5. 사용자 의도·정서](#5-사용자-의도정서)
- [6. 후속 작업 지시(spec06 연결 지점)](#6-후속-작업-지시spec06-연결-지점) · [7. 이월 컨펌·미확정](#7-이월-컨펌미확정)

## 0. 현재 상태

| 항목 | 상태 |
|---|---|
| 담당 작업 | spec_rc2_05_plan_billing + spec_rc2_05a_plan_admin (5/6) |
| 완료 여부 | 구현·검증·SQL 적용 완료. 화면 wiring(차감 호출·표시)은 spec06 |
| build | PASS (npm run build, tsc+vite) |
| SQL 적용 | 적용 완료(사장님 수동, 2026-06-06) — 플랜표·CHECK·owner 부여 검증 PASS |
| 커밋 | 작성 당시 미커밋 → 이후 RC2 전체 커밋됨 |
| 남은 위험 | 낮음. consume_turn **호출 규약(응답 성공 시에만)** 을 spec06이 반드시 지켜야 함(어기면 실패 턴도 차감) |

> ★ spec06은 프론트 UX 단계다. **spec05의 계산 구조(차감·환산·주기·플랜)를 임의 변경하지 않는다.** 표시·호출 위치만 담당. 최종 기준 = docs/go_work 원본 명세 + decision_log #16·#4·#7.

## 1. 반드시 알아야 할 것

- **차감 단위 = 턴(=회) 비율**(방 단위 폐기 #16). 1턴=1회. 잔여량은 **정수 "회"로만** 표시("AI 코칭 N회 남음"). 코칭권은 쇼핑 구매 묶음 표기.
- **모든 계산은 DB RPC가 단일 처리**(차액 가산·주기 리셋·할증 환산·원자적 차감). 프론트(spec06)는 호출·표시만. 계산식 재구현 금지.
- **응답 성공 시에만 consumeTurn 호출**(실패=무차감).
- **결제·연장은 결제 Phase 전용**(베타 비활성). `billingPhase.isBillingPhase()` 분기. 무료 연장 불가.
- **사용량 구조는 storageGate(원문 저장)와 독립.** 저장 OFF여도 thread_id만으로 동작. spec06이 게이트 켜지 말 것.

## 2. 구현된 내용

DB(`migrations/2026-06-07_coachchat_plan_billing.sql`+GUIDE): 테이블 5(coach_plan_catalog·coach_billing_setting·coach_subscription·coach_entitlement·coach_thread_usage) + plan_type CHECK 7종 확장(ALTER) + RPC 7(ensure·reset_cycle·get_entitlement·**consume_turn**·change_plan·cancel·usage_summary). 전 테이블 RLS 본인만, 플랜표·설정 읽기전용.
코드(`src/lib/coachChat/`): planCatalog·billingPhase·subscription·entitlement·usageReport(신규 5) + types.ts(PlanType owner/beta/vip + 타입 추가).

## 3. 검증 결과

| 항목 | 결과 | 근거 |
|---|---|---|
| npm run build | PASS | tsc+vite, 타입 오류 0 |
| 차감 로직 시뮬레이션 | PASS(28/28) | 소진·실패무차감·연장할증·업그레이드차액·OWNER·BETA/VIP·리셋 |
| SQL 적용(플랜표 7행/CHECK 7종/owner 부여) | PASS | 사장님 검증 쿼리 |
| get_entitlement (SQL Editor) | N/A→정상 | auth.uid()=NULL이라 빈 결과(의도). 앱/가장 세션 동작 |
| MCP 브라우징/화면 검증 | N/A | UI는 spec06 |

검증 중 **무료 연장 거부 누락 버그 발견·수정**(is_purchasable 가드 추가).

## 4. 건드리면 안 되는 결정

- 턴(회) 비율 차감 / 토큰으로 사용자 차감 금지(이중장부). 잔여량 정수 "회"로만 표시.
- 응답 실패 무차감. consumeTurn은 성공 시에만 호출.
- 연장·결제 결제 Phase 전용(베타 비활성, 무료 연장 불가).
- 할증률·연장 한도·결제 Phase는 설정값(coach_billing_setting). 하드코딩 금지.
- BETA/VIP 제공량·기간 임의 확정 금지(재시뮬레이션 후). OWNER/BETA/VIP는 Phase1 가격·전환율 집계에서 분리.
- storageGate와 독립(게이트 켜지 말 것). spec01~04 무수정(이번은 plan_type CHECK ALTER만 추가).

## 5. 사용자 의도·정서

- **친절한 제공자 정책(축소 금지)** — 사용량을 구석에 작게 숨기면 의도 위반. 자연히 인지할 위치·크기로 노출.
- **차감 번복 정서(#16)** — 방 단위는 "단발 질문에 무료 체험 통째 증발"하는 불공정("당했다")→이탈. 턴 비율은 같은 비용 상한에서 체감 관대함 확보+운영 원가도 하락(짧은 방 분산).
- **손실방어 1번 원칙** — 출혈 도박 금지. 무료 평생 1회성, BETA/VIP 호의도 시뮬레이션으로 결정.
- **빼앗기 프레임 회피** — 캡/소진/할증 문구는 자연스러운 종료 프레임("이 코칭이 마무리됐다 / 새 대화로 잇겠다").
- **설정값화 = 락인 아닌 기본값 선택** — 베타 실측 후 조정.

## 6. 후속 작업 지시(spec06 연결 지점)

> spec_rc2_06_frontend_ux 담당이 이어서 할 일. 최종 기준 = spec_rc2_06 원문 + spec05a §5·§A-7 + decision_log #7·#16.

1. **사용량 상시 노출** — `entitlement.getEntitlement()` → remaining_turns·turn_cap·is_unlimited·is_lifetime. 메인 "AI 코칭 N회 남음"(`formatRemaining()`), 방 게이지 "이 대화 {turn_count}/{turn_cap}회". 방 단위 표기 → 회 단위 교체(§A-7). 축소 금지.
2. **턴 차감 wiring(★)** — AI 응답 **성공 직후에만** `entitlement.consumeTurn(threadId, isExtension)`. 실패 시 호출 금지. threadId = coach_request_log.thread_id 동일.
3. **캡 도달 모달** — turn_count≥turn_cap 시 강제차단 없이 경고. 베타=새 대화방 권장, 결제 Phase=할증 인라인 고지. 유료=[새 대화방]/[연장], 무료=유료전환. 연장 거부 reason(extension_disabled/extension_limit) 처리.
4. **소진 UX** — is_exhausted=true → 무료=유료전환, 유료=상위 플랜. 베타는 결제창 대신 예고 톤(isBillingPhase 분기).
5. **플랜 쇼핑 페이지** — `planCatalog.fetchPurchasablePlans()` → tier1~3 카드("월 구독가(웹 기준가)"). 웹 결제 유도 컨테이너 히든. 업그레이드 = `subscription.changePlan()`.
6. **OWNER 표시** — 잔여 대신 `usageReport.getUsageSummary()` 오늘/주/보름/달/누적 {회, 원가} + `turnsToCredit()`. 턴캡 경고·게이지 동일.

## 7. 이월 컨펌·미확정

| # | 항목 | 상태 |
|---|---|---|
| ⓐ | VIP 부여 기간(1/3/6개월) | 이월 — 시뮬레이션 후 |
| ⓑ | 원가 시뮬레이션 가정 대조 | 이월 |
| ⓒ | 업그레이드 차액 가산 방식(기본값) | 기본값 적용, 이의 시 조정 |
| — | store_price_krw(스토어가) | NULL — 스토어 정책 검토 후 |
| — | 웹 결제 유도 컨테이너 노출 시점 | 스토어 등록 시점 결정 |
