Skip to content

Story: 가드레일 체크 서비스 구현

메타

항목
Story IDE-05-S-02
EpicE-05 요금제 정책 재설계 및 가드레일
상태draft
우선순위P0
규모L
담당창훈님 (하록님, 수민님)

사용자 스토리

As a 장사왕 시스템, I want 요금제별 Feature 접근을 통합 관리하는 서비스, So that 모든 가드레일을 일관되게 적용할 수 있다.


수락 기준 (Acceptance Criteria)

AC-01: 요금제 조회 서비스

항목내용
Given사업장 ID
When요금제 조회 요청
Then현재 요금제 정보 + Feature 제한 반환

API 응답:

typescript
interface PlanInfo {
  tierId: number;
  tierCode: string;          // 'starter', 'basic', 'pro', 'business'
  tierName: string;
  isGrandfather: boolean;    // Grandfather 정책 적용 여부
  limits: {
    maxVendors: number;
    maxMonthlyOrders: number | null;
    dataRetentionDays: number;
  };
  features: {
    [featureCode: string]: {
      enabled: boolean;
      limit: number | null;   // count 타입일 경우
    };
  };
}

AC-02: Feature 접근 체크

항목내용
Given사업장 ID + Feature 코드
When접근 체크 요청
Then접근 가능 여부 + 잔여 한도 반환

체크 함수:

typescript
interface FeatureCheckResult {
  allowed: boolean;
  reason?: 'disabled' | 'limit_exceeded';
  currentUsage?: number;
  limit?: number;
  upgradeRequired?: string;  // 필요한 최소 티어
}

checkFeatureAccess(bizId: string, featureCode: string): Promise<FeatureCheckResult>

AC-03: 사용량 카운트

항목내용
Given사업장 ID + count 타입 Feature
When사용량 조회
Then현재 사용량 반환

카운트 대상:

featureCode카운트 방식
deep_layer_campaigns광고 연동된 캠페인 수
keywords키워드 분석 요청 수 (당월)

AC-04: 캐싱

항목내용
Given요금제 조회 요청
When캐시 존재
Then캐시 데이터 반환 (TTL 5분)

캐시 키: plan:${bizId}


태스크 분해

Task 1: PlanGuardService 생성 AC-01

  • [ ] 1.1: PlanGuardService 클래스 생성
  • [ ] 1.2: getPlanInfo(bizId) 메서드
  • [ ] 1.3: BizPlanInfo → PlanTier 매핑 로직
  • [ ] 1.4: Grandfather 판정 로직

Task 2: Feature 체크 AC-02

  • [ ] 2.1: checkFeatureAccess(bizId, featureCode) 메서드
  • [ ] 2.2: boolean 타입 Feature 체크
  • [ ] 2.3: count 타입 Feature 체크
  • [ ] 2.4: 업그레이드 필요 티어 계산

Task 3: 사용량 카운트 AC-03

  • [ ] 3.1: getFeatureUsage(bizId, featureCode) 메서드
  • [ ] 3.2: deep_layer_campaigns 카운트 (CoupangAdCampaignDaily)
  • [ ] 3.3: keywords 카운트 (별도 테이블 또는 로그)

Task 4: 캐싱 AC-04

  • [ ] 4.1: Redis 또는 메모리 캐시 설정
  • [ ] 4.2: 캐시 키 전략
  • [ ] 4.3: 캐시 무효화 (요금제 변경 시)

Task 5: 테스트

  • [ ] 5.1: 단위 테스트 (각 티어별)
  • [ ] 5.2: Grandfather 케이스 테스트

Dev Notes (AI Agent 최적화)

영향 받는 소스 트리

src/
├── service/
│   └── plan-guard.service.ts         # 🆕 핵심 서비스
├── dto/
│   ├── plan-info.dto.ts              # 🆕
│   └── feature-check.dto.ts          # 🆕
└── decorator/
    └── require-feature.decorator.ts  # 🆕 선택적

핵심 로직

typescript
// Grandfather 판정
isGrandfather(bizPlanInfo: BizPlanInfo): boolean {
  // PortOne PG 사용 중인 기존 고객
  return bizPlanInfo.paymentMethod === 'PORTONE';
}

// 티어 매핑
mapToTier(bizPlanInfo: BizPlanInfo): PlanTier {
  if (this.isGrandfather(bizPlanInfo)) {
    // 기존 planType → 새 tier 매핑
    const mapping = {
      'basic1_*': 'starter',
      'pro3_*': 'basic',
      'pro10_*': 'pro',
      'pro20_*': 'business',
    };
    // ...
  }
  return bizPlanInfo.planTier;
}

충돌 감지

항목상태설명
기존 코드 충돌🟡 있음기존 요금제 체크 로직 대체 필요

의존성

의존설명상태
E-05-S-01PlanTier/PlanFeature 테이블선행 필수

이 Story가 블로킹하는 Story:

  • E-05-S-03 ~ S-07 (모든 FE/BE Feature 분기)

생성일: 2026-01-27

장사왕 Product Team