feat(chat): 채팅 쿼터 광고 충전을 추가한다
This commit is contained in:
119
docs/20260430_채팅쿼터충전확장.md
Normal file
119
docs/20260430_채팅쿼터충전확장.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# 20260430 채팅 쿼터 충전 확장
|
||||
|
||||
## 작업 체크리스트
|
||||
- [x] `ChatRoomViewModel`의 쿼터 안내 표시 기준을 `nextRechargeAtEpoch` null 여부에서 `totalRemaining <= 0` 기준으로 전환한다.
|
||||
- [x] 무료 충전이 없어진 정책에 맞춰 채팅방 쿼터 카운트다운/무료 대기 관련 상태와 타이머 로직을 제거한다.
|
||||
- [x] `ChatQuotaNoticeItemView`를 2단 구성으로 재작성하고, 광고 버튼 1개 + 캔 구매 버튼 2개 UI를 요구사항 스펙에 맞게 반영한다.
|
||||
- [x] 채팅 쿼터 구매 요청 DTO를 `CAN`/`AD` 충전 타입과 캔 옵션을 전달할 수 있도록 확장한다.
|
||||
- [x] `ChatRoomQuotaChargeType`, `ChatRoomQuotaCanOption` 모델을 iOS 코드베이스 규칙에 맞게 추가한다.
|
||||
- [x] 채팅방 쿼터 구매 흐름을 광고 보상 충전과 캔 옵션 충전으로 분기한다.
|
||||
- [x] `totalRemaining <= 1`일 때 채팅 쿼터 전용 Yandex rewarded 광고를 준비하도록 채팅방 진입/상태 갱신 흐름을 확장한다.
|
||||
- [x] 광고 버튼 탭 시 Yandex rewarded 광고를 표시하고, 리워드 지급 가능 시점에만 쿼터 충전 API를 호출하도록 연결한다.
|
||||
- [x] 채팅방 쿼터 관련 문자열/I18n 사용 여부를 정리하고, 제거/추가가 필요한 문구를 반영한다.
|
||||
- [x] 수정 파일 진단과 빌드를 실행하고 결과를 검증 기록에 남긴다.
|
||||
|
||||
## 작업 기준
|
||||
|
||||
- 사용자 요청 대상 화면:
|
||||
- `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 기준
|
||||
|
||||
- `ChatRoomViewModel`은 `nextRechargeAtEpoch` null 여부와 무관하게 `totalRemaining <= 0`일 때만 `showQuotaNoticeView`를 `true`로 만든다.
|
||||
- 채팅방 쿼터 안내 영역에는 더 이상 시간 아이콘, 카운트다운 텍스트, `기다리면 무료 이용이 가능합니다` 문구가 표시되지 않는다.
|
||||
- 상단 광고 버튼은 `광고 / 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 분기와 광고 준비 판단은 이 값을 기준으로 통일한다.
|
||||
- 현재 `ChatQuotaPurchaseRequest`는 `container`만 전송하므로, 요청 스펙 확장 시 기존 기본값(`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 콜백이 동작하지 않는다는 제한이 명시되어 있지 않다.
|
||||
- 공식 문서는 `RewardedAd`와 `RewardedAdLoader`의 강한 참조 유지, 로드 성공 후 delegate 설정을 권장한다.
|
||||
- `YandexRewardedAdManager.preloadAd`에서 로드 성공 직후 `loadedAd.delegate = self`를 설정하도록 변경했다.
|
||||
- `showAdIfAvailable`에서 광고 표시 직전 `rewardedAd`를 nil로 만들지 않도록 변경해 표시 생명주기 동안 강한 참조를 유지했다.
|
||||
- `SodaLive-dev` Debug 빌드 성공.
|
||||
- SourceKit 단독 LSP는 기존과 동일하게 `YandexMobileAds` 모듈 미해결 환경성 오류를 보고했으나, 실제 `xcodebuild`는 통과했다.
|
||||
Reference in New Issue
Block a user