본문으로 건너뛰기

활동 보상 시스템 (Activity Reward System)

문서 정보

  • 작성일: 2026-02-27
  • 최종 업데이트: 2026-02-27
  • 버전: v1.0.0

TL;DR

사용자의 활동(콕 시청, 운동 컨디션 기록, 광고 시청)에 대해 Rules Engine이 DB에 저장된 규칙을 평가하여 보상(리워드 적립, 카메라 잠금 해제)을 자동 지급하는 시스템입니다. 규칙 변경 시 코드 수정 없이 DB만 수정하면 됩니다.


목차

  1. 핵심 개념
  2. 활동 유형과 보상
  3. Rules Engine 아키텍처
  4. 자격 조건 (Eligibility)
  5. 제한 설정 (Limits)
  6. MembershipRewardCounter
  7. 활동별 상세 플로우
  8. Google AdMob SSV 연동
  9. 카메라 잠금 해제
  10. 데이터 모델
  11. FAQ

핵심 개념

한 줄 요약

사용자가 활동을 하면, Rules Engine규칙을 평가하여 보상을 지급한다.

핵심 용어

용어설명
활동 (Activity)사용자의 행동. 콕 시청, 운동 컨디션 기록, 광고 시청 등
규칙 (Rule)"누가, 무엇을 하면, 어떤 보상을, 얼마나 받을 수 있는지" 정의한 설정
Rules Engine규칙을 평가하고 보상 지급을 결정하는 핵심 엔진
리워드활동 보상으로 적립되는 포인트 (서비스 내 재화)
카메라 잠금 해제FREE 사용자가 광고 시청 후 PLUS 전용 카메라에 접근하는 것
멱등성 (Idempotency)같은 활동을 여러 번 요청해도 보상이 한 번만 지급되는 안전장치

설계 철학

  1. 분리된 구조: 콕, 운동 컨디션 등 핵심 서비스는 보상 로직을 모릅니다. 이벤트만 발행하고, 보상 시스템이 처리합니다.
  2. 유연한 규칙: 자격 조건, 보상 금액, 제한 등을 코드 수정 없이 DB 설정으로 변경할 수 있습니다.
  3. 감사 추적: 모든 활동과 보상 결과가 기록되어 추적이 가능합니다.

활동 유형과 보상

시스템이 지원하는 4가지 활동과 해당 보상입니다.

활동 유형트리거보상대상제한
콕 시청 (KOK_WATCH)콕 시청 완료 시리워드 5 적립PLUS 구독자50% 확률, 구독 주기당 240회
운동 컨디션 (WORKOUT_CONDITION)컨디션 기록 시리워드 5 적립PLUS 패스 보유자스케줄당 1회
광고 시청 (AD_WATCH)광고 시청 완료 시카메라 잠금 해제FREE 사용자제한 없음
Google 광고 (GOOGLE_AD)Google 리워드 광고 시청 시리워드 5 적립PLUS 구독자일 50회, 쿨다운 7분
콕 시청의 확률 적용 시점

콕 시청 보상의 50% 확률은 콕 생성 시점에 미리 결정됩니다 (isPointEligible 필드). 시청 시점이 아닌 생성 시점에 결정하여, 어떤 콕이 보상 대상인지 사전에 공정하게 배분합니다.


Rules Engine 아키텍처

Rules Engine은 사용자 활동이 발생할 때 다음 순서로 보상 여부를 판별합니다.

각 단계 설명

단계이름설명
1규칙 매칭활동 유형에 맞는 활성화된 규칙을 DB에서 조회합니다
2멱등성 체크동일 활동에 대한 중복 보상을 방지합니다 (멱등성 키 기반)
3자격 검증사용자의 멤버십, 패스 타입이 규칙의 자격 조건을 만족하는지 확인합니다
4제한 확인일별, 구독 주기별 등 보상 횟수 제한을 초과하지 않았는지 확인합니다
5확률 검사확률 기반 보상인 경우 랜덤 체크를 수행합니다 (기본값 100%)
6보상 지급리워드 적립 또는 카메라 잠금 해제를 실행합니다
모든 결과는 기록됩니다

보상이 지급되든 거부되든, 모든 결과는 ActivityRecord에 기록됩니다. 이를 통해 "왜 보상을 못 받았는지" 추적이 가능합니다.


자격 조건 (Eligibility)

규칙마다 누가 보상을 받을 수 있는지 자격 조건이 JSON으로 정의되어 있습니다.

자격 조건 필드

필드설명예시
membershipTypes필요한 멤버십 타입["PLUS"]
passTypes허용되는 패스 타입["PLUS"] 또는 ["FREE"]
requiresActiveSubscription보상 청구 시점에 활성 구독 필요 여부true / false

규칙별 자격 조건

규칙멤버십 필요?패스 타입설명
콕 시청 리워드PLUS 구독 필수PLUS구독 중인 PLUS 멤버십 사용자만
운동 컨디션 리워드불필요PLUSPLUS 패스(단건 구매 포함) 보유자
광고 카메라 해제불필요FREEFREE 사용자만 (PLUS는 이미 전체 접근 가능)
Google 광고 리워드PLUS 구독 필수-PLUS 구독 중인 사용자만

