Files
sodalive-android/docs/20260424_커뮤니티시리즈알림Yandex배너광고추가계획.md

20 KiB

20260424 커뮤니티/시리즈/알림 Yandex 배너 광고 추가 계획

작업 체크리스트

  • 대상 6개 화면의 배너 삽입 위치와 기존 Yandex inline banner 패턴을 최종 확정한다.
    QA: CreatorCommunityAllActivity, SeriesMainHomeFragment, SeriesMainDayOfWeekFragment, SeriesMainByGenreFragment, PushNotificationListActivity, NotificationReceiveSettingsActivity와 기존 MyPageFragment, LiveFragment, LiveRoomDetailFragment, AudioContentDetailActivity를 근거로 각 위치를 설명할 수 있어야 한다.
  • AD_UNIT_ID 운영 방식을 기존 광고와 동일한 조건으로 정리하고, 생성/재사용 기준을 문서에 고정한다.
    QA: app/build.gradle의 기존 YANDEX_INLINE_BANNER_* buildConfigField 패턴을 기준으로, 어떤 지면이 기존 ID를 재사용하고 어떤 경우에만 신규 ID를 만드는지 설명할 수 있어야 한다.
  • 크리에이터 커뮤니티 전체보기 화면의 탭과 RecyclerView 사이 배너 추가 계획을 수립한다.
    QA: activity_creator_community_all.xml에서 탭 하단 구분선 아래, 콘텐츠 FrameLayout 위에 배너 위치가 명시되어야 한다.
  • 시리즈 메인 홈 화면의 완결 시리즈와 추천 시리즈 사이 배너 추가 계획을 수립한다.
    QA: fragment_series_main_home.xml에서 ll_completed_series 다음, ll_recommend_series 이전에 배너 위치가 명시되어야 한다.
  • 시리즈 메인 요일별 화면의 요일 RecyclerView와 시리즈 RecyclerView 사이 배너 추가 계획을 수립한다.
    QA: fragment_series_main_day_of_week.xml에서 rv_series_day_of_week_day 다음, rv_series_day_of_week 이전에 배너 위치가 명시되어야 한다.
  • 시리즈 메인 장르별 화면의 장르 RecyclerView와 시리즈 RecyclerView 사이 배너 추가 계획을 수립한다.
    QA: fragment_series_main_by_genre.xml에서 rv_genre 다음, rv_series_by_genre 이전에 배너 위치가 명시되어야 한다.
  • 알림 리스트 화면의 카테고리와 알림 리스트 사이 배너 추가 계획을 수립한다.
    QA: activity_push_notification_list.xml에서 rv_category 아래, rv_notification를 감싸는 FrameLayout 위 또는 내부 상단에 배너 위치와 빈 상태(ll_empty) 영향이 정리되어야 한다.
  • 알림 수신 설정 화면의 팔로잉 채널과 서비스 알림 사이 배너 추가 계획을 수립한다.
    QA: activity_notification_receive_settings.xml에서 서비스 알림 카드 영역 아래, 팔로잉 채널 섹션 제목 위에 배너 위치가 명시되어야 한다.
  • 각 화면 생명주기에 맞는 배너 로드/정리 규칙과 검증 계획을 문서에 남긴다.
    QA: Activity/Fragment 종료 시 BannerAdView.destroy() 호출 위치, assembleDebug/testDebugUnitTest 기준, 수동 확인 포인트가 문서에 있어야 한다.

