Files
sodalive-android/docs/plan-task/20260427_채팅탭Yandex배너광고추가.md

196 lines
16 KiB
Markdown

# 20260427 채팅 탭 Yandex 배너 광고 추가
## 작업 체크리스트
- [x] 대상 화면 3곳의 구조와 기존 Yandex 광고 패턴을 조사한다.
QA: `CharacterTabFragment`, `OriginalTabFragment`, `TalkTabFragment`, `LiveFragment`, `MyPageFragment`, 공식 Yandex adaptive inline banner 문서를 근거로 삽입 위치와 구현 패턴을 설명할 수 있어야 한다.
- [x] 화면별 AD_UNIT_ID 분리 전략과 추가 대상 키를 확정한다.
QA: `app/build.gradle``debug`/`release`에 각 화면용 `BuildConfig` 필드가 필요하다는 판단 근거가 문서에 남아 있어야 한다.
- [x] `app/build.gradle`에 Character/Original/Talk 탭용 Yandex inline banner ad unit id를 추가한다.
QA: `debug`/`release` 모두에서 3개 화면용 `BuildConfig` 값이 생성되어야 한다.
- [x] `fragment_character_tab.xml``CharacterTabFragment.kt`에 최근 대화한 캐릭터와 인기 캐릭터 사이 배너를 추가한다.
QA: `ll_latest_characters` 다음, `ll_popular_characters` 이전에 배너가 배치되고 기존 상단 콘텐츠 배너는 유지되어야 하며, 배너 최대 높이는 90dp를 넘지 않아야 한다.
- [x] `fragment_original_tab.xml``OriginalTabFragment.kt`에 최상단 배너를 추가한다.
QA: 배너가 `rv_original` 위에 배치되고, 리스트가 배너 아래부터 시작해야 하며, 프래그먼트 종료 시 리소스가 정리되어야 한다.
- [x] `fragment_talk_tab.xml``TalkTabFragment.kt`에 최상단 배너를 추가한다.
QA: 배너가 `rv_talk` 위에 배치되고, 빈 상태 문구가 배너와 겹치지 않아야 하며, 프래그먼트 종료 시 리소스가 정리되어야 한다.
- [x] 변경 사항을 빌드/테스트/수동 확인 기준으로 검증한다.
QA: 최소 `:app:assembleDebug`, `:app:testDebugUnitTest`, `:app:ktlintCheck` 결과와 수동 확인 가능 여부가 기록되어야 한다.
- [x] 검증 기록과 체크리스트를 문서 하단에 누적 갱신한다.
QA: 무엇/왜/어떻게, 실행 명령, 결과가 한국어로 누적되어야 한다.
- [x] Original/Talk 탭 배너를 RecyclerView 헤더로 이동한다.
QA: 배너가 고정 영역이 아니라 RecyclerView 첫 아이템으로 스크롤되어야 하며, Original 그리드에서는 전체 span을 차지해야 한다.
- [x] Original/Talk 탭의 세로 여백을 Character 탭 기준 24dp 흐름에 맞춘다.
QA: 스크롤 콘텐츠가 상단 24dp에서 시작하고, Talk/Original의 하단 여백이 24dp 기준으로 유지되어야 한다.
- [x] 후속 변경 검증 상태를 재실행 가능 상태로 갱신한다.
QA: 이번 요청의 도구 제한 때문에 Gradle 명령은 실행하지 않고, 재실행 대상 명령과 제한 사유를 문서에 남겨야 한다.
## 범위 메모
- 이번 요청 범위는 채팅 영역의 3개 화면에 Yandex adaptive inline banner를 추가하는 작업으로 한정한다.
- `CharacterTabFragment`는 기존 상단 콘텐츠 캐러셀 배너를 제거하지 않고, 최근 대화한 캐릭터와 인기 캐릭터 사이에 Yandex 배너를 추가한다.
- `OriginalTabFragment`, `TalkTabFragment`는 화면 최상단에 Yandex 배너를 추가하되, 후속 변경에서는 RecyclerView 헤더 아이템으로 이동해 리스트와 함께 스크롤되도록 한다.
- AD_UNIT_ID는 사용자 요청대로 페이지별로 분리하며, 기존 저장소 관례에 맞춰 `app/build.gradle``debug`/`release` `buildConfigField`로 관리한다.
- Yandex SDK 의존성과 앱 초기화는 이미 존재하므로 이번 작업에서 SDK 추가나 `SodaLiveApp.kt` 수정은 제외한다.
- 배너 로드 패턴은 현재 저장소의 `LiveFragment`, `MyPageFragment`에서 사용하는 `post { -> setAdUnitId -> setAdSize(BannerAdSize.inlineSize(...)) -> loadAd(...) }` 흐름을 우선 따른다.
- 배너 정리는 프래그먼트 뷰 생명주기에 맞춰 `onDestroyView()`에서 `destroy()`를 호출하는 방향으로 맞춘다.
## 조사 근거
- 대상 화면
- `app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt`
- `app/src/main/res/layout/fragment_character_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/original/OriginalTabFragment.kt`
- `app/src/main/res/layout/fragment_original_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkTabFragment.kt`
- `app/src/main/res/layout/fragment_talk_tab.xml`
- 기존 배너 구현 참고
- `app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt`
- `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`
- `app/src/main/res/layout/fragment_live.xml`
- 설정 참고
- `app/build.gradle`
- 공식 문서
- `https://ads.yandex.com/helpcenter/ko/dev/android/adaptive-inline-banner`
## 구현 계획
### 1. AD_UNIT_ID 추가
- 수정 대상: `app/build.gradle`
- 계획:
- `release``debug` 각각에 아래 키를 추가한다.
- `YANDEX_INLINE_BANNER_CHARACTER_TAB_AD_UNIT_ID`
- `YANDEX_INLINE_BANNER_ORIGINAL_TAB_AD_UNIT_ID`
- `YANDEX_INLINE_BANNER_TALK_TAB_AD_UNIT_ID`
- 실제 값은 아직 제공되지 않았으므로 화면별·빌드타입별 placeholder 문자열을 서로 다르게 사용한다.
- 이유:
- 기존 Yandex 광고가 모두 `BuildConfig` 기반으로 주입되고 있고, 사용자도 페이지별 분리를 명시했기 때문이다.
### 2. Character 탭 중간 배너 추가
- 수정 대상:
- `app/src/main/res/layout/fragment_character_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt`
- 위치:
- `ll_latest_characters` 다음, `ll_popular_characters` 이전
- 계획:
- 섹션 사이에 `BannerAdView`를 추가하고 기존 24dp 간격 흐름을 유지한다.
- `setupView()` 흐름에 배너 로드 메서드를 추가한다.
- 측정된 너비 기준으로 adaptive inline banner를 로드하고, 최대 높이는 90dp 상한을 유지한다.
- `onDestroyView()`에서 배너를 정리한다.
### 3. Original 탭 최상단 배너 추가
- 수정 대상:
- `app/src/main/res/layout/fragment_original_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/original/OriginalTabFragment.kt`
- 위치:
- 루트 최상단, `rv_original`
- 계획:
- `ConstraintLayout` 상단에 `BannerAdView`를 추가하고, `rv_original`의 top constraint를 배너 아래로 조정한다.
- 프래그먼트 로드 시 배너를 설정하고 종료 시 정리한다.
### 4. Talk 탭 최상단 배너 추가
- 수정 대상:
- `app/src/main/res/layout/fragment_talk_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkTabFragment.kt`
- 위치:
- 루트 최상단, `rv_talk`
- 계획:
- 최상단에 `BannerAdView`를 추가하고, `rv_talk``tv_empty`가 배너 아래 레이아웃 기준을 따르도록 constraint를 조정한다.
- 빈 상태 문구는 배너와 겹치지 않게 유지한다.
- 프래그먼트 종료 시 배너를 정리한다.
## 예상 수정 파일
- `docs/20260427_채팅탭Yandex배너광고추가.md`
- `app/build.gradle`
- `app/src/main/res/layout/fragment_character_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/character/CharacterTabFragment.kt`
- `app/src/main/res/layout/fragment_original_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/original/OriginalTabFragment.kt`
- `app/src/main/res/layout/fragment_talk_tab.xml`
- `app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkTabFragment.kt`
## 검증 계획
- `./gradlew :app:assembleDebug`
- `./gradlew :app:testDebugUnitTest`
- `./gradlew :app:ktlintCheck`
- 수동 확인
- Character 탭에서 최근 대화한 캐릭터와 인기 캐릭터 사이에 배너가 보이는지 확인한다.
- Original 탭에서 배너가 최상단에 보이고 그 아래부터 그리드가 시작하는지 확인한다.
- Talk 탭에서 배너가 최상단에 보이고, 빈 상태 문구가 배너와 겹치지 않는지 확인한다.
## 구현 반영 기록
- 2026-04-27
- 무엇: Character/Original/Talk 탭에 Yandex adaptive inline banner 구현을 반영했다.
- 왜: 각 채팅 탭 화면에 별도 광고 단위 키를 사용하고, 지정 위치에 inline banner를 추가하며, 뷰 종료 시 광고 리소스를 정리해야 하기 때문이다.
- 어떻게:
- `app/build.gradle``debug`/`release`에 화면별 placeholder `BuildConfig` 키 3개씩을 추가했다.
- `fragment_character_tab.xml``ll_latest_characters``ll_popular_characters` 사이에 `BannerAdView`를 추가했다.
- `fragment_original_tab.xml``fragment_talk_tab.xml`은 최상단 `BannerAdView` 아래로 리스트가 시작되도록 조정했고, Talk 빈 상태 문구도 배너 아래 영역을 기준으로 배치했다.
- 각 Fragment는 `setAdUnitId(...)`, `BannerAdSize.inlineSize(...)`, `AdRequest.Builder().build()` 패턴으로 배너를 로드하고 `onDestroyView()`에서 `destroy()`를 호출하도록 연결했다.
- 결과: 구현 체크리스트 중 코드 반영 항목을 완료 상태로 갱신했다. 빌드/테스트/수동 확인 결과는 아직 기록하지 않았다.
- 2026-04-27
- 무엇: Original/Talk 탭의 Yandex 배너를 고정 XML 자식에서 RecyclerView 헤더 아이템으로 이동하는 후속 변경을 반영했다.
- 왜: 배너가 리스트 바깥에 고정되어 있으면 Character 탭의 스크롤 콘텐츠 여백 흐름과 맞지 않고, 사용자가 요청한 “리스트와 함께 스크롤되는 배너” 조건을 만족하지 못하기 때문이다.
- 어떻게:
- `YandexInlineBannerHeaderAdapter`를 추가해 `BannerAdView`를 RecyclerView 첫 아이템으로 생성하고, 기존 Yandex SDK 7.18.5 방식인 `setAdUnitId(...)`, `BannerAdSize.inlineSize(...)`, `AdRequest.Builder().build()` 호출 흐름을 유지했다.
- `OriginalTabFragment``ConcatAdapter`로 배너 헤더와 기존 그리드 어댑터를 연결하고, `GridLayoutManager.SpanSizeLookup``GridSpacingItemDecoration(headerCount = 1)`로 헤더가 전체 3열을 차지하게 했다.
- Original 그리드는 기존 16dp 아이템 하단 간격을 유지하면서 스크롤 콘텐츠 끝 여백이 24dp가 되도록 RecyclerView 하단 padding을 8dp로 보정했다.
- `TalkTabFragment``ConcatAdapter`로 배너 헤더와 기존 Talk 어댑터를 연결하고, ItemDecoration이 헤더를 건너뛰도록 보정했다.
- `fragment_original_tab.xml`, `fragment_talk_tab.xml`에서는 고정 배너 뷰를 제거하고 RecyclerView를 부모 상단에 직접 연결했다.
- Talk 빈 상태 문구는 유지하되 RecyclerView는 배너 헤더를 표시할 수 있도록 빈 목록에서도 보이게 했다.
- 결과: 후속 변경의 코드 반영 항목을 완료 상태로 갱신했다.
## 검증 기록
- 2026-04-27
- 무엇: 채팅 탭 Yandex 배너 광고 추가 작업의 계획 문서를 생성했다.
- 왜: 저장소 규칙에 따라 구현 전에 `docs` 아래 계획 문서를 먼저 만들고, 범위·광고 위치·검증 기준을 먼저 고정해야 하기 때문이다.
- 어떻게:
- 생성 파일: `docs/20260427_채팅탭Yandex배너광고추가.md`
- 근거 파일: `app/build.gradle`, `CharacterTabFragment.kt`, `OriginalTabFragment.kt`, `TalkTabFragment.kt`, 각 대응 XML, `LiveFragment.kt`, `MyPageFragment.kt`
- 근거 문서: `https://ads.yandex.com/helpcenter/ko/dev/android/adaptive-inline-banner`
- 결과: 화면별 위치, AD_UNIT_ID 분리, 예상 수정 파일, 검증 계획을 구현 전에 확정했다.
- 2026-04-27
- 무엇: 채팅 탭 3개 화면의 Yandex 배너 추가 구현에 대해 빌드, 테스트, 린트, 수동 확인 가능 여부를 검증했다.
- 왜: 이번 변경은 `BuildConfig`, Kotlin, XML을 함께 수정하므로 실제 Android 리소스 병합과 컴파일, 테스트, 스타일 검사를 통과해야 안전하게 반영됐다고 볼 수 있기 때문이다.
- 어떻게:
- 진단 도구: `lsp_diagnostics`
- 진단 결과: `.kt`, `.xml` LSP 서버가 현재 환경에 설정되어 있지 않아 정적 진단은 수행하지 못했다.
- 실행 명령: `./gradlew :app:assembleDebug`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `./gradlew :app:testDebugUnitTest`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `./gradlew :app:ktlintCheck`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `adb devices`
- 실행 결과: 연결 기기 없음
- 수동 확인 결과: ADB 연결 기기가 없어 앱 실행 기반 수동 QA는 수행하지 못했다.
- 비고: `app/build.gradle`의 Character/Original/Talk 탭 ad unit id는 현재 placeholder 값이므로, 실제 광고 응답 확인은 실 ad unit id 교체 후 추가 검증이 필요하다.
- 2026-04-27
- 무엇: Original/Talk 탭 Yandex 배너 후속 변경의 정적 진단 가능 여부와 참조 정리를 확인했다.
- 왜: 이번 변경은 XML 바인딩 참조를 제거하고 RecyclerView 헤더 어댑터로 이동했으므로, 남은 고정 배너 참조와 문서 검증 상태를 확인해야 하기 때문이다.
- 어떻게:
- 진단 도구: `lsp_diagnostics`
- 진단 결과: Markdown 문서(`docs/20260427_채팅탭Yandex배너광고추가.md`)는 진단 없음.
- 진단 결과: `.kt`, `.xml` LSP 서버가 현재 환경에 설정되어 있지 않아 Kotlin/XML 정적 진단은 수행하지 못했다.
- 확인 도구: `grep`
- 확인 결과: `OriginalTabFragment`, `TalkTabFragment`, `fragment_original_tab.xml`, `fragment_talk_tab.xml`에는 기존 고정 배너 바인딩/ID 참조가 남아 있지 않다.
- 재실행 대기 명령: `./gradlew :app:assembleDebug`, `./gradlew :app:testDebugUnitTest`, `./gradlew :app:ktlintCheck`
- 결과: 이 시점에서는 Gradle 기반 빌드/테스트/린트를 아직 재실행하지 않았고, 기존 검증 명령은 재실행 준비 상태로 유지했다.
- 2026-04-27
- 무엇: Original/Talk 탭 배너 헤더 후속 변경에 대해 빌드, 테스트, 린트, 기기 연결 상태를 다시 검증했다.
- 왜: 이번 후속 변경은 배너를 RecyclerView 헤더 어댑터로 옮기고 간격 계산을 변경했으므로, 실제 Android 컴파일과 테스트를 다시 통과해야 안전하게 반영됐다고 볼 수 있기 때문이다.
- 어떻게:
- 진단 도구: `lsp_diagnostics`
- 진단 결과: `.kt`, `.xml` LSP 서버가 현재 환경에 설정되어 있지 않아 Kotlin/XML 정적 진단은 수행하지 못했다.
- 실행 명령: `./gradlew :app:assembleDebug`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `./gradlew :app:testDebugUnitTest`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `./gradlew :app:ktlintCheck`
- 실행 결과: `BUILD SUCCESSFUL`
- 실행 명령: `adb devices`
- 실행 결과: 한때 `2cec640c34017ece` 기기가 표시되었으나, 이후 설치/실행 시점에는 연결이 끊어졌다.
- 실행 명령: `./gradlew :app:installDebug`
- 실행 결과: `No connected devices!`
- 실행 명령: `adb shell am start -n kr.co.vividnext.sodalive.debug/kr.co.vividnext.sodalive.splash.SplashActivity`
- 실행 결과: `adb: no devices/emulators found`
- Oracle 검토 결과: 후속 변경은 요청한 스크롤 배너 요구사항을 충족하며, 즉시 수정이 필요한 correctness/layout/pagination 결함은 확인되지 않았다.
- 결과: 빌드/테스트/린트는 모두 통과했고, 수동 QA는 기기 연결 해제 때문에 완료하지 못했다. 남은 검증 공백은 Original/Talk 화면에서 배너가 첫 아이템처럼 함께 스크롤되는지 실제 기기에서 확인하는 것이다.