패스 타입별 접근 권한 요약

기능FREEPLUS 패스 (단건)PLUS 멤버십 (구독)
카메라 접근일부만 (광고 시청으로 해제)전체전체
콕 시청 리워드XXO (50%, 240회/주기)
운동 컨디션 리워드XOO
Google 광고 리워드XXO (일 50회)

제한 설정 (Limits)

보상의 과도한 지급을 막기 위해 규칙별로 제한이 설정됩니다.

제한 유형

제한 유형설명사용 예시
daily하루 최대 보상 횟수Google 광고: 일 50회
monthly월 최대 보상 횟수(현재 미사용)
perMembershipCycle구독 주기당 최대 보상 횟수콕 시청: 주기당 240회
perSchedule스케줄당 최대 보상 횟수운동 컨디션: 스케줄당 1회
cooldownSeconds보상 간 최소 대기 시간(초)Google 광고: 420초 (7분)

현재 규칙별 제한 설정

규칙제한
콕 시청 리워드구독 주기당240회
운동 컨디션 리워드스케줄당1회 (멱등성으로 보장)
광고 카메라 해제없음-
Google 광고 리워드일일 + 쿨다운50회/일 + 420초 간격

MembershipRewardCounter

구독 주기(Billing Cycle) 기반으로 보상 횟수를 추적하는 카운터입니다. 콕 시청 리워드의 "주기당 240회" 제한을 관리합니다.

동작 방식

카운터 주요 필드

필드설명
currentCount현재 주기 내 보상 횟수
maxCount최대 허용 횟수 (240)
accumulatedAmount현재 주기 내 누적 리워드 금액
cycleStartDate / cycleEndDate구독 주기 시작/종료일
subscriptionId연결된 구독 ID

주기 관리 규칙

  • 새 구독 주기 시작 시: currentCountaccumulatedAmount가 0으로 초기화됩니다
  • 구독 환불 시: 카운터가 삭제됩니다 (Hard Delete)
  • 카운터는 기존 레코드를 업데이트: 주기별로 새 레코드를 만들지 않고, 기존 레코드의 주기 정보를 갱신합니다
예상 결제 금액 계산

콕 시청 리워드 응답에는 cycleSummary가 포함됩니다. 여기에는 estimatedPayment (예상 결제 금액)이 있으며, 이는 구독 가격 - 적립된 리워드 잔액으로 계산됩니다. 사용자에게 "리워드를 더 모으면 결제 금액이 줄어든다"는 동기부여를 제공합니다.


활동별 상세 플로우

콕 시청 (KOK_WATCH) 리워드

PLUS 구독자가 콕을 시청 완료하면, 50% 확률로 5 리워드가 적립됩니다.

콕 시청의 특수성:

  • 확률(50%)은 시청 시점이 아닌 콕 생성 시점isPointEligible 플래그로 미리 결정됩니다
  • 멱등성 키 패턴: KOK_WATCH_{kokId}_{timestampId}_{userId}
  • 응답에 cycleSummary (주기 내 누적 현황, 예상 결제 금액)를 포함합니다

운동 컨디션 (WORKOUT_CONDITION) 리워드

PLUS 패스 보유자(구독 또는 단건 구매)가 운동 컨디션을 기록하면, 5 리워드가 적립됩니다.

운동 컨디션의 특수성:

  • 멤버십(구독) 없이도 PLUS 패스(단건 구매)만 있으면 보상 가능
  • 스케줄당 1회 제한은 멱등성 키로 보장 (별도 카운터 불필요)
  • 멱등성 키 패턴: WORKOUT_CONDITION_{workoutConditionId}_{userId}
  • 확률: 100% (항상 지급)

Google AdMob SSV 연동

PLUS 구독자가 Google 리워드 광고를 시청하면, 5 리워드가 적립됩니다. 광고 시청 검증은 Google이 직접 서버로 콜백을 보내는 SSV (Server-Side Verification) 방식을 사용합니다.

전체 흐름

SSV 서명 검증 과정

단계설명
1Google 공개키를 조회합니다 (24시간 캐싱)
2쿼리스트링에서 &signature= 앞부분을 서명 대상 메시지로 추출합니다
3ECDSA SHA256 알고리즘으로 서명을 검증합니다
4custom_data에서 userUuid를 추출하여 사용자를 식별합니다

보상 정책

항목
대상PLUS 구독자
보상5 리워드
일일 제한50회
쿨다운420초 (7분)
멱등성transaction_id 기반

Grant Status 코드

상태설명
GRANTED보상 지급 성공
ALREADY_PROCESSED이미 처리된 transaction_id
NOT_PLUS_MEMBERSHIPPLUS 구독이 아님
COOLDOWN_ACTIVE쿨다운 대기 중
DAILY_LIMIT_EXCEEDED일일 한도 초과
SSV_VERIFICATION_FAILED서명 검증 실패
RULE_NOT_FOUND규칙 미설정
SSE 알림

