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

11 KiB

20260430 채팅 쿼터 충전 확장

작업 체크리스트

  • ChatRoomViewModel의 쿼터 안내 표시 기준을 nextRechargeAtEpoch null 여부에서 totalRemaining <= 0 기준으로 전환한다.
  • 무료 충전이 없어진 정책에 맞춰 채팅방 쿼터 카운트다운/무료 대기 관련 상태와 타이머 로직을 제거한다.
  • ChatQuotaNoticeItemView를 2단 구성으로 재작성하고, 광고 버튼 1개 + 캔 구매 버튼 2개 UI를 요구사항 스펙에 맞게 반영한다.
  • 채팅 쿼터 구매 요청 DTO를 CAN/AD 충전 타입과 캔 옵션을 전달할 수 있도록 확장한다.
  • ChatRoomQuotaChargeType, ChatRoomQuotaCanOption 모델을 iOS 코드베이스 규칙에 맞게 추가한다.
  • 채팅방 쿼터 구매 흐름을 광고 보상 충전과 캔 옵션 충전으로 분기한다.
  • totalRemaining <= 1일 때 채팅 쿼터 전용 Yandex rewarded 광고를 준비하도록 채팅방 진입/상태 갱신 흐름을 확장한다.
  • 광고 버튼 탭 시 Yandex rewarded 광고를 표시하고, 리워드 지급 가능 시점에만 쿼터 충전 API를 호출하도록 연결한다.
  • 채팅방 쿼터 관련 문자열/I18n 사용 여부를 정리하고, 제거/추가가 필요한 문구를 반영한다.
  • 수정 파일 진단과 빌드를 실행하고 결과를 검증 기록에 남긴다.

작업 기준

  • 사용자 요청 대상 화면:
    • SodaLive/Sources/Chat/Talk/Room/ChatRoomView.swift
    • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaNoticeItemView.swift
  • 현재 쿼터 상태/로직:
    • SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift
    • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaStatusResponse.swift
    • SodaLive/Sources/Chat/Talk/Room/Enter/ChatRoomEnterResponse.swift
    • SodaLive/Sources/Chat/Talk/Room/Message/SendChatMessageResponse.swift
  • 쿼터 구매 API/DTO:
    • SodaLive/Sources/Chat/Talk/Room/ChatRoomRepository.swift
    • SodaLive/Sources/Chat/Talk/TalkApi.swift
    • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift
  • 광고 공용 지원:
    • SodaLive/Sources/Common/YandexAdSupport.swift
    • SodaLive/Sources/App/AppDelegate.swift
  • 문자열 경로:
    • SodaLive/Sources/I18n/I18n.swift
  • 공식 문서:
    • https://ads.yandex.com/helpcenter/en/dev/ios/rewarded

QA 기준

  • ChatRoomViewModelnextRechargeAtEpoch null 여부와 무관하게 totalRemaining <= 0일 때만 showQuotaNoticeViewtrue로 만든다.
  • 채팅방 쿼터 안내 영역에는 더 이상 시간 아이콘, 카운트다운 텍스트, 기다리면 무료 이용이 가능합니다 문구가 표시되지 않는다.
  • 상단 광고 버튼은 광고 / 5채팅 라벨로 표시되고, 배경색은 hex FEF8E3(RGB 254, 248, 227), 보더는 hex F7CB50(RGB 247, 203, 80)로 적용된다.
  • 하단에는 가로 2개 버튼이 표시되고, 왼쪽은 ic_can + 10 / 15채팅, 오른쪽은 ic_can + 20 / 40채팅 구성이 적용된다.
  • 버튼 내 캔 숫자는 bold, 채팅 개수 텍스트는 medium 폰트로 구분된다.
  • 채팅 쿼터 구매 요청은 광고 충전 시 chargeType = AD, 캔 충전 시 chargeType = CAN과 선택한 canOption을 함께 전송한다.
  • totalRemaining <= 1 상태 진입 시 채팅 쿼터 전용 rewarded 광고가 준비되고, 광고 버튼 탭 시 로드된 광고가 있으면 표시된다.
  • rewarded 광고 보상 가능 콜백에서만 쿼터 충전 API가 호출되고, 로드 실패/표시 실패/보상 미지급 시에는 API가 호출되지 않는다.
  • 광고/캔 충전 성공 후 채팅 쿼터 UI와 사용자 can 잔액이 응답 기준으로 올바르게 갱신된다.
  • 변경 파일 lsp_diagnostics 확인과 xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build, xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build가 통과한다.

