feat(mypage): 최하단에 배너 광고 추가

This commit is contained in:
2026-04-20 15:25:38 +09:00
parent 8271f117a4
commit 68e8941cc1
3 changed files with 131 additions and 0 deletions

View File

@@ -15,6 +15,15 @@ import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.google.gson.Gson import com.google.gson.Gson
import com.orhanobut.logger.Logger
import droom.daro.core.adunit.DaroBannerAdUnit
import droom.daro.core.model.DaroAdInfo
import droom.daro.core.model.DaroAdLoadError
import droom.daro.core.model.DaroBannerSize
import droom.daro.core.model.DaroViewAd
import droom.daro.view.DaroAdViewListener
import droom.daro.view.DaroBannerAdView
import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
@@ -57,6 +66,9 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
companion object { companion object {
private const val FUNCTION_BUTTON_SPAN_COUNT = 4 private const val FUNCTION_BUTTON_SPAN_COUNT = 4
private const val DARO_BANNER_AD_UNIT_KEY = "43df2529-31d8-45f8-a17d-1a760f5bc777"
private const val DARO_BANNER_PLACEMENT = "MyPage"
} }
private val viewModel: MyPageViewModel by inject() private val viewModel: MyPageViewModel by inject()
@@ -64,6 +76,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private val functionButtonAdapter = FunctionButtonAdapter() private val functionButtonAdapter = FunctionButtonAdapter()
private var daroBannerAdView: DaroBannerAdView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -73,6 +86,71 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
bindData() bindData()
setupRecentContentSection() setupRecentContentSection()
setupLatestNotice() setupLatestNotice()
setupDaroBottomBanner()
}
override fun onDestroyView() {
daroBannerAdView?.destroy()
daroBannerAdView = null
binding.flDaroBannerContainer.removeAllViews()
super.onDestroyView()
}
private fun setupDaroBottomBanner() {
binding.flDaroBannerContainer.visibility = View.GONE
binding.flDaroBannerContainer.removeAllViews()
val adUnit = DaroBannerAdUnit(
key = DARO_BANNER_AD_UNIT_KEY,
placement = DARO_BANNER_PLACEMENT,
bannerSize = DaroBannerSize.Banner
)
val adView = DaroBannerAdView(
context = requireContext(),
adUnit = adUnit
).apply {
setListener(object : DaroAdViewListener {
override fun onAdImpression(adInfo: DaroAdInfo) = Unit
override fun onAdClicked(adInfo: DaroAdInfo) = Unit
override fun onAdLoadSuccess(ad: DaroViewAd, adInfo: DaroAdInfo) {
binding.flDaroBannerContainer.visibility = View.VISIBLE
}
override fun onAdLoadFail(err: DaroAdLoadError) {
handleDaroBannerLoadFail(err)
}
})
}
daroBannerAdView = adView
binding.flDaroBannerContainer.addView(adView)
adView.loadAd()
}
private fun handleDaroBannerLoadFail(err: DaroAdLoadError) {
binding.flDaroBannerContainer.visibility = View.GONE
Logger.w(
"Daro banner load failed. package=${BuildConfig.APPLICATION_ID}, placement=$DARO_BANNER_PLACEMENT"
)
Logger.w(
"Daro banner load failed. code=${err.code}, message=${err.message}"
)
if (err.message.contains("no fill", ignoreCase = true)) {
Logger.w(
"Daro no fill. Verify app-ads.txt, Live status, registered package=${BuildConfig.APPLICATION_ID}"
)
if (BuildConfig.DEBUG && BuildConfig.APPLICATION_ID.endsWith(".debug")) {
Logger.w(
"Debug package differs from release. Register ${BuildConfig.APPLICATION_ID} in Daro or test release package."
)
}
}
} }
private fun setupLatestNotice() { private fun setupLatestNotice() {

View File

@@ -346,6 +346,15 @@
android:clipToPadding="false" android:clipToPadding="false"
android:paddingHorizontal="24dp" /> android:paddingHorizontal="24dp" />
</LinearLayout> </LinearLayout>
<!-- Daro bottom banner container -->
<FrameLayout
android:id="@+id/fl_daro_banner_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="32dp"
android:visibility="gone" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,44 @@
# 20260420 마이페이지 배너 광고 추가
## 작업 체크리스트
- [x] Daro 공식 Android 배너 가이드와 현재 프로젝트의 광고 기본 세팅 상태를 확인한다.
QA: 배너 뷰 타입, 필수 값, 기존 SDK 초기화/플러그인 상태를 근거 파일로 설명할 수 있어야 한다.
- [x] `fragment_my.xml` 최하단에 배너 광고 영역을 추가한다.
QA: 기존 MyPage 콘텐츠 하단에 배너 광고 뷰가 배치되고, 화면 레이아웃을 깨지 않아야 한다.
- [x] `MyPageFragment.kt`에 배너 광고 로드/정리 로직과 임시 설정값 위치 안내를 추가한다.
QA: 사용자가 교체해야 하는 값이 코드에서 명확히 드러나고, Fragment 생명주기에 맞는 정리 처리가 있어야 한다.
- [x] 변경 사항을 진단하고 필요한 Gradle 검증을 수행한 뒤 결과를 기록한다.
QA: 변경 파일 진단 결과와 실행한 검증 명령/결과가 문서 하단에 누적 기록되어야 한다.
## 임시 설정값 위치
- `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`
- `DARO_BANNER_AD_UNIT_KEY`
- `DARO_BANNER_PLACEMENT`
- 위 두 상수를 실제 Daro 배너 값으로 교체하면 된다.
## 검증 기록
- 2026-04-20
- 무엇: `fragment_my.xml` 최하단에 Daro 배너 컨테이너를 추가하고, `MyPageFragment`에서 `DaroBannerAdView`를 생성해 로드/정리하도록 구현했다.
- 왜: 공식 Daro Android 배너 가이드가 XML 커스텀 태그 대신 컨테이너 뷰 + 코드 생성 방식의 `DaroBannerAdView` 사용을 요구했고, 요청 위치가 MyPage 최하단이었기 때문이다.
- 어떻게:
- 수정 파일: `app/src/main/res/layout/fragment_my.xml`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `docs/20260420_마이페이지배너광고추가.md`
- 임시 값 위치: `MyPageFragment.kt``DARO_BANNER_AD_UNIT_KEY`, `DARO_BANNER_PLACEMENT`
- 진단 도구: Kotlin/XML LSP 서버 미구성으로 `lsp_diagnostics`는 실행 불가였다.
- 실행 명령: `./gradlew :app:assembleDebug`
- 결과: 성공. Daro 플러그인 설정과 앱 debug 빌드가 모두 완료됐다.
- 실행 명령: `./gradlew :app:ktlintCheck`
- 결과: 실패. 이번 변경 파일이 아니라 기존 파일들(`NicknameUpdateViewModel.kt`, `ProfileUpdateActivity.kt`, `RecentContentDao.kt` 등)의 스타일 오류 때문에 `:app:ktlintMainSourceSetCheck`가 실패했다.
- 실행 명령: `adb devices`
- 결과: 연결된 기기가 없어 실제 화면 수동 확인은 이 환경에서 진행하지 못했다.
- 2026-04-20
- 무엇: MyPage Daro 배너의 `No fill` 원인을 추가 조사하고, 배너 로드 실패 시 진단 로그가 남도록 `MyPageFragment`를 보강했다.
- 왜: 공식 Daro 문서와 SDK 소스 기준으로 `No fill`은 코드 버그보다 `app-ads.txt`, 앱/광고 단위 Live 상태, 등록 패키지 불일치 같은 송출 조건 이슈 가능성이 더 높았고, 기존 코드는 실패를 조용히 숨기고 있었다.
- 어떻게:
- 수정 파일: `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `docs/20260420_마이페이지배너광고추가.md`
- 조사 근거: Daro 공식 FAQ/배너 가이드, SDK `DaroAdLoader.kt``no fill` 처리 메시지, debug merged manifest의 패키지 `kr.co.vividnext.sodalive.debug`, release merged manifest의 패키지 `kr.co.vividnext.sodalive`
- 확인 사항: 저장소 내 `app-ads.txt` 파일은 없었고, debug 빌드는 `applicationIdSuffix '.debug'`로 인해 release 패키지와 다른 앱 ID를 사용한다.
- 적용 내용: `No fill` 발생 시 현재 `APPLICATION_ID`, placement, 에러 코드/메시지와 함께 `app-ads.txt`, Live 상태, 등록 패키지 확인이 필요하다는 로그를 남기고, debug 패키지(`.debug`)면 별도 등록 또는 release 패키지 테스트 필요 로그를 추가했다.
- 실행 명령: `./gradlew :app:assembleDebug`
- 결과: 성공. 진단 로그 추가 후에도 debug 빌드는 정상 완료됐다.
- 실행 명령: `./gradlew :app:ktlintCheck`
- 결과: 성공. `MyPageFragment.kt`의 로그 문자열 줄 길이를 정리한 뒤 lint를 통과했다.