[Android] AdMob 배너 광고 레이아웃 깨짐 방지 및 가변 크기(Adaptive Banner) 적용 방법
앱을 운영하다 보면 AdMob 배너 광고를 붙여야 할 때가 많습니다. 저의 경우도 하단에 배너를 달아두고 운영 중인데, 최근 나오는 Android 기기들은 화면 크기나 비율이 워낙 제각각이라(폴더블, 태블릿, 가로 모드 등) 고정 크기 배너만 고집하면 레이아웃이 붕 뜨거나 어색하게 깨지는 문제가 발생하더군요.
자꾸 까먹기도 하고 실무에서 매번 찾아보기 귀찮아서, 안정적으로 배너 광고를 올리는 레이아웃 구조와 가변 배너(Anchored Adaptive Banner) 적용 방법을 정리해 둡니다.
1. 배너 광고 적용 시 자주 겪는 문제와 원인
단순히 "화면 아래에 View 하나 얹으면 되겠지" 하고 접근하면 운영하다가 꼭 아래와 같은 안 좋은 상황을 마주하게 됩니다.
레이아웃 튐 현상: 광고가 로딩되기 전에는 높이가 0이었다가, 광고가 로드되는 순간 갑자기 화면이 툭 밀려 내려가면서 사용자가 버튼을 잘못 누르는 오인 클릭이 발생합니다.
화면 크기 미대응: XML에
320x50같은 고정 크기만 넣어두면 태블릿이나 가로 모드에서 양옆이 훤히 비어 보입니다.메모리 누수: Fragment나 Activity가 종료될 때 리소스를 제대로 해제하지 않으면 메모리를 계속 잡아먹습니다.
정책 위반 위험: UMP(개인정보 동의) 흐름이 끝나기도 전에 광고를 요청하거나, 개발 중에 실제 광고 ID를 그대로 박아서 테스트하면 계정이 정지될 수 있습니다.
따라서 배너는 광고 컨테이너 높이, 화면 회전, 라이프사이클 관리를 처음부터 세트로 묶어서 설계해야 안전합니다.
2. 해결 방법: XML 레이아웃 구조 잡기
광고를 콘텐츠 위에 겹쳐 올리면 100% 버튼 오인 클릭으로 구글 경고를 받거나 사용자 원성을 삽니다. 아예 콘텐츠 영역과 광고 영역을 아래처럼 확실하게 분리해 주는 것이 좋습니다.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/contentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/adContainer"
app:layout_constraintStart_of="parent"
app:layout_end_of="parent" />
<FrameLayout
android:id="@+id/adContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_of="parent"
app:layout_end_of="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tip: 광고 컨테이너에
android:minHeight="50dp"정도로 최소 높이를 잡아두면, 광고가 늦게 뜨더라도 밑에 공간이 미리 확보되어 있어서 레이아웃이 위아래로 출렁거리는 현상을 막을 수 있습니다.
3. Kotlin 구현 코드 (Adaptive Banner 적용 및 해제)
공식 문서에서도 일반적인 상/하단 배너에는 화면 폭에 맞춰 높이가 결정되는 Anchored adaptive banner 방식을 권장합니다. 현재 디바이스의 가로 폭(dp)을 구해서 동적으로 광고 크기를 요청하는 컨트롤러 클래스 예시입니다.
class BannerAdController(
private val activity: Activity,
private val adContainer: FrameLayout,
private val adUnitId: String,
) {
private var adView: AdView? = null
fun load() {
// 현재 컨테이너 폭이나 디바이스 폭을 기준으로 dp 단위 가로 크기 계산
val widthPx = adContainer.width.takeIf { it > 0 }
?: activity.resources.displayMetrics.widthPixels
val density = activity.resources.displayMetrics.density
val adWidth = (widthPx / density).toInt()
val bannerView = AdView(activity).apply {
// 현재 방향과 가로 폭에 맞는 가변 배너 사이즈 설정
setAdSize(
AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
activity,
adWidth,
),
)
this.adUnitId = adUnitId
adListener = object : AdListener() {
override fun onAdFailedToLoad(error: LoadAdError) {
// 실무 운영 시 에러 코드 정도만 로그를 남기고, 광고 ID는 로그에 찍지 마세요.
}
}
}
// 기존 뷰 정리 후 교체
adContainer.removeAllViews()
adContainer.addView(bannerView)
adView = bannerView
// 광고 로드
bannerView.loadAd(AdRequest.Builder().build())
}
// Activity나 Fragment의 onDestroy에서 반드시 호출해줘야 메모리 누수가 안 납니다.
fun destroy() {
adContainer.removeAllViews()
adView?.destroy()
adView = null
}
}
배너 형식 선택 가이드
운영 중인 화면의 성격에 따라 배너 타입을 맞춰서 써야 뷰가 안 깨집니다.
| 배너 종류 | 설명 | 추천 상황 |
| Anchored adaptive | 화면 폭에 맞춰 높이가 자동 결정되는 고정 위치 배너 | 일반적인 상단 / 하단 고정 레이아웃 |
| Inline adaptive | 스크롤 영역 안에 들어가는 가변 높이 배너 | 피드 목록, 기사 본문 중간 삽입 |
| Fixed banner | 320x50 등 딱 정해진 dp 크기 사용 | 구형 UI 유지나 특수 목적 레이아웃 |
4. 실무 운영 시 핵심 체크리스트
코드를 잘 짜놓고도 삐끗하면 계정이 날아가거나 심사 거절을 당하기 십상입니다. 제 경험상 아래 네 가지는 무조건 배포 전에 확인하셔야 합니다.
① UMP 동의 흐름 직후 로드하기
유럽연합 기준(및 최신 정책)으로 UMP 동의 팝업 처리가 완전히 끝나고 광고 요청이 가능한 상태(ConsentStatus)가 되었을 때 배너 로드 함수를 호출해야 안전합니다. 동의를 안 받았거나 거부된 상태에서 무작정 로드 함수부터 돌리면 안 됩니다.
② 테스트 광고 ID 철저 분리
개발이나 테스트 기기에서 실제 운영 광고 ID를 호출해 버리면 무효 클릭 트래픽으로 계정이 정지될 수 있습니다. BuildConfig나 빌드 플래버를 나눠서 개발 빌드에는 무조건 AdMob 공식 테스트 ID를 쓰도록 격리해 두세요.
Android 배너 테스트 광고 단위 ID:
ca-app-pub-3940256099942544/6300978111주의: 실제 광고 ID나 테스트 디바이스 해시값은 GitHub 같은 공개 저장소에 소스코드로 커밋되지 않도록 마스킹 처리가 필수입니다.
③ 주요 조작 버튼과의 안전거리 확보
하단 내비게이션 바 바로 위에 배너를 바짝 붙여놓거나, '다음 단계' 버튼 바로 밑에 배너를 두면 오인 클릭 유도로 제재를 받습니다. 마진(Margin)을 주거나 경계선을 명확히 긋는 편이 좋습니다.
5. 마치며 (요약)
XML 레이아웃에서 콘텐츠 영역과 광고 컨테이너 영역을 겹치지 않게 완전히 분리하자.
AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize를 써서 기기 대응을 해주자.Activity/Fragment 생명주기에 맞춰
destroy()처리를 안 하면 메모리가 질질 샌다.실제 광고 ID 관리는 소스코드에 날것으로 노출하지 말고 빌드 환경별로 분리해서 주입하자.
배너 광고의 핵심은 많이 노출해서 클릭을 유도하는 게 아니라, 사용자가 앱 서비스를 이용하는 흐름을 방해하지 않으면서 묵묵히 제자리에 붙어 있게 만드는 것입니다. 혹시 지금 고정 크기로 띄워둔 배너가 있다면 가변 배너 구조로 가볍게 변경해 보시는 걸 권장합니다.
댓글
댓글 쓰기