범위 메모

  • 이번 요청 범위는 아래 6개 화면에 Yandex adaptive inline banner를 추가하기 위한 계획 문서 작성으로 한정한다.
    • CreatorCommunityAllActivity
    • SeriesMainHomeFragment
    • SeriesMainDayOfWeekFragment
    • SeriesMainByGenreFragment
    • PushNotificationListActivity
    • NotificationReceiveSettingsActivity
  • 광고 포맷은 모두 inline banner로 통일하고, 전면 광고·보상형 광고·배너 슬라이더 변경은 이번 범위에 포함하지 않는다.
  • 기존 Yandex SDK 의존성과 앱 초기화(SodaLiveApp)는 이미 존재하므로 이번 작업에서 SDK 추가나 초기화 구조 변경은 제외한다.
  • 기존 광고와 동일하게 ad unit id는 app/build.gradledebug/release buildConfigField를 우선 기준으로 사용한다.
  • AD_UNIT_ID 생성 또는 재사용 원칙
    • 기존 광고와 동일한 운영 조건으로 판단한다.
    • 동일 포맷(inline banner)이고 운영에서 같은 지면으로 취급해도 되는 경우에는 기존 AD_UNIT_ID 재사용을 우선 검토한다.
    • 화면 목적, 노출 위치, 성과 분리, 운영 정책 분리가 필요한 경우에만 같은 방식의 신규 buildConfigField를 생성한다.
    • 최종 재사용 여부는 서버/광고 운영 기준 확인 후 확정하되, 구현 구조는 재사용/신규 생성 모두 수용 가능하게 계획한다.
    • 현재 구현은 지면별 buildConfigField를 분리했고, app/build.gradle에 각 지면별 ad unit 값을 별도 선언해 관리한다.
  • 기존 구현 관례상 inline banner는 BannerAdView를 XML에 배치하고, 코드에서 실제 측정 너비 기준으로 BannerAdSize.inlineSize(...)를 설정한 뒤 loadAd(...)를 호출한다.
  • 기존 구현 관례상 배너 높이 상한은 XML android:maxHeight="90dp"와 코드의 maxAdHeightDp = 90 조합을 기본값으로 둔다.

조사 근거

  • 기존 inline banner 구현
    • app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt
    • app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt
    • app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/LiveRoomDetailFragment.kt
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/detail/AudioContentDetailActivity.kt
  • 기존 ad unit id 주입
    • app/build.gradle
  • 대상 화면
    • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt
    • app/src/main/res/layout/activity_creator_community_all.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/home/SeriesMainHomeFragment.kt
    • app/src/main/res/layout/fragment_series_main_home.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/day_of_week/SeriesMainDayOfWeekFragment.kt
    • app/src/main/res/layout/fragment_series_main_day_of_week.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/by_genre/SeriesMainByGenreFragment.kt
    • app/src/main/res/layout/fragment_series_main_by_genre.xml
    • app/src/main/java/kr/co/vividnext/sodalive/home/pushnotification/PushNotificationListActivity.kt
    • app/src/main/res/layout/activity_push_notification_list.xml
    • app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationReceiveSettingsActivity.kt
    • app/src/main/res/layout/activity_notification_receive_settings.xml
  • 공식 문서
    • https://ads.yandex.com/helpcenter/ko/dev/android/adaptive-inline-banner

구현 계획

1. AD_UNIT_ID 운영 기준 확정

  • 수정 후보: app/build.gradle
  • 계획:
    • 기존 YANDEX_INLINE_BANNER_* buildConfigField 선언 구조를 그대로 따른다.
    • 운영에서 이번 6개 지면을 기존 inline banner와 같은 지면으로 묶을 수 있으면, 기존 inline banner용 AD_UNIT_ID를 재사용한다.
    • 지면별 리포트 분리나 운영 분리가 필요하면 화면 또는 위치 단위로 신규 YANDEX_INLINE_BANNER_* 필드를 추가한다.
  • 판단 기준:
    • 재사용 가능: 같은 포맷, 같은 운영 정책, 같은 성과 집계 단위로 봐도 되는 경우
    • 신규 생성 필요: 화면 성격이 다르거나 성과/노출 정책을 따로 봐야 하는 경우
  • 비고:
    • 구현 코드는 BuildConfig 참조로 통일해 재사용/신규 생성 어느 쪽이든 코드 수정 범위를 최소화한다.

2. 크리에이터 커뮤니티 전체보기 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/activity_creator_community_all.xml
    • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt
  • 위치:
    • 탭 영역 및 1dp 구분선 아래, FrameLayout
  • 계획:
    • 현재는 탭 영역 아래 바로 콘텐츠 FrameLayout이 시작되므로, 그 사이에 BannerAdView를 배치한다.
    • list/grid 두 모드를 공통으로 덮는 구조이므로, 배너는 RecyclerView 내부 아이템이 아니라 상단 고정 섹션으로 넣는다.
    • Activity에서 binding.yandexInlineBannerView.post { ... } 패턴으로 배너 너비 측정 후 광고를 로드한다.
    • onDestroy()에서 destroy()를 호출해 리소스를 정리한다.