구현 메모

  • 현재 ChatRoomViewModel.updateQuota(nextRechargeAtEpoch:)는 epoch 존재 여부와 타이머로 쿼터 안내 노출을 제어하므로, totalRemaining을 받는 형태로 시그니처와 호출부 전체를 함께 정리해야 한다.
  • ChatRoomEnterResponse, SendChatMessageResponse, ChatQuotaStatusResponse에는 이미 totalRemaining이 있으므로, UI 분기와 광고 준비 판단은 이 값을 기준으로 통일한다.
  • 현재 ChatQuotaPurchaseRequestcontainer만 전송하므로, 요청 스펙 확장 시 기존 기본값(ios)은 유지하고 charge type / can option만 추가한다.
  • ChatQuotaNoticeItemView의 기존 I18n.Chat.Room.quotaWaitForFreeNotice, quotaPurchaseAction(chatCount:)는 신규 UI에 맞지 않을 수 있으므로 재사용 여부를 점검하고 필요 시 신규 문자열을 추가한다.
  • 기존 YandexAdSupport.swift에는 YandexInlineBannerView, YandexInterstitialAdManager만 있으므로, rewarded 광고는 동일 파일에 공용 매니저를 추가하는 방향을 우선 검토한다.
  • Yandex 공식 rewarded 문서 기준으로 SDK 호출은 메인 스레드에서 수행하고, RewardedAdLoader 로드 성공 후 광고 객체를 유지한 뒤 didReward 시점에만 보상 API를 연결한다.
  • 광고 unit id는 기존 배너/전면광고 unit id를 재사용하지 않고, 채팅 쿼터 rewarded 광고 전용 상수를 Constants 계층에 별도로 추가한다. 실제 상수 추가 위치는 구현 시 운영/디버그 Constants 파일 확인 후 확정한다.
  • totalRemaining <= 1에서 “광고 준비”를 요구하므로, 채팅방 진입 직후/메시지 전송 응답/쿼터 상태 재조회/충전 완료 이후까지 동일 조건으로 preload 경로가 유지되어야 한다.

