테마
Story: Grandfather 정책 적용 로직
메타
| 항목 | 값 |
|---|---|
| Story ID | E-05-S-09 |
| Epic | E-05 요금제 정책 재설계 및 가드레일 |
| 상태 | draft |
| 우선순위 | P0 |
| 규모 | M |
| 담당 | 창훈님 (하록님, 수민님) |
사용자 스토리
As a 기존 유료 고객, I want 현재 조건을 유지하면서 새 요금제 체계로 전환되고 싶다, So that 갑작스러운 가격 인상이나 기능 제한 없이 서비스를 이용할 수 있다.
수락 기준 (Acceptance Criteria)
AC-01: Grandfather 고객 식별
| 항목 | 내용 |
|---|---|
| Given | 요금제 정보 조회 |
| When | Grandfather 플래그 확인 |
| Then | isGrandfather = TRUE면 특별 처리 |
식별 조건:
Grandfather 조건:
1. PortOne PG로 결제 중인 기존 유료 고객
2. 새 요금제 체계 도입 이전 가입AC-02: 기존 가격 유지
| 항목 | 내용 |
|---|---|
| Given | Grandfather 고객 |
| When | 결제 갱신 |
| Then | 기존 가격으로 결제 |
가격 매핑:
| 기존 planType | 신규 티어 | 기존 가격 | 신규 가격 |
|---|---|---|---|
| basic1_* | Starter | ₩22,000 | ₩33,000 |
| pro3_* | Basic | ₩44,000 | ₩55,000 |
| pro10_* | Pro | ₩110,000 | ₩110,000 |
| pro20_* | Business | ₩220,000 | ₩330,000 |
로직:
typescript
getPrice(bizPlanInfo: BizPlanInfo): number {
if (bizPlanInfo.isGrandfather) {
return this.getLegacyPrice(bizPlanInfo.planType);
}
return this.getTierPrice(bizPlanInfo.tierId);
}AC-03: 기존 Feature 한도 유지
| 항목 | 내용 |
|---|---|
| Given | Grandfather 고객 |
| When | Feature 체크 |
| Then | 기존 한도 적용 (더 유리한 경우) |
한도 비교:
| Feature | 기존 PRO 10 | 신규 Pro | Grandfather 적용 |
|---|---|---|---|
| 사업장 수 | 10개 | 3개 | 10개 (기존 유지) |
| 캠페인 분석 | 제한 없음 | 30개 | 제한 없음 |
로직:
typescript
getFeatureLimit(bizId: string, featureCode: string): number {
const planInfo = await this.getPlanInfo(bizId);
if (planInfo.isGrandfather) {
const legacyLimit = this.getLegacyLimit(planInfo.planType, featureCode);
const newLimit = this.getNewLimit(planInfo.tierId, featureCode);
// 더 유리한 쪽 적용
return Math.max(legacyLimit, newLimit);
}
return this.getNewLimit(planInfo.tierId, featureCode);
}AC-04: Toss PG 전환 시 Grandfather 해제
| 항목 | 내용 |
|---|---|
| Given | Grandfather 고객이 Toss PG로 전환 |
| When | 결제 수단 변경 완료 |
| Then | isGrandfather = FALSE, 신규 가격 적용 |
전환 플로우:
Grandfather 고객 (PortOne)
↓
Toss PG 전환 요청
↓
전환 완료
↓
isGrandfather = FALSE
↓
신규 가격 적용 (다음 결제 주기부터)AC-05: Grandfather 해제 안내
| 항목 | 내용 |
|---|---|
| Given | Grandfather 고객 |
| When | Toss PG 전환 시도 |
| Then | 가격 변경 안내 + 확인 요청 |
UI 스펙:
┌────────────────────────────────────────┐
│ ⚠️ 결제 수단 변경 안내 │
├────────────────────────────────────────┤
│ │
│ 현재 기존 고객 특별 가격이 적용되어 │
│ 있습니다. │
│ │
│ 결제 수단을 Toss로 변경하시면 │
│ 다음 결제부터 신규 가격이 적용됩니다. │
│ │
│ 현재 가격: ₩49,900/월 │
│ 변경 후: ₩110,000/월 │
│ │
│ 변경하시겠습니까? │
│ │
│ [취소] [변경하기] │
└────────────────────────────────────────┘태스크 분해
Task 1: Grandfather 조회 로직 AC-01
- [ ] 1.1: PlanGuardService에 isGrandfather 체크 추가
- [ ] 1.2: getPlanInfo 응답에 isGrandfather 포함
Task 2: 가격 분기 로직 AC-02
- [ ] 2.1: 기존 가격 테이블/매핑 정의
- [ ] 2.2: getPrice() 메서드 분기 처리
- [ ] 2.3: 결제 서비스에 분기 로직 적용
Task 3: Feature 한도 분기 AC-03
- [ ] 3.1: 기존 한도 테이블/매핑 정의
- [ ] 3.2: getFeatureLimit() 메서드 분기 처리
- [ ] 3.3: Math.max 로직으로 유리한 쪽 적용
Task 4: PG 전환 시 해제 AC-04
- [ ] 4.1: 결제 수단 변경 API에 Grandfather 해제 로직
- [ ] 4.2: isGrandfather = FALSE 업데이트
- [ ] 4.3: 변경 이력 로깅
Task 5: 전환 안내 UI AC-05
- [ ] 5.1: FE - Toss 전환 시 가격 변경 안내 모달
- [ ] 5.2: 현재 가격 vs 변경 후 가격 표시
- [ ] 5.3: 확인 후 전환 진행
Dev Notes (AI Agent 최적화)
영향 받는 소스 트리
BE:
src/
├── service/
│ ├── plan-guard.service.ts # 🔄 수정 (Grandfather 로직)
│ └── payment.service.ts # 🔄 수정 (가격 분기)
├── dto/
│ └── plan-info.dto.ts # 🔄 수정 (isGrandfather)
└── constants/
└── legacy-plan.constant.ts # 🆕 기존 가격/한도 매핑
FE:
src/
└── components/
└── payment/
└── GrandfatherWarningModal.tsx # 🆕기존 가격/한도 매핑 상수
typescript
// legacy-plan.constant.ts
export const LEGACY_PLAN_CONFIG = {
'basic1': {
price: 22000,
limits: {
maxVendors: 1,
maxCampaigns: null, // 무제한
maxKeywords: null,
}
},
'pro3': {
price: 44000,
limits: {
maxVendors: 3,
maxCampaigns: null, // 무제한
maxKeywords: null,
}
},
'pro10': {
price: 110000,
limits: {
maxVendors: 10,
maxCampaigns: null,
maxKeywords: null,
}
},
'pro20': {
price: 220000,
limits: {
maxVendors: 20,
maxCampaigns: null,
maxKeywords: null,
}
},
};
// planType → legacyKey 매핑
export function getLegacyKey(planType: string): string | null {
if (planType.startsWith('basic1_')) return 'basic1';
if (planType.startsWith('pro3_')) return 'pro3';
if (planType.startsWith('pro10_')) return 'pro10';
if (planType.startsWith('pro20_')) return 'pro20';
return null;
}Grandfather 판정 플로우
요금제 정보 요청
↓
BizPlanInfo 조회
↓
isGrandfather 확인
↓
┌─── TRUE ───┐ ┌─── FALSE ───┐
↓ ↓ ↓ ↓
기존 가격 기존 한도 신규 가격 신규 한도
(유지) (유리한 쪽) (적용) (적용)의존성
| 의존 | 설명 | 상태 |
|---|---|---|
| E-05-S-01 | PlanTier 테이블 | 선행 필수 |
| E-05-S-08 | 마이그레이션 완료 | 선행 필수 |
비즈니스 고려사항
Grandfather 정책 목적
- 기존 고객 보호: 갑작스러운 가격 인상 방지
- Toss PG 전환 유도: 전환 조건으로 활용
- 점진적 전환: 자연스러운 신규 체계 도입
예상 시나리오
| 시나리오 | Grandfather | 가격 |
|---|---|---|
| 기존 PRO 10, PortOne 유지 | ✅ | ₩49,900 |
| 기존 PRO 10, Toss 전환 | ❌ | ₩110,000 |
| 신규 가입 | ❌ | ₩110,000 |
| Trial 후 Pro 결제 | ❌ | ₩110,000 |
생성일: 2026-01-27