3. 시리즈 메인 홈 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/fragment_series_main_home.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/home/SeriesMainHomeFragment.kt
  • 위치:
    • ll_completed_series 아래, ll_recommend_series
  • 계획:
    • NestedScrollView 내부 세로 섹션 사이에 별도 배너 컨테이너를 추가한다.
    • 완결 시리즈/추천 시리즈 visibility와 독립적으로 배너 노출 조건을 설계한다.
    • 기존 setupCompletedSeriesView()/setupRecommendSeriesView()와 분리된 setupInlineBanner() 성격의 로직을 추가한다.
    • onDestroyView()에서 destroy()를 호출한다.

4. 시리즈 메인 요일별 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/fragment_series_main_day_of_week.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/day_of_week/SeriesMainDayOfWeekFragment.kt
  • 위치:
    • rv_series_day_of_week_day 아래, rv_series_day_of_week
  • 계획:
    • 상단 요일 필터와 하단 시리즈 그리드 사이의 중간 섹션으로 BannerAdView를 배치한다.
    • match_parent 높이로 쓰이는 하단 RecyclerView 영역을 깨지 않도록 배너 추가 후 레이아웃 weight/height 영향을 먼저 점검한다.
    • Fragment에서 기존 inline banner 로드 패턴을 복사하되, 화면 너비와 90dp 상한을 동일하게 적용한다.
    • onDestroyView()에서 배너 리소스를 해제한다.

5. 시리즈 메인 장르별 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/fragment_series_main_by_genre.xml
    • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/by_genre/SeriesMainByGenreFragment.kt
  • 위치:
    • rv_genre 아래, rv_series_by_genre
  • 계획:
    • 요일별 화면과 같은 구조이므로, 동일한 배치/로드/해제 패턴을 우선 재사용한다.
    • 장르 변경 시 시리즈 리스트만 다시 로드되도록 두고, 배너는 화면 생성 시 1회 로드 구조를 우선 검토한다.
    • onDestroyView()에서 destroy()를 호출한다.

6. 알림 리스트 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/activity_push_notification_list.xml
    • app/src/main/java/kr/co/vividnext/sodalive/home/pushnotification/PushNotificationListActivity.kt
  • 위치:
    • rv_category 아래, 알림 영역 시작 지점
  • 계획:
    • 빈 상태(ll_empty)와 알림 리스트(rv_notification)가 같은 FrameLayout을 공유하므로, 배너를 FrameLayout 바깥 상단 섹션으로 두는 안을 우선 적용한다.
    • 이렇게 하면 카테고리 아래에 항상 동일 위치로 배너를 유지하면서, 빈 상태/리스트 상태 전환에 영향을 덜 준다.
    • Activity에서 기존 inline banner 로드 패턴을 추가하고, 종료 시 destroy()를 호출한다.

7. 알림 수신 설정 배너 계획

  • 수정 대상:
    • app/src/main/res/layout/activity_notification_receive_settings.xml
    • app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationReceiveSettingsActivity.kt
  • 위치:
    • 서비스 알림 카드 영역 아래, 팔로잉 채널 섹션 제목 위
  • 계획:
    • NestedScrollView 내부 세로 흐름을 유지하면서 두 섹션 사이에 BannerAdView를 넣는다.
    • 서비스 알림 토글과 팔로잉 채널 리스트 스크롤/페이징 로직에 영향이 없도록, 배너는 독립 섹션으로 추가한다.
    • Activity 종료 시 destroy()를 호출한다.

8. 공통 배너 로드/정리 규칙

  • 공통 코드 방향:
    • XML에 BannerAdView를 직접 선언한다.
    • post {} 이후 width 기반 adWidthDp를 계산한다.
    • BannerAdSize.inlineSize(context, adWidthDp, 90)를 사용한다.
    • setAdUnitId(BuildConfig.YANDEX_INLINE_BANNER_...)loadAd(AdRequest.Builder().build())를 호출한다.
  • 생명주기:
    • Fragment는 onDestroyView()에서 destroy()
    • Activity는 onDestroy()에서 destroy()
  • 문서상 확인 포인트:
    • 배너가 섹션 사이에 위치하는지
    • 기존 스크롤/무한 로드 동작을 깨지 않는지
    • list/grid, empty/content, section visibility 변화와 충돌하지 않는지

