배너 광고는 화면의 일부 영역에 표시되는 직사각형 광고입니다. XML 레이아웃과 코드 모두에서 사용할 수 있습니다.
주요 특징
- 화면 상단, 하단 또는 중간에 고정 배치 가능
- 이미지 및 동영상 광고 지원
- XML 레이아웃 및 프로그래밍 방식 모두 지원
- 리스너를 통한 광고 이벤트 처리
개발 환경에서는 테스트 유닛 ID를 사용하세요: PUBLIC_TEST_UNIT_ID_320_100
XML 레이아웃 구현
XML 레이아웃에서 AdropBanner를 정의하고 Activity나 Fragment에서 로드하는 방식입니다.
1. XML 레이아웃 정의
FrameLayout 또는 다른 컨테이너에 AdropBanner를 배치합니다.
<?xml version="1.0" encoding="utf-8"?>
<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">
<!-- 메인 콘텐츠 -->
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메인 콘텐츠"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 배너 광고 컨테이너 -->
<FrameLayout
android:id="@+id/banner_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<io.adrop.ads.banner.AdropBanner
android:id="@+id/adrop_banner"
android:layout_width="320dp"
android:layout_height="100dp"
android:layout_gravity="center"
app:adrop_unit_id="YOUR_UNIT_ID" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
2. Activity에서 로드
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.adrop.ads.banner.AdropBanner
import io.adrop.ads.banner.AdropBannerListener
import io.adrop.ads.model.AdropErrorCode
class MainActivity : AppCompatActivity() {
private var banner: AdropBanner? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. XML에서 정의한 배너 뷰 찾기
banner = findViewById(R.id.adrop_banner)
// 2. 리스너 설정
banner?.listener = object : AdropBannerListener {
override fun onAdReceived(banner: AdropBanner) {
println("배너 광고 수신 성공")
}
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
println("배너 광고 수신 실패: $errorCode")
}
override fun onAdImpression(banner: AdropBanner) {
println("배너 광고 노출")
}
override fun onAdClicked(banner: AdropBanner) {
println("배너 광고 클릭")
}
}
// 3. 광고 로드
banner?.load()
}
override fun onDestroy() {
super.onDestroy()
// 4. 메모리 해제
banner?.destroy()
banner = null
}
}
XML 속성
| 속성 | 설명 | 필수 |
|---|
app:adrop_unit_id | 광고 유닛 ID | O |
app:adrop_context_id | 문맥 타겟팅을 위한 Context ID | |
android:layout_width | 배너 너비 (dp) | O |
android:layout_height | 배너 높이 (dp) | O |
프로그래밍 방식 구현
코드에서 직접 AdropBanner 인스턴스를 생성하여 뷰에 추가하는 방식입니다.
import android.os.Bundle
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.adrop.ads.banner.AdropBanner
import io.adrop.ads.banner.AdropBannerListener
import io.adrop.ads.model.AdropErrorCode
class MainActivity : AppCompatActivity() {
private var banner: AdropBanner? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 배너 인스턴스 생성
banner = AdropBanner(this, "YOUR_UNIT_ID").apply {
// 2. 리스너 설정
listener = object : AdropBannerListener {
override fun onAdReceived(banner: AdropBanner) {
println("배너 광고 수신 성공")
}
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
println("배너 광고 수신 실패: $errorCode")
}
override fun onAdImpression(banner: AdropBanner) {
println("배너 광고 노출")
}
override fun onAdClicked(banner: AdropBanner) {
println("배너 광고 클릭")
}
}
}
// 3. 뷰 계층에 추가
val container = findViewById<FrameLayout>(R.id.banner_container)
val params = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
)
container.addView(banner, params)
// 4. 광고 로드
banner?.load()
}
override fun onDestroy() {
super.onDestroy()
// 5. 메모리 해제
banner?.destroy()
banner = null
}
}
AdropBanner 초기화
생성자
Activity 또는 Application Context
val banner = AdropBanner(context, "YOUR_UNIT_ID")
유닛 ID 설정
banner.setUnitId("YOUR_UNIT_ID")
광고 로드
배너를 화면에 추가한 후 load() 메서드를 호출하여 광고를 요청합니다.
배너가 화면에 보이는 시점에 load()를 호출하세요. 화면에 보이지 않는 상태에서 로드하면 노출이 정확하게 측정되지 않을 수 있습니다.
리스너 구현
AdropBannerListener 인터페이스는 광고의 생명주기 이벤트를 처리합니다.
onAdReceived (필수)
광고 수신이 성공했을 때 호출됩니다.
override fun onAdReceived(banner: AdropBanner) {
println("배너 광고 수신 성공")
// 광고 로딩 인디케이터 숨김 등의 처리
}
onAdFailedToReceive (필수)
광고 수신이 실패했을 때 호출됩니다.
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
println("배너 광고 수신 실패: $errorCode")
// 에러 처리 및 대체 콘텐츠 표시
}
에러 유형을 나타내는 코드. 자세한 내용은 레퍼런스를 참고하세요.
onAdImpression (선택)
광고가 화면에 노출되었을 때 호출됩니다.
override fun onAdImpression(banner: AdropBanner) {
println("배너 광고 노출")
// 노출 분석 로깅 등의 처리
}
onAdClicked (필수)
사용자가 광고를 클릭했을 때 호출됩니다.
override fun onAdClicked(banner: AdropBanner) {
println("배너 광고 클릭")
// 클릭 분석 로깅 등의 처리
}
Context ID 설정
문맥 타겟팅을 위해 Context ID를 설정할 수 있습니다.
// contextId는 읽기 전용 — 생성자에서 설정
val banner = AdropBanner(this, "YOUR_UNIT_ID", "article_123")
Context ID는 광고를 로드하기 전에 설정해야 합니다.
생명주기 관리
destroy()
배너를 더 이상 사용하지 않을 때는 반드시 destroy()를 호출하여 리소스를 해제해야 합니다.
override fun onDestroy() {
super.onDestroy()
banner?.destroy()
banner = null
}
destroy()를 호출하지 않으면 메모리 누수가 발생할 수 있습니다.
광고 크기
배너 광고는 유닛에 설정한 크기에 맞춰 뷰의 크기를 지정해야 합니다.
일반적인 배너 크기
| 크기 | 용도 |
|---|
| 320 x 50 | 소형 배너 |
| 320 x 100 | 중형 배너 |
| 16:9 비율 | 동영상 배너 |
XML에서 크기 지정
<io.adrop.ads.banner.AdropBanner
android:id="@+id/adrop_banner"
android:layout_width="320dp"
android:layout_height="100dp"
app:adrop_unit_id="YOUR_UNIT_ID" />
코드에서 크기 지정
val params = FrameLayout.LayoutParams(
dpToPx(320), // width
dpToPx(100) // height
)
container.addView(banner, params)
// dp를 px로 변환하는 유틸리티 함수
fun dpToPx(dp: Int): Int {
val density = resources.displayMetrics.density
return (dp * density).toInt()
}
테스트 유닛 ID
개발 및 테스트 시 아래의 테스트 유닛 ID를 사용하세요.
| 광고 유형 | 테스트 유닛 ID | 크기 |
|---|
| 배너 (320x50) | PUBLIC_TEST_UNIT_ID_320_50 | 320 x 50 |
| 배너 (320x100) | PUBLIC_TEST_UNIT_ID_320_100 | 320 x 100 |
| 캐러셀 배너 | PUBLIC_TEST_UNIT_ID_CAROUSEL | 가변 |
| 배너 비디오 (16:9) | PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_16_9 | 16:9 비율 |
| 배너 비디오 (9:16) | PUBLIC_TEST_UNIT_ID_BANNER_VIDEO_9_16 | 9:16 비율 |
사용 예시
val banner = AdropBanner(this, "PUBLIC_TEST_UNIT_ID_320_100")
동영상 재생 제어
동영상 배너 광고의 경우, play()와 pause()를 사용하여 비디오 재생을 수동으로 제어할 수 있습니다.
play()
비디오를 재생하거나 재개합니다. 이 메서드는 WebKit 오디오 파이프라인을 복원하기 위해 비디오를 강제 재시작하므로, 다른 앱이나 백그라운드에서 복귀한 후 소리가 나지 않는 문제를 해결할 때 유용합니다.
pause()
현재 재생 중인 비디오를 일시 정지합니다.
예시: 백그라운드/포그라운드 처리
override fun onResume() {
super.onResume()
banner?.play()
}
override fun onPause() {
super.onPause()
banner?.pause()
}
play()와 pause()는 동영상 배너 광고에만 영향을 미칩니다. 이미지 배너에는 효과가 없습니다.
커스텀 클릭 처리
기본적으로 SDK는 사용자가 배너 광고를 클릭하면 자동으로 도착 URL을 엽니다. 클릭 이벤트를 직접 처리하고 싶다면 (예: 확인 다이얼로그 표시, 분석 로깅 후 이동 등) useCustomClick을 사용할 수 있습니다.
true로 설정하면 SDK가 클릭 시 자동으로 도착 URL을 열지 않습니다. onAdClicked 이벤트만 발생하며, 직접 open()을 호출해야 합니다.
// 1. 커스텀 클릭 처리 활성화
banner?.useCustomClick = true
// 2. 리스너에서 클릭 처리
banner?.listener = object : AdropBannerListener {
override fun onAdClicked(banner: AdropBanner) {
// 커스텀 로직 수행 (다이얼로그 표시, 분석 로깅 등)
println("광고 클릭됨 — 도착 URL 열기")
banner.open()
}
// ... 기타 리스너 메서드
}
open()
광고의 도착 URL을 엽니다. 커스텀 URL을 전달하여 다른 URL을 열 수도 있습니다.
// 기본 도착 URL 열기
banner?.open()
// 커스텀 URL 열기
banner?.open("https://example.com")
open()은 useCustomClick이 true로 설정된 경우에만 동작합니다. useCustomClick이 false(기본값)이면 SDK가 자동으로 URL을 엽니다.
모범 사례
1. 메모리 관리
Activity나 Fragment가 종료될 때 배너를 반드시 해제하세요.
override fun onDestroy() {
super.onDestroy()
banner?.destroy()
banner = null
}
2. 화면 가시성
배너가 화면에 보일 때 광고를 로드하세요.
override fun onResume() {
super.onResume()
banner?.load()
}
3. 에러 처리
광고 로드 실패 시 적절한 에러 처리를 구현하세요.
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
when (errorCode) {
AdropErrorCode.ERROR_CODE_NETWORK -> {
println("네트워크 오류: 연결을 확인하세요")
}
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
println("노출 가능한 광고 없음")
}
else -> {
println("광고 로드 실패: $errorCode")
}
}
}
4. 리사이클러뷰에서 사용
리사이클러뷰에서 배너를 사용할 때는 뷰홀더에서 적절히 관리하세요.
class BannerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val banner: AdropBanner = view.findViewById(R.id.adrop_banner)
fun bind() {
banner.listener = object : AdropBannerListener {
// 리스너 구현
}
banner.load()
}
fun recycle() {
banner.destroy()
}
}
// 어댑터에서
override fun onViewRecycled(holder: BannerViewHolder) {
holder.recycle()
}
전체 예제
Kotlin 예제
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import io.adrop.ads.banner.AdropBanner
import io.adrop.ads.banner.AdropBannerListener
import io.adrop.ads.model.AdropErrorCode
class BannerActivity : AppCompatActivity() {
private var banner: AdropBanner? = null
private lateinit var progressBar: ProgressBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_banner)
progressBar = findViewById(R.id.progress_bar)
setupBanner()
}
private fun setupBanner() {
// 1. 배너 인스턴스 생성
// contextId는 읽기 전용 프로퍼티 — 생성자에서 설정
banner = AdropBanner(this, "YOUR_UNIT_ID", "article_123").apply {
// 2. 리스너 설정
listener = object : AdropBannerListener {
override fun onAdReceived(banner: AdropBanner) {
println("배너 광고 수신 성공")
progressBar.visibility = View.GONE
}
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
println("배너 광고 수신 실패: $errorCode")
progressBar.visibility = View.GONE
// 에러에 따른 처리
when (errorCode) {
AdropErrorCode.ERROR_CODE_NETWORK -> {
showToast("네트워크 연결을 확인하세요")
}
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
println("노출 가능한 광고가 없습니다")
}
else -> {
showToast("광고를 불러올 수 없습니다")
}
}
}
override fun onAdImpression(banner: AdropBanner) {
println("배너 광고 노출")
}
override fun onAdClicked(banner: AdropBanner) {
println("배너 광고 클릭")
}
}
}
// 3. 뷰 계층에 추가
val container = findViewById<FrameLayout>(R.id.banner_container)
val params = FrameLayout.LayoutParams(
dpToPx(320),
dpToPx(100)
)
container.addView(banner, params)
// 4. 광고 로드
progressBar.visibility = View.VISIBLE
banner?.load()
}
private fun dpToPx(dp: Int): Int {
val density = resources.displayMetrics.density
return (dp * density).toInt()
}
private fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
// 5. 메모리 해제
banner?.destroy()
banner = null
}
}
Java 예제
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import io.adrop.ads.banner.AdropBanner;
import io.adrop.ads.banner.AdropBannerListener;
import io.adrop.ads.model.AdropErrorCode;
import org.jetbrains.annotations.NotNull;
public class BannerActivity extends AppCompatActivity {
private AdropBanner banner;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_banner);
progressBar = findViewById(R.id.progress_bar);
setupBanner();
}
private void setupBanner() {
// 1. 배너 인스턴스 생성 (Context ID 포함)
banner = new AdropBanner(this, "YOUR_UNIT_ID", "article_123");
// 2. 리스너 설정
banner.setListener(new AdropBannerListener() {
@Override
public void onAdReceived(@NotNull AdropBanner banner) {
System.out.println("배너 광고 수신 성공");
progressBar.setVisibility(View.GONE);
}
@Override
public void onAdFailedToReceive(@NotNull AdropBanner banner, @NotNull AdropErrorCode errorCode) {
System.out.println("배너 광고 수신 실패: " + errorCode);
progressBar.setVisibility(View.GONE);
// 에러에 따른 처리
if (errorCode == AdropErrorCode.ERROR_CODE_NETWORK) {
showToast("네트워크 연결을 확인하세요");
} else if (errorCode == AdropErrorCode.ERROR_CODE_AD_NO_FILL) {
System.out.println("노출 가능한 광고가 없습니다");
} else {
showToast("광고를 불러올 수 없습니다");
}
}
@Override
public void onAdImpression(@NotNull AdropBanner banner) {
System.out.println("배너 광고 노출");
}
@Override
public void onAdClicked(@NotNull AdropBanner banner) {
System.out.println("배너 광고 클릭");
}
});
// 3. 뷰 계층에 추가
FrameLayout container = findViewById(R.id.banner_container);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
dpToPx(320),
dpToPx(100)
);
container.addView(banner, params);
// 4. 광고 로드
progressBar.setVisibility(View.VISIBLE);
banner.load();
}
private int dpToPx(int dp) {
float density = getResources().getDisplayMetrics().density;
return (int) (dp * density);
}
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 5. 메모리 해제
if (banner != null) {
banner.destroy();
banner = null;
}
}
}
XML 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<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">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="배너 광고 예제"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginTop="32dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/banner_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
백필 광고
백필 광고가 활성화된 경우, 직광고가 없을 때 자동으로 백필 광고가 로드됩니다. isBackfilled 프로퍼티로 백필 광고 여부를 확인할 수 있습니다.
banner.listener = object : AdropBannerListener {
override fun onAdReceived(banner: AdropBanner) {
if (banner.isBackfilled) {
println("백필 광고가 로드되었습니다")
} else {
println("직광고가 로드되었습니다")
}
}
override fun onAdFailedToReceive(banner: AdropBanner, errorCode: AdropErrorCode) {
when (errorCode) {
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
println("직광고 없음, 백필 광고 요청 중...")
}
AdropErrorCode.ERROR_CODE_AD_BACKFILL_NO_FILL -> {
println("백필 광고도 없음")
}
else -> {
println("광고 로드 실패: $errorCode")
}
}
}
}
백필 광고를 사용하려면 io.adrop:adrop-ads-backfill 의존성을 추가해야 합니다.
다음 단계
네이티브 광고
UI에 맞게 커스터마이징 가능한 네이티브 광고 구현하기
전면 광고
화면 전체를 덮는 전면 광고 구현하기
타겟팅 설정
사용자 속성 및 문맥 타겟팅 설정하기