검증 기록

  • 2026-04-30 / 계획 수립

    • 무엇/왜/어떻게: 채팅 쿼터 충전 확장 구현 전에 현재 채팅방 쿼터 표시 로직, 구매 DTO, Yandex 광고 지원 범위를 확인하고 실제 수정 범위를 계획 문서로 정리했다.
    • 확인 근거:
      • SodaLive/Sources/Chat/Talk/Room/ChatRoomView.swift
      • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaNoticeItemView.swift
      • SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift
      • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift
      • SodaLive/Sources/Common/YandexAdSupport.swift
      • SodaLive/Sources/I18n/I18n.swift
      • https://ads.yandex.com/helpcenter/en/dev/ios/rewarded
      • 기존 계획 문서 패턴: docs/20260320_채팅창얼림버튼및문구수정.md, docs/20260428_채팅탭Yandex배너추가.md, docs/20260428_Yandex광고화면배치구현.md
    • 결과:
      • 현재 쿼터 안내는 nextRechargeAtEpoch 기반 카운트다운 구조임을 확인
      • totalRemaining은 응답 모델에 존재하지만 UI 분기에는 아직 미사용임을 확인
      • 공용 Yandex 지원은 배너/인터스티셜까지 구현되어 있고 rewarded 지원은 별도 추가가 필요함을 확인
      • 위 범위를 기준으로 구현 체크리스트와 QA 기준을 확정
  • 2026-04-30 / 구현 및 검증

    • 무엇/왜/어떻게: 무료 충전 제거 정책에 맞춰 채팅 쿼터 안내 노출 기준을 totalRemaining <= 0으로 전환하고, 광고/캔 충전 UI와 CAN/AD 구매 요청 DTO, Yandex rewarded 광고 보상 콜백 기반 API 호출 흐름을 구현했다.
    • 실행 명령/도구:
      • lsp_diagnostics:
        • SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift
        • SodaLive/Sources/Chat/Talk/Room/ChatRoomView.swift
        • SodaLive/Sources/Common/YandexAdSupport.swift
        • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift
        • SodaLive/Sources/I18n/I18n.swift
        • SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaNoticeItemView.swift
      • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
      • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build
      • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test
      • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
      • rg "countdownText|quotaWaitForFreeNotice|quotaPurchaseAction|remainingTime|stopTimer|startTimer\(" "SodaLive/Sources/Chat/Talk/Room" "SodaLive/Sources/I18n/I18n.swift"
      • rg "totalRemaining <= 0|totalRemaining <= 1|ChatRoomQuotaChargeType|ChatRoomQuotaCanOption|YandexRewardedAdManager|YANDEX_CHAT_ROOM_QUOTA_REWARDED_AD_UNIT_ID|quotaAdAction|quotaChatCount" "SodaLive/Sources"
    • 결과:
      • ChatQuotaPurchaseRequest.swift는 LSP 진단 없음.
      • 나머지 SwiftUI/네트워크/광고 파일은 SourceKit 단독 해석 환경에서 Kingfisher, Moya, YandexMobileAds, 프로젝트 확장 심볼을 해석하지 못하는 환경성 오류가 있었지만, 실제 xcodebuild 실컴파일은 두 스킴 모두 통과했다.
      • SodaLive-dev Debug 빌드 성공.
      • SodaLive Debug 빌드 성공. Crashlytics dSYM 관련 기존 빌드 경고는 있었지만 빌드는 성공했다.
      • 테스트는 두 스킴 모두 Scheme ... is not currently configured for the test action으로 실행 불가했다.
      • 제거 대상 카운트다운/무료 대기 심볼 검색 결과는 없음.
      • 신규 쿼터 기준/DTO/광고/I18n 심볼 검색 결과가 기대 위치에서 확인됨.
      • 채팅 쿼터 rewarded ad unit id는 별도 상수로 추가했으며, 실제 운영 unit id가 제공되지 않아 현재 값은 Yandex 공식 demo rewarded unit id(demo-rewarded-yandex)로 둔다.
  • 2026-04-30 / rewarded 콜백 미호출 수정

    • 무엇/왜/어떻게: rewarded 광고가 표시된 뒤 didReward/didDismiss 콜백이 호출되지 않는 문제를 공식 문서 기준으로 점검하고, 로드 성공 직후 RewardedAd.delegate를 설정하며 광고 표시 중 RewardedAd 강한 참조를 유지하도록 수정했다.
    • 실행 명령/도구:
      • 공식 문서 확인: https://ads.yandex.com/helpcenter/en/dev/ios/rewarded, https://ads.yandex.com/helpcenter/en/dev/ios/demo-blocks
      • rg "loadedAd.delegate = self|rewardedAd.show|rewardedAd = nil|didReward|purchaseChatQuota\(chargeType: \.ad" "SodaLive/Sources/Common/YandexAdSupport.swift" "SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift"
      • lsp_diagnostics: SodaLive/Sources/Common/YandexAdSupport.swift
      • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
    • 결과:
      • 공식 문서에는 iOS Simulator에서 rewarded 콜백이 동작하지 않는다는 제한이 명시되어 있지 않다.
      • 공식 문서는 RewardedAdRewardedAdLoader의 강한 참조 유지, 로드 성공 후 delegate 설정을 권장한다.
      • YandexRewardedAdManager.preloadAd에서 로드 성공 직후 loadedAd.delegate = self를 설정하도록 변경했다.
      • showAdIfAvailable에서 광고 표시 직전 rewardedAd를 nil로 만들지 않도록 변경해 표시 생명주기 동안 강한 참조를 유지했다.
      • SodaLive-dev Debug 빌드 성공.
      • SourceKit 단독 LSP는 기존과 동일하게 YandexMobileAds 모듈 미해결 환경성 오류를 보고했으나, 실제 xcodebuild는 통과했다.