예상 수정 파일

  • docs/20260424_커뮤니티시리즈알림Yandex배너광고추가계획.md
  • app/build.gradle (AD_UNIT_ID 재사용 불가 시에만)
  • app/src/main/res/layout/activity_creator_community_all.xml
  • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt
  • app/src/main/res/layout/fragment_series_main_home.xml
  • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/home/SeriesMainHomeFragment.kt
  • app/src/main/res/layout/fragment_series_main_day_of_week.xml
  • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/day_of_week/SeriesMainDayOfWeekFragment.kt
  • app/src/main/res/layout/fragment_series_main_by_genre.xml
  • app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/main/by_genre/SeriesMainByGenreFragment.kt
  • app/src/main/res/layout/activity_push_notification_list.xml
  • app/src/main/java/kr/co/vividnext/sodalive/home/pushnotification/PushNotificationListActivity.kt
  • app/src/main/res/layout/activity_notification_receive_settings.xml
  • app/src/main/java/kr/co/vividnext/sodalive/settings/notification/NotificationReceiveSettingsActivity.kt

검증 계획

  • 정적 확인
    • 변경 대상 .kt, .xml에 대해 가능 범위에서 lsp_diagnostics를 시도한다.
  • 빌드/테스트
    • ./gradlew :app:assembleDebug
    • ./gradlew :app:testDebugUnitTest
  • 수동 확인
    • 커뮤니티 전체보기에서 탭과 콘텐츠 리스트 사이에 배너가 노출되는지 확인한다.
    • 시리즈 메인 홈에서 완결 시리즈와 추천 시리즈 사이에 배너가 노출되는지 확인한다.
    • 시리즈 메인 요일별/장르별에서 필터 리스트와 시리즈 리스트 사이에 배너가 노출되는지 확인한다.
    • 알림 리스트에서 카테고리 아래, 알림 영역 시작 위치에 배너가 노출되는지 확인한다.
    • 알림 수신 설정에서 서비스 알림과 팔로잉 채널 사이에 배너가 노출되는지 확인한다.
    • 각 화면 종료 후 재진입 시 크래시나 중복 로드 문제가 없는지 확인한다.

검증 기록

  • 2026-04-24
    • 무엇: 커뮤니티/시리즈/알림 화면의 Yandex inline banner 추가 계획 문서를 생성했다.
    • 왜: 저장소 규칙에 따라 구현 전에 docs 아래 계획 문서를 먼저 만들고, 범위·삽입 위치·검증 기준을 문서로 고정해야 하기 때문이다.
    • 어떻게:
      • 생성 파일: docs/20260424_커뮤니티시리즈알림Yandex배너광고추가계획.md
      • 근거 파일: activity_creator_community_all.xml, fragment_series_main_home.xml, fragment_series_main_day_of_week.xml, fragment_series_main_by_genre.xml, activity_push_notification_list.xml, activity_notification_receive_settings.xml, 각 대응 Activity/Fragment Kotlin 파일
      • 재사용 근거: MyPageFragment.kt, LiveFragment.kt, LiveRoomDetailFragment.kt, AudioContentDetailActivity.kt, app/build.gradle
      • 근거 문서: https://ads.yandex.com/helpcenter/ko/dev/android/adaptive-inline-banner
      • 결과: 대상 6개 화면의 삽입 위치, 생명주기 정리 규칙, AD_UNIT_ID 재사용/신규 생성 기준, 예상 수정 파일, 검증 계획을 구현 전에 확정했다.
  • 2026-04-24
    • 무엇: 계획 대상 6개 화면에 Yandex adaptive inline banner 배치, 로드, 생명주기 정리를 반영했다.
    • 왜: 각 화면의 계획된 섹션 사이에 inline banner를 노출하고, 기존 구현과 동일하게 화면 종료 시 광고 리소스를 해제해야 하기 때문이다.
    • 어떻게:
      • 수정 파일: activity_creator_community_all.xml, CreatorCommunityAllActivity.kt, fragment_series_main_home.xml, SeriesMainHomeFragment.kt, fragment_series_main_day_of_week.xml, SeriesMainDayOfWeekFragment.kt, fragment_series_main_by_genre.xml, SeriesMainByGenreFragment.kt, activity_push_notification_list.xml, PushNotificationListActivity.kt, activity_notification_receive_settings.xml, NotificationReceiveSettingsActivity.kt
      • AD_UNIT_ID: 신규 buildConfigField를 만들지 않고 기존 BuildConfig.YANDEX_INLINE_BANNER_MYPAGE_AD_UNIT_ID를 6개 지면에 일관 재사용했다.
      • 근거: 신규 운영 ID가 별도로 제공되지 않았고, 이번 변경은 동일 포맷의 inline banner 추가이므로 문서의 재사용 우선 기준에 부합한다.
      • 생명주기: Activity는 onDestroy(), Fragment는 onDestroyView()에서 BannerAdView.destroy()를 호출하도록 반영했다.
  • 2026-04-24
    • 무엇: 구현 후 정적 확인, 빌드, 단위 테스트를 실행했다.
    • 왜: XML BannerAdView 추가, ViewBinding 생성, Kotlin import/컴파일, Fragment/Activity 생명주기 코드가 기존 동작을 깨지 않는지 확인해야 하기 때문이다.
    • 어떻게:
      • lsp_diagnostics: 환경에 .kt/.xml LSP 서버가 구성되어 있지 않아 실행 불가(No LSP server configured for extension)를 확인했다.
      • ./gradlew :app:assembleDebug: 성공. XML 리소스, ViewBinding, Kotlin 컴파일이 통과했다.
      • ./gradlew :app:testDebugUnitTest: 성공. Debug 단위 테스트가 통과했다.
      • ./gradlew :app:ktlintCheck: 실패. 대상 파일의 미사용 import 1건은 제거했으며, 남은 실패는 기존 패키지 경로의 underscore 규칙(audio_content/series/main/by_genre) 위반이다.
  • 2026-04-24
    • 무엇: 신규 6개 배너 지면의 ad unit 참조를 MyPage 공용 값 재사용에서 지면별 BuildConfig 필드 분리 구조로 변경했다.
    • 왜: 동일 포맷이라도 운영상 화면별 리포팅과 교체 가능성을 유지하는 편이 더 안전하고, 기존 저장소도 지면별 Yandex 광고 필드를 분리해 관리하고 있기 때문이다.
    • 어떻게:
      • 수정 파일: app/build.gradle, CreatorCommunityAllActivity.kt, SeriesMainHomeFragment.kt, SeriesMainDayOfWeekFragment.kt, SeriesMainByGenreFragment.kt, PushNotificationListActivity.kt, NotificationReceiveSettingsActivity.kt
      • build.gradle: release/debug 각각에 6개 신규 YANDEX_INLINE_BANNER_*_AD_UNIT_ID 필드를 추가했다.
      • 값 분리: release는 R-M-19140295-7R-M-19140295-12, debug는 R-M-19140297-7R-M-19140297-12로 각 지면 ad unit 값을 분리했다.
      • 코드 반영: 각 화면이 자신의 지면용 BuildConfig 필드를 참조하도록 변경해 이후 실제 값 교체 시 코드 수정이 필요 없게 정리했다.
  • 2026-04-24
    • 무엇: ad unit 분리 구조 반영 후 빌드와 테스트를 다시 검증했다.
    • 왜: buildConfigField 추가와 BuildConfig 참조 교체가 실제 컴파일과 단위 테스트를 깨지 않는지 새 결과로 확인해야 하기 때문이다.
    • 어떻게:
      • 실행 명령: ./gradlew :app:assembleDebug
      • 실행 결과: BUILD SUCCESSFUL
      • 실행 명령: ./gradlew :app:testDebugUnitTest
      • 실행 결과: BUILD SUCCESSFUL
      • 실행 명령: ./gradlew :app:ktlintCheck
      • 실행 결과: 실패
      • 실패 원인: 이번 변경으로 추가된 스타일 오류가 아니라 기존 패키지 경로의 underscore 규칙 위반(creator_community, audio_content/series/main/home, audio_content/series/main/day_of_week, audio_content/series/main/by_genre)이 계속 보고되었다.