패스 스냅샷 적용 정책
스냅샷 기반 정책 적용 시점과 가격/기능 분리 원칙을 정의한다.
문서 정보
- 작성일: 2025-12-03
- 최종 업데이트: 2026-01-20
- 버전: v3.0.0
- 관련 문서:
- POLICY-COMPARISON.md - 정책 비교표
- SCHEMA-MIGRATION.md - 스키마 마이그레이션
- 패스 시스템 — 스냅샷 불변 정책 - 정책 동기화 전략
1. 개요
1.1 스냅샷 시스템 구조
1.2 정책 관리 방식
중요: 정책 변경은 어드민 UI가 아닌 코드 재배포를 통해 이루어집니다.
| 단계 | 작업 | 설명 |
|---|---|---|
| 1 | PASS-POLICY.yml 수정 | 정책 정의 변경 |
| 2 | /sync-pass-policy 실행 | 코드 자동 동기화 |
| 3 | 코드 리뷰 & 배포 | PR → 머지 → 배포 |
| 4 | pnpm db:cli | 새 스냅샷 DB에 추가 |
1.3 핵심 원칙
| 항목 | 적용 시점 | 근거 |
|---|---|---|
| 가격 (priceInGrains/priceInKrw) | 구매/구독 시점 고정 | 계약 보호, 법적 안정성 |
| 기능 (featurePolicy) | 구매 시점 고정 | 스냅샷 패턴 일관성 |
| 멤버십 혜택 (benefitPolicy) | 항상 최신 적용 | 구독자 혜택 즉시 반영 |
2. 스냅샷 생성 플로우
2.1 정책 변경 → 스냅샷 생성
2.2 Upcasters 패턴
기존 스냅샷은 DB에서 수정하지 않고, 조회 시점에 최신 스키마로 변환합니다.
// feature-policy.vo.ts
static fromJson(json: JsonValue, policyVersion?: number): FeaturePolicyVO {
const upcastedData = this.upcastToLatest(json, policyVersion ?? this.currentVersion);
return new FeaturePolicyVO(featurePolicySchema.parse(upcastedData));
}
private static upcastToLatest(data: unknown, version: number): unknown {
let current = data;
// v1 → v2: WORKOUT_CONDITION 추가
if (version < 2) {
current = { ...current, WORKOUT_CONDITION: { enabled: false } };
}
return current;
}
3. 정책 적용 규칙
3.1 일반 패스 (건별 구매)
| 상황 | 참조 스냅샷 | 고정 값 |
|---|---|---|
| 스케줄 예약 | 해당 패스의 예약 시점 최신 PassSnapshot | priceInGrains, featurePolicy |
3.2 멤버십 (구독)
| 상황 | 가격 | 혜택 (benefitPolicy) |
|---|---|---|
| 구독 신청 | 구독 시점 고정 | 항상 최신 적용 |
| 구독 갱신 | 구독 시점 가격 유지 | 항상 최신 적용 |
가격 고정 이유:
- 계약 시점의 합의된 가격 보장
- 환불, 분쟁 시 증빙 자료
- 법적 안정성 (소비자 보호)
혜택 최신 적용 이유:
- 구독자에게 즉시 새 기능 제공
- 서비스 개선 즉시 반영
3.3 기능 정책 조회 방식
// 일반 패스: 구매 시점 featurePolicy
const passSnapshot = userSchedule.passSnapshot;
const featurePolicy = FeaturePolicyVO.fromJson(passSnapshot.featurePolicy, passSnapshot.policyVersion);
// 멤버십: 최신 benefitPolicy
const latestSnapshot = await getLatestMembershipSnapshot(subscription.sourcePlanId);
const benefitPolicy = BenefitPolicyVO.fromJson(latestSnapshot.benefitPolicy, latestSnapshot.policyVersion);
4. featurePolicy vs benefitPolicy
4.1 구분
| 정책 | 저장 위치 | 적용 시점 | 용도 |
|---|---|---|---|
| featurePolicy | PassSnapshot | 구매 시점 고정 | 패스별 기본 기능 (광고, 품질 등) |
| benefitPolicy | PassMembershipSnapshot | 항상 최신 | 멤버십 전용 혜택 (콕, 마이콕) |
4.2 featurePolicy 구조
{
"AD": { "enabled": false },
"SCHEDULE_ALBUM": { "enabled": true },
"CAMERA_TRANSITION_SPEED": { "speed": "FAST" },
"CAMERA_ACCESS": { "type": "UNLIMITED" },
"STREAMING": { "quality": "4K (3840x2160)", "fps": 30 },
"DOWNLOAD": { "quality": "4K (3840x2160)", "fps": 30 },
"WORKOUT_CONDITION": { "enabled": true }
}
4.3 benefitPolicy 구조
{
"kok": { "limit": 30, "periodType": "SCHEDULE" },
"myKok": { "maxCount": 30, "periodType": "MEMBERSHIP" }
}
Note: v1.2.0에서
KOK_VIEW가 featurePolicy에서 benefitPolicy.kok으로 이동됨. 콕 저장/시청은 멤버십 전용 기능입니다.
5. 예외 케이스
주의: 아래 케이스들은 향후 비즈니스/법무 검토 필요
| 상황 | 고려사항 | 검토 필요 사항 |
|---|---|---|
| 기능 축소 (다운그레이드) | 정책적으로 금지 권장 | 불가피한 경우 처리 절차, 환불/보상 정책 |
| 기능 변경 (동등 교환) | 사용자 경험 영향 최소화 | 사전 공지 기간, 동의 필요 여부 |
| 기능 삭제 | 법적 리스크 존재 | 법무 검토, 대체 기능 제공 의무 |
6. 관련 코드
| 파일 | 역할 |
|---|---|
PASS-POLICY.yml | 정책 정의 (SSOT) |
feature-policy.vo.ts | featurePolicy Value Object |
benefit-policy.vo.ts | benefitPolicy Value Object |
reserve-transaction.util.ts | 예약 시 최신 스냅샷 조회 |
pass-snapshot-history.seed.ts | 스냅샷 시드 데이터 |
변경 이력
| 버전 | 날짜 | 변경 내용 |
|---|---|---|
| v3.0.0 | 2026-01-20 | 문서 전면 재작성 - TierFeature 관련 내용 제거 (테이블 없음) - PASS-POLICY.yml 기반 정책 관리 방식 반영 - KOK_VIEW → benefitPolicy.kok 이동 반영 - featurePolicy vs benefitPolicy 구분 추가 - 멤버십 혜택 "항상 최신" 적용 명시 |
| v2.9.0 | 2025-12-29 | Pass → Pass 마이그레이션 |
| v2.3.0 | 2025-12-21 | 기능 정책도 구매 시점 고정으로 변경 |
| v1.0.0 | 2025-12-03 | 초기 문서 작성 |