보상 결과는 SSE (Server-Sent Events)로 클라이언트에 실시간 전달됩니다. activity-reward.google-ad.granted (성공) 또는 activity-reward.google-ad.failed (실패) 이벤트가 발행됩니다.


카메라 잠금 해제

FREE 사용자는 일부 카메라(PLUS 티어)가 잠겨 있습니다. 광고를 시청하면 해당 타임슬롯의 PLUS 카메라 전체가 잠금 해제됩니다.

해제 흐름

잠금 해제 범위 (Scope)

범위설명현재 사용
TIMESLOT광고를 시청한 타임슬롯의 PLUS 카메라만 해제O (기본값)
SCHEDULE해당 스케줄의 모든 타임슬롯에서 PLUS 카메라 해제X (미사용)

비디오 조회 시 접근 권한 판별

사용자가 비디오 목록을 조회할 때, 카메라별 접근 가능 여부가 결정됩니다.

사용자 타입판별 방식
PLUS 패스모든 카메라 접근 가능 (추가 조회 없음)
FREE 패스FREE 티어 카메라는 항상 접근 가능. PLUS 티어는 CameraUnlockRecord 존재 여부로 판별

유효 기간

  • expiresAtnull이면 스케줄 종료 시까지 유효합니다
  • 타임슬롯 기반이므로 동일 스케줄의 다른 타임슬롯에서는 별도로 광고를 시청해야 합니다

데이터 모델

ERD

주요 JSON 스키마

자격 조건 (eligibility)

{
"membershipTypes": ["PLUS"],
"passTypes": ["PLUS"],
"requiresActiveSubscription": false
}

보상 설정 (rewardConfig)

// 리워드 적립 (POINT)
{ "type": "POINT", "amount": 5, "probability": 0.5 }

// 카메라 잠금 해제 (CAMERA_UNLOCK)
{ "type": "CAMERA_UNLOCK", "durationInSeconds": null, "scope": "TIMESLOT" }

제한 설정 (limitConfig)

// 구독 주기당 제한
{ "perMembershipCycle": 240 }

// 일일 제한 + 쿨다운
{ "daily": 50, "cooldownSeconds": 420 }

현재 등록된 규칙 (Seed Data)

코드활동보상자격제한
KOK_WATCH_POINT콕 시청리워드 5 (50%)PLUS 구독주기당 240회
WORKOUT_CONDITION_POINT운동 컨디션리워드 5 (100%)PLUS 패스스케줄당 1회
AD_WATCH_CAMERA_UNLOCK광고 시청카메라 해제FREE 패스없음
GOOGLE_AD_POINTGoogle 광고리워드 5 (100%)PLUS 구독일 50회 + 쿨다운 7분

FAQ

Q: 같은 콕을 여러 번 시청해도 리워드를 받나요?

아닙니다. 멱등성 키(KOK_WATCH_{kokId}_{timestampId}_{userId})로 중복을 방지합니다. 이미 처리된 활동은 ALREADY_WATCHED 상태로 기록됩니다.

Q: 구독을 갱신하면 카운터는 어떻게 되나요?

새 구독 주기(Billing Cycle)가 시작되면 기존 MembershipRewardCountercurrentCountaccumulatedAmount가 0으로 초기화됩니다. 매 주기마다 240회 제한이 새로 적용됩니다.

Q: FREE 사용자가 PLUS로 업그레이드하면 카메라 해제 기록은?

PLUS 사용자는 모든 카메라에 접근 가능하므로, 기존 CameraUnlockRecord와 무관하게 전체 카메라를 볼 수 있습니다. 기록은 히스토리로 남습니다.

Q: 광고 시청으로 해제한 카메라는 언제까지 유효한가요?

해당 스케줄이 만료될 때까지입니다. CameraUnlockRecord.expiresAtnull이면 스케줄 만료 시 자동 종료됩니다.

Q: Google 광고 시청 후 보상이 바로 반영되나요?

거의 즉시 반영됩니다. Google이 SSV 콜백을 서버로 직접 보내고, 서버는 보상 처리 후 SSE로 클라이언트에 실시간 알림합니다. 클라이언트에서 별도의 보상 요청 API를 호출할 필요가 없습니다.

Q: 새로운 활동 유형을 추가하려면?

  1. ActivityReward.Constants.ACTIVITY_TYPE에 새 활동 유형 추가
  2. 핵심 서비스에서 이벤트 발행 코드 추가
  3. DB에 새 규칙 INSERT (코드 변경 최소화)
  4. (선택) 전용 Command 구현 및 ActivityContext 스키마 정의

관련 문서


변경 이력

버전날짜변경 내용
v1.0.02026-02-27초기 문서 작성
- Rules Engine 아키텍처 설명
- 4가지 활동 유형 및 보상 정의
- 자격 조건, 제한 설정 정리
- MembershipRewardCounter 동작 방식
- Google AdMob SSV 연동 흐름
- 카메라 잠금 해제 플로우
- 데이터 모델 ERD 및 JSON 스키마
- FAQ 작성