Files
sodalive-android/docs/20260430_채팅쿼터충전확장계획.md

15 KiB

20260430 채팅 쿼터 충전 확장 계획

작업 체크리스트

  • 기존 채팅 쿼터 UI, 응답 DTO, 구매 API, Yandex 광고 사용 패턴을 근거 파일 기준으로 조사한다.
    QA: ChatRoomActivity, ChatMessageAdapter, ChatQuotaPurchaseRequest, TalkApi, ChatRepository, AudioContentDetailActivity, app/build.gradle를 근거로 현재 구조를 설명할 수 있어야 한다.
  • 쿼터 안내 노출 조건을 nextRechargeAtEpoch != null에서 totalRemaining <= 0 기준으로 전환한다.
    QA: 채팅방 입장, 메시지 전송 응답, 쿼터 상태 조회, 쿼터 구매 응답 모두에서 updateQuotaUitotalRemaining 기준으로만 노출 여부를 결정해야 한다.
  • 무료 충전 제거에 맞춰 카운트다운 및 무료 대기 문구 관련 로직을 제거한다.
    QA: CountDownTimer, tv_time, nextRechargeAtEpoch 기반 갱신, checkQuotaStatus()의 무료 충전 재조회 목적 로직이 더 이상 남지 않아야 한다.
  • item_chat_quota_notice를 2단 구성의 신규 구매 UI로 교체한다.
    QA: 상단에는 광고 버튼 1개, 하단에는 캔 구매 버튼 2개가 가로 배치되어야 하며, 기존 시간/무료 안내 문구 영역은 제거되어야 한다.
  • 광고 버튼과 캔 구매 버튼의 텍스트/스타일을 요청 사양대로 반영한다.
    QA: 광고 버튼은 광고 / 5채팅, 하단 버튼은 10 / 15채팅, 20 / 40채팅 구조를 가지며, 각 캔 숫자 앞에 ic_can 아이콘이 표시되고 캔 숫자는 bold, 채팅 개수는 medium이어야 한다.
  • 쿼터 구매 요청 DTO를 광고/캔 구매 구분이 가능하도록 확장한다.
    QA: 요청 본문에 container, chargeType, canOption이 포함되고, 캔 구매 시 CAN_10, CAN_20, 광고 구매 시 AD 타입을 표현할 수 있어야 한다.
  • Yandex 채팅 쿼터 전용 rewarded ad unit id를 buildType별 BuildConfig로 추가한다.
    QA: debug/release에서 채팅 쿼터 광고용 별도 BuildConfig 키가 생성되어야 하며, 기존 오디오 콘텐츠 전면 광고 id와 분리되어야 한다.
  • totalRemaining == 1일 때 광고를 미리 준비하고, 광고 버튼 터치 시 rewarded 광고를 표시한 뒤 onRewarded() 시점에 쿼터 충전 API를 호출하도록 흐름을 추가한다.
    QA: 광고 미로드/표시 실패 시에는 API가 호출되지 않아야 하고, 사용자가 reward 조건을 충족했을 때만 AD 타입 구매 요청이 한 번만 전송되어야 한다.
  • 캔 구매 버튼 2종이 각각 올바른 구매 옵션으로 API를 호출하고, 성공 시 헤더 캔 수와 쿼터 UI를 갱신하도록 정리한다.
    QA: 10캔 버튼은 15채팅, 20캔 버튼은 40채팅과 연결되고, 성공 후 SharedPreferenceManager.cantvCanBadge가 실제 차감값에 맞게 갱신되어야 한다.
  • 변경 결과를 문서 하단 검증 기록에 누적한다.
    QA: 최소 빌드, 테스트, 수동 확인 계획과 실제 실행 결과가 한국어로 누적되어야 한다.

범위 메모

  • 이번 요청은 ChatRoomActivity의 채팅 쿼터 부족 안내와 구매 흐름을 광고/캔 3가지 선택지로 확장하는 작업으로 한정한다.
  • 무료 충전은 제거되므로, 기존 nextRechargeAtEpoch 기반 표시 판단과 카운트다운 UI/로직은 제거 대상으로 본다.
  • 안내 노출 여부는 totalRemaining <= 0일 때만 표시하는 방향으로 고정한다.
  • 광고 버튼은 Yandex rewarded ad를 사용하고, reward 지급 콜백인 onRewarded() 시점에 쿼터 충전 API를 호출한다.
  • reward 기준으로 처리하므로, 광고가 단순히 표시되거나 닫힌 것만으로는 보상을 지급하지 않는다.
  • 광고 unit id는 기존 app/build.gradlebuildConfigField 패턴을 유지하되, 채팅 쿼터 지면은 별도 키로 분리한다.
  • 캔 구매 옵션은 요청에 맞춰 10캔→15채팅, 20캔→40채팅 두 가지만 제공한다.
  • AD 타입 쿼터 지급은 서버가 광고 보상 완료를 검증할 수 있는 구조(예: SSV, 검증 토큰, nonce/idempotency)인지 별도 확인이 필요하다.
  • 요청 범위를 넘는 별도 결제 화면 추가, 무료 충전 대체 UX 확장, 다른 화면 공통화 리팩터링은 제외한다.

조사 근거

  • 현재 채팅 쿼터 흐름
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapter.kt
    • app/src/main/res/layout/item_chat_quota_notice.xml
  • 쿼터 응답/구매 DTO
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomEnterResponse.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/SendChatMessageResponse.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaStatusResponse.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaPurchaseRequest.kt
  • API/Repository
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt
  • 기존 Yandex 광고/전면광고 패턴
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt
    • app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt
    • app/build.gradle
  • 공식 문서
    • https://ads.yandex.com/helpcenter/en/dev/android/interstitial
    • https://ads.yandex.com/helpcenter/en/dev/android/rewarded

구현 계획

1. 쿼터 상태 판단 기준 전환

  • 수정 대상:
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomEnterResponse.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/SendChatMessageResponse.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaStatusResponse.kt
  • 계획:
    • updateQuotaUi 시그니처와 호출부를 nextRechargeAtEpoch 대신 totalRemaining 중심으로 재구성한다.
    • 입장 응답, 메시지 전송 응답, 쿼터 조회/구매 응답에서 모두 동일한 판단식을 사용한다.
    • inputContainer 표시 여부도 totalRemaining > 0 기준으로 정리한다.

2. 무료 충전/카운트다운 제거

  • 수정 대상:
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapter.kt
    • app/src/main/res/layout/item_chat_quota_notice.xml
  • 계획:
    • quotaTimer, startQuotaCountdown, stopQuotaCountdown, formatEpochToHms, formatMillisToHms, checkQuotaStatus의 역할을 재평가하고, 무료 충전용 로직은 제거한다.
    • ChatListItem.QuotaNotice가 시간 텍스트를 들고 다니는 구조를 단순화한다.
    • QuotaNoticeViewHoldertv_time 의존성을 제거하고, 버튼 클릭만 처리하는 형태로 바꾼다.

3. QuotaNotice 레이아웃 교체

  • 수정 대상:
    • app/src/main/res/layout/item_chat_quota_notice.xml
    • 필요 시 관련 drawable / string 리소스
  • 계획:
    • 상단 시간/무료 안내 블록을 제거하고 광고 버튼 1개를 match_parent로 배치한다.
    • 하단에는 좌우 2개 버튼을 동일 행에 둔다.
    • 광고 버튼 배경색 RGB(254, 248, 227), border RGB(247, 203, 80)를 기존 코드 스타일에 맞춰 hex로 정의한다.
    • 버튼 텍스트는 국제화 리소스로 분리하되, 요청 문구를 그대로 반영한다.

4. 캔 구매 DTO 및 API 호출 확장

  • 수정 대상:
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaPurchaseRequest.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
  • 계획:
    • 기존 ChatQuotaPurchaseRequest를 요청 사양에 맞게 확장하고, 필요하면 내부 클래스명도 API 계약과 맞는 방향으로 정리한다.
    • ChatRoomQuotaChargeType, ChatRoomQuotaCanOption enum을 추가한다.
    • purchaseChatQuota(...)가 광고/캔 버튼별로 다른 요청 본문을 받을 수 있게 repository 시그니처를 확장한다.

5. 채팅 쿼터 전용 Yandex rewarded 흐름 추가

  • 수정 대상:
    • app/build.gradle
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
  • 계획:
    • debug/release 각각에 채팅 쿼터 rewarded ad unit id BuildConfig 키를 추가한다.
    • 기존 Yandex 광고 초기화 구조는 유지하고, rewarded ad의 loader / load listener / event listener / show(activity) 패턴을 채팅방에 맞게 적용한다.
    • totalRemaining == 1 시점에 preload를 시도하고, 광고 버튼 터치 시 로드된 광고가 있으면 표시한다.
    • onRewarded() 콜백에서 AD 타입 쿼터 구매 API를 호출한다.
    • onAdFailedToShow, onAdDismissed, onDestroy에서 listener와 ad 참조를 정리한다.

6. 버튼별 구매 액션과 로컬 상태 갱신 정리

  • 수정 대상:
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapter.kt
    • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
  • 계획:
    • 어댑터 콜백을 광고/10캔/20캔으로 구분 가능한 형태로 확장하거나, 단일 콜백에 옵션 인자를 전달하도록 바꾼다.
    • 캔 구매 성공 시에는 선택한 needCan만큼 SharedPreferenceManager.can을 차감하고 tvCanBadge를 갱신한다.
    • 광고 구매 성공 시에는 캔 차감 없이 쿼터 UI만 갱신한다.
    • 응답의 totalRemaining 기준으로 입력창 복구 여부와 QuotaNotice 제거 여부를 결정한다.

예상 수정 파일

  • docs/20260430_채팅쿼터충전확장계획.md
  • app/build.gradle
  • app/src/main/res/layout/item_chat_quota_notice.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/res/values-en/strings.xml
  • app/src/main/res/values-ja/strings.xml
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapter.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaPurchaseRequest.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaStatusResponse.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomEnterResponse.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/SendChatMessageResponse.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt
  • app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt

검증 계획

  • ./gradlew :app:assembleDebug
  • ./gradlew :app:testDebugUnitTest
  • 필요 시 ./gradlew :app:ktlintCheck
  • 수동 확인:
    • totalRemaining > 0 상태에서 입력창이 보이고 QuotaNotice가 사라지는지 확인한다.
    • totalRemaining <= 0 상태에서 신규 QuotaNotice UI가 노출되는지 확인한다.
    • totalRemaining == 1일 때 rewarded 광고 preload가 준비되는지 로그 또는 디버그 포인트로 확인한다.
    • 광고 버튼 탭 시 rewarded 광고가 뜨고 onRewarded() 직후 AD 타입 구매 요청이 1회 호출되는지 확인한다.
    • 10캔/20캔 버튼 각각이 15채팅/40채팅 구매와 연결되고, 성공 후 헤더 캔 수와 입력창 상태가 갱신되는지 확인한다.

검증 기록

  • 2026-04-30
    • 무엇: 채팅 쿼터 충전 확장 작업의 계획 문서를 생성했다.
    • 왜: 저장소 규칙에 따라 구현 전에 docs 아래 계획 문서를 먼저 만들고, 그 문서를 기준으로 범위·근거·검증 기준을 고정해야 하기 때문이다.
    • 어떻게:
      • 생성 파일: docs/20260430_채팅쿼터충전확장계획.md
      • 근거 파일: ChatRoomActivity.kt, ChatMessageAdapter.kt, item_chat_quota_notice.xml, ChatQuotaPurchaseRequest.kt, ChatQuotaStatusResponse.kt, TalkApi.kt, ChatRepository.kt, ChatRoomEnterResponse.kt, SendChatMessageResponse.kt, AudioContentDetailActivity.kt, app/build.gradle
      • 근거 문서: https://ads.yandex.com/helpcenter/en/dev/android/interstitial, https://ads.yandex.com/helpcenter/en/dev/android/rewarded
      • 결과: 무료 충전 제거, totalRemaining 기준 노출 전환, 광고/캔 3가지 구매 UI, DTO 확장, Yandex rewarded 연동 범위, 예상 수정 파일과 검증 계획을 구현 전에 먼저 확정했다.
  • 2026-04-30
    • 무엇: 채팅 쿼터 충전 확장 구현과 검증을 완료했다.
    • 왜: 무료 충전 제거 이후에도 채팅 쿼터 부족 상태에서 rewarded 광고와 2종의 캔 구매 옵션으로 다시 충전할 수 있어야 했기 때문이다.
    • 어떻게:
      • 수정 파일: app/build.gradle, app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt, app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapter.kt, app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/quota/ChatQuotaPurchaseRequest.kt, app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt, app/src/main/res/layout/item_chat_quota_notice.xml, app/src/main/res/drawable/bg_chat_quota_rewarded_ad_button.xml, app/src/main/res/values/strings.xml, app/src/main/res/values-en/strings.xml, app/src/main/res/values-ja/strings.xml, app/src/test/java/kr/co/vividnext/sodalive/chat/talk/room/ChatMessageAdapterTest.kt
      • 구현 내용: totalRemaining 기준 QuotaNotice 노출 전환, countdown/free-wait 로직 제거, rewarded 광고 preload/show/onRewarded 구매 호출, 10캔/20캔 구매 분기, 채팅 쿼터 전용 rewarded ad unit id 추가, QuotaNotice 3버튼 UI 적용
      • 실행 명령: ./gradlew :app:testDebugUnitTest :app:assembleDebug
      • 실행 결과: BUILD SUCCESSFUL
      • 실행 명령: ./gradlew :app:ktlintCheck
      • 실행 결과: BUILD SUCCESSFUL
      • 진단 도구: lsp_diagnostics
      • 진단 결과: .kt LSP 서버 미설정으로 No LSP server configured for extension: .kt
      • 수동 확인 시도: adb devices
      • 수동 확인 결과: 연결된 Android 기기가 없어 앱 실행 기반의 광고 노출/버튼 동작 수동 검증은 이번 세션에서 진행하지 못했다.
  • 2026-04-30
    • 무엇: 리뷰 피드백을 반영해 중복 요청 방지와 rewarded preload 조건을 보강하고, 서버 검증 리스크를 문서에 기록했다.
    • 왜: 빠른 연속 탭으로 인한 중복 구매/중복 광고 시도와, 이미 quota가 소진된 상태에서 첫 광고 탭이 항상 실패하는 UX를 줄여야 했기 때문이다.
    • 어떻게:
      • 수정 파일: app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt, docs/20260430_채팅쿼터충전확장계획.md
      • 로직 보강: isQuotaPurchaseInFlight, isChatQuotaRewardedAdShowing 가드 추가, rewarded preload 조건을 totalRemaining <= 1로 확장
      • 문서 반영: AD 타입 쿼터 지급의 서버 검증/SSV/idempotency 확인 필요성을 범위 메모에 기록