네이티브 광고는 앱의 디자인에 맞게 광고 UI를 자유롭게 커스터마이징할 수 있는 광고 포맷입니다. 광고 소재(이미지, 동영상), 제목, 설명, CTA 버튼 등의 요소를 개별적으로 배치하여 자연스러운 사용자 경험을 제공할 수 있습니다.
주요 기능
- 완전한 UI 커스터마이징: 앱의 디자인 시스템에 맞게 광고 레이아웃을 자유롭게 구성
- 다양한 미디어 지원: 이미지 및 동영상 광고 소재 지원
- 유연한 클릭 영역 설정: 전체 클릭 또는 개별 요소별 클릭 처리 가능
- 프로필 정보 표시: 광고주 프로필 로고, 이름, 링크 지원
클래스 및 인터페이스
AdropNativeAd
네이티브 광고를 로드하고 관리하는 메인 클래스입니다.
주요 프로퍼티
| 프로퍼티 | 타입 | 설명 |
|---|
headline | String | 광고 제목 |
body | String | 광고 본문 |
icon | String | 아이콘 이미지 URL |
cover | String | 커버 이미지 URL |
advertiser | String | Deprecated. profile.displayName을 대신 사용하세요. |
advertiserURL | String | Deprecated. profile.link를 대신 사용하세요. |
accountTag | JSONObject | Deprecated. 더 이상 지원되지 않습니다. |
creativeTag | JSONObject | Deprecated. 더 이상 지원되지 않습니다. |
callToAction | String | 행동 유도 문구 (예: “지금 확인하기”) |
profile | AdropNativeAdProfile | 광고주 프로필 정보 |
isLoaded | Boolean | 광고 로드 완료 여부 |
isDestroyed | Boolean | 광고 제거 여부 |
AdropNativeAdView
네이티브 광고를 표시할 컨테이너 뷰입니다.
전체 영역 클릭 활성화 여부. true로 설정하면 광고 뷰 전체 영역에서 클릭 이벤트가 발생합니다.
주요 메서드
| 메서드 | 설명 |
|---|
setIconView(view, listener) | 아이콘 이미지 뷰 설정 (ImageView 타입) |
setHeadLineView(view, listener) | 제목 텍스트 뷰 설정 (TextView 타입) |
setBodyView(view) | 본문 텍스트 뷰 설정 (TextView 타입) |
setMediaView(view) | 미디어 컨테이너 뷰 설정 (AdropMediaView 타입) |
setAdvertiserView(view, listener) | 광고주명 텍스트 뷰 설정 (TextView 타입) |
setCallToActionView(view) | CTA 버튼 또는 텍스트 뷰 설정 |
setProfileLogoView(view, listener) | 프로필 로고 이미지 뷰 설정 (ImageView 타입) |
setProfileNameView(view, listener) | 프로필 이름 텍스트 뷰 설정 (TextView 타입) |
setNativeAd(ad) | 광고 데이터를 뷰에 바인딩 |
destroy() | 리소스 해제 |
광고 이미지 또는 동영상을 표시할 미디어 컨테이너 뷰입니다.
<io.adrop.ads.nativeAd.AdropMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="200dp" />
AdropNativeAdListener
광고 이벤트를 처리하는 인터페이스입니다.
interface AdropNativeAdListener {
fun onAdReceived(ad: AdropNativeAd)
fun onAdClicked(ad: AdropNativeAd)
fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode)
fun onAdImpression(ad: AdropNativeAd) // 선택적 구현
fun onAdVideoStart(ad: AdropNativeAd) // 선택적 구현
fun onAdVideoEnd(ad: AdropNativeAd) // 선택적 구현
}
AdropNativeAdProfile
광고주 프로필 정보를 담는 데이터 클래스입니다.
| 프로퍼티 | 타입 | 설명 |
|---|
displayLogo | String | 프로필 로고 이미지 URL |
displayName | String | 프로필 표시 이름 |
link | String | 프로필 링크 URL |
구현 가이드
1. XML 레이아웃 설정
네이티브 광고를 표시할 레이아웃을 정의합니다. AdropNativeAdView를 루트로 하고, 내부에 광고 요소들을 배치합니다.
res/layout/native_ad_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<io.adrop.ads.nativeAd.AdropNativeAdView
android:id="@+id/native_ad_view"
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="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!-- 프로필 영역 -->
<ImageView
android:id="@+id/ad_profile_logo"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/ad_profile_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/ad_profile_logo"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/ad_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:text="광고"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/ad_profile_logo"
app:layout_constraintTop_toBottomOf="@id/ad_profile_name" />
<!-- 미디어 영역 -->
<io.adrop.ads.nativeAd.AdropMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="@id/ad_profile_logo" />
<!-- 광고 내용 -->
<TextView
android:id="@+id/ad_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/ad_media" />
<TextView
android:id="@+id/ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/ad_headline" />
<!-- 하단 영역 -->
<TextView
android:id="@+id/ad_advertiser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_body" />
<Button
android:id="@+id/ad_call_to_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ad_body" />
</androidx.constraintlayout.widget.ConstraintLayout>
</io.adrop.ads.nativeAd.AdropNativeAdView>
2. 광고 로드 및 표시 (Kotlin)
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import io.adrop.ads.model.AdropErrorCode
import io.adrop.ads.nativeAd.AdropMediaView
import io.adrop.ads.nativeAd.AdropNativeAd
import io.adrop.ads.nativeAd.AdropNativeAdListener
import io.adrop.ads.nativeAd.AdropNativeAdView
class NativeAdActivity : AppCompatActivity() {
private lateinit var nativeAd: AdropNativeAd
private lateinit var nativeAdView: AdropNativeAdView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_native_ad)
nativeAdView = findViewById(R.id.native_ad_view)
loadNativeAd()
}
private fun loadNativeAd() {
// 1. 네이티브 광고 인스턴스 생성
nativeAd = AdropNativeAd(
context = this,
unitId = "YOUR_UNIT_ID" // 실제 유닛 ID로 교체
)
// 2. 리스너 설정
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
Log.d("Adrop", "광고 수신 성공")
populateNativeAdView(ad)
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "광고 수신 실패: $errorCode")
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "광고 클릭됨")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "광고 노출됨")
}
override fun onAdVideoStart(ad: AdropNativeAd) {
Log.d("Adrop", "광고 동영상 재생 시작")
}
override fun onAdVideoEnd(ad: AdropNativeAd) {
Log.d("Adrop", "광고 동영상 재생 종료")
}
}
// 3. 광고 로드
nativeAd.load()
}
private fun populateNativeAdView(ad: AdropNativeAd) {
// 프로필 설정
val profileLogoView = findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(this).load(ad.profile.displayLogo).into(profileLogoView)
nativeAdView.setProfileLogoView(profileLogoView)
val profileNameView = findViewById<TextView>(R.id.ad_profile_name)
profileNameView.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileNameView)
// 제목 설정
val headlineView = findViewById<TextView>(R.id.ad_headline)
headlineView.text = ad.headline
nativeAdView.setHeadLineView(headlineView)
// 미디어 설정
val mediaView = findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(mediaView)
// 본문 설정
val bodyView = findViewById<TextView>(R.id.ad_body)
bodyView.text = ad.body
nativeAdView.setBodyView(bodyView)
// 광고주 설정
val advertiserView = findViewById<TextView>(R.id.ad_advertiser)
advertiserView.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiserView)
// CTA 버튼 설정
val ctaView = findViewById<Button>(R.id.ad_call_to_action)
ctaView.text = ad.callToAction
nativeAdView.setCallToActionView(ctaView)
// 광고 데이터 바인딩
nativeAdView.setNativeAd(ad)
}
override fun onDestroy() {
// 리소스 해제
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
}
3. 광고 로드 및 표시 (Java)
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import io.adrop.ads.model.AdropErrorCode;
import io.adrop.ads.nativeAd.AdropMediaView;
import io.adrop.ads.nativeAd.AdropNativeAd;
import io.adrop.ads.nativeAd.AdropNativeAdListener;
import io.adrop.ads.nativeAd.AdropNativeAdView;
public class NativeAdActivity extends AppCompatActivity {
private AdropNativeAd nativeAd;
private AdropNativeAdView nativeAdView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_native_ad);
nativeAdView = findViewById(R.id.native_ad_view);
loadNativeAd();
}
private void loadNativeAd() {
// 1. 네이티브 광고 인스턴스 생성
nativeAd = new AdropNativeAd(
this,
"YOUR_UNIT_ID", // 실제 유닛 ID로 교체
null // contextId (선택사항)
);
// 2. 리스너 설정
nativeAd.setListener(new AdropNativeAdListener() {
@Override
public void onAdReceived(AdropNativeAd ad) {
Log.d("Adrop", "광고 수신 성공");
populateNativeAdView(ad);
}
@Override
public void onAdFailedToReceive(AdropNativeAd ad, AdropErrorCode errorCode) {
Log.e("Adrop", "광고 수신 실패: " + errorCode);
}
@Override
public void onAdClicked(AdropNativeAd ad) {
Log.d("Adrop", "광고 클릭됨");
}
@Override
public void onAdImpression(AdropNativeAd ad) {
Log.d("Adrop", "광고 노출됨");
}
@Override
public void onAdVideoStart(AdropNativeAd ad) {
Log.d("Adrop", "광고 동영상 재생 시작");
}
@Override
public void onAdVideoEnd(AdropNativeAd ad) {
Log.d("Adrop", "광고 동영상 재생 종료");
}
});
// 3. 광고 로드
nativeAd.load();
}
private void populateNativeAdView(AdropNativeAd ad) {
// 프로필 로고 설정
ImageView profileLogoView = findViewById(R.id.ad_profile_logo);
Glide.with(this).load(ad.getProfile().getDisplayLogo()).into(profileLogoView);
nativeAdView.setProfileLogoView(profileLogoView, null);
// 프로필 이름 설정
TextView profileNameView = findViewById(R.id.ad_profile_name);
profileNameView.setText(ad.getProfile().getDisplayName());
nativeAdView.setProfileNameView(profileNameView, null);
// 제목 설정
TextView headlineView = findViewById(R.id.ad_headline);
headlineView.setText(ad.getHeadline());
nativeAdView.setHeadLineView(headlineView, null);
// 미디어 설정
AdropMediaView mediaView = findViewById(R.id.ad_media);
nativeAdView.setMediaView(mediaView);
// 본문 설정
TextView bodyView = findViewById(R.id.ad_body);
bodyView.setText(ad.getBody());
nativeAdView.setBodyView(bodyView);
// 광고주 설정
TextView advertiserView = findViewById(R.id.ad_advertiser);
advertiserView.setText(ad.getProfile().getDisplayName());
nativeAdView.setAdvertiserView(advertiserView, null);
// CTA 버튼 설정
Button ctaView = findViewById(R.id.ad_call_to_action);
ctaView.setText(ad.getCallToAction());
nativeAdView.setCallToActionView(ctaView);
// 광고 데이터 바인딩
nativeAdView.setNativeAd(ad);
}
@Override
protected void onDestroy() {
// 리소스 해제
if (nativeAdView != null) {
nativeAdView.destroy();
}
if (nativeAd != null) {
nativeAd.destroy();
}
super.onDestroy();
}
}
고급 기능
전체 클릭 영역 설정
광고 뷰 전체 영역에서 클릭 이벤트가 발생하도록 설정할 수 있습니다.
nativeAdView.isEntireClick = true
isEntireClick을 true로 설정하면 광고 뷰 전체가 클릭 가능한 영역이 됩니다. 개별 요소의 클릭 리스너는 동작하지 않습니다.
개별 요소 클릭 리스너
특정 광고 요소에 커스텀 클릭 리스너를 설정할 수 있습니다.
// 광고주 클릭 시 커스텀 동작
nativeAdView.setAdvertiserView(advertiserView) {
Log.d("Adrop", "광고주 클릭")
// 커스텀 동작 구현
}
// 프로필 로고 클릭 시 커스텀 동작
nativeAdView.setProfileLogoView(profileLogoView) {
Log.d("Adrop", "프로필 로고 클릭")
// 커스텀 동작 구현
}
문맥 타겟팅
특정 컨텍스트에 맞는 광고를 요청할 수 있습니다.
val nativeAd = AdropNativeAd(
context = this,
unitId = "YOUR_UNIT_ID",
contextId = "SPORTS_NEWS" // 컨텍스트 ID
)
RecyclerView에서 사용하기
RecyclerView의 아이템으로 네이티브 광고를 표시할 수 있습니다.
class ContentAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val VIEW_TYPE_CONTENT = 0
const val VIEW_TYPE_AD = 1
const val AD_INTERVAL = 5 // 5개 아이템마다 광고 표시
}
private val items = mutableListOf<Any>()
private val nativeAds = mutableMapOf<Int, AdropNativeAd>()
override fun getItemViewType(position: Int): Int {
return if (position % (AD_INTERVAL + 1) == AD_INTERVAL) {
VIEW_TYPE_AD
} else {
VIEW_TYPE_CONTENT
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_AD -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_native_ad, parent, false)
NativeAdViewHolder(view)
}
else -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_content, parent, false)
ContentViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is NativeAdViewHolder -> loadNativeAd(holder, position)
is ContentViewHolder -> holder.bind(items[position])
}
}
private fun loadNativeAd(holder: NativeAdViewHolder, position: Int) {
// 이미 로드된 광고가 있으면 재사용
nativeAds[position]?.let { ad ->
if (ad.isLoaded) {
holder.bind(ad)
return
}
}
// 새 광고 로드
val nativeAd = AdropNativeAd(holder.itemView.context, "YOUR_UNIT_ID")
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
nativeAds[position] = ad
holder.bind(ad)
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "광고 로드 실패: $errorCode")
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "광고 클릭")
}
}
nativeAd.load()
}
class NativeAdViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val nativeAdView: AdropNativeAdView = itemView.findViewById(R.id.native_ad_view)
fun bind(ad: AdropNativeAd) {
val profileLogo = itemView.findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(itemView.context).load(ad.profile.displayLogo).into(profileLogo)
nativeAdView.setProfileLogoView(profileLogo)
val profileName = itemView.findViewById<TextView>(R.id.ad_profile_name)
profileName.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileName)
val headline = itemView.findViewById<TextView>(R.id.ad_headline)
headline.text = ad.headline
nativeAdView.setHeadLineView(headline)
val media = itemView.findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(media)
val body = itemView.findViewById<TextView>(R.id.ad_body)
body.text = ad.body
nativeAdView.setBodyView(body)
val advertiser = itemView.findViewById<TextView>(R.id.ad_advertiser)
advertiser.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiser)
val cta = itemView.findViewById<Button>(R.id.ad_call_to_action)
cta.text = ad.callToAction
nativeAdView.setCallToActionView(cta)
nativeAdView.setNativeAd(ad)
}
}
fun destroy() {
nativeAds.values.forEach { it.destroy() }
nativeAds.clear()
}
}
테스트 유닛 ID
개발 및 테스트 시 다음 테스트 유닛 ID를 사용하세요.
| 포맷 | 테스트 유닛 ID |
|---|
| 네이티브 (이미지) | PUBLIC_TEST_UNIT_ID_NATIVE |
| 네이티브 비디오 (16:9) | PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_16_9 |
| 네이티브 비디오 (9:16) | PUBLIC_TEST_UNIT_ID_NATIVE_VIDEO_9_16 |
프로덕션 배포 전에 반드시 실제 유닛 ID로 교체하세요.
생명주기 관리
Activity/Fragment에서 리소스 해제
네이티브 광고는 메모리 누수를 방지하기 위해 사용이 끝나면 반드시 해제해야 합니다.
override fun onDestroy() {
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
RecyclerView에서 리소스 해제
RecyclerView Adapter에서 광고를 사용하는 경우, 액티비티나 프래그먼트가 종료될 때 모든 광고를 해제해야 합니다.
override fun onDestroy() {
adapter.destroy()
super.onDestroy()
}
모범 사례
1. 필수 요소만 표시
모든 광고 요소를 표시할 필요는 없습니다. 앱의 디자인에 맞게 필요한 요소만 선택적으로 사용하세요.
// 최소 구성 예시 (미디어 + CTA만 사용)
nativeAdView.setMediaView(mediaView)
nativeAdView.setCallToActionView(ctaView)
nativeAdView.setNativeAd(ad)
2. 이미지 로딩 라이브러리 사용
아이콘, 커버, 프로필 이미지를 표시할 때는 Glide, Coil 등의 이미지 로딩 라이브러리를 사용하세요.
// Glide 사용 예시
Glide.with(context)
.load(ad.icon)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(iconView)
3. 광고 뷰 재사용
RecyclerView에서 광고를 표시할 때는 광고 인스턴스를 캐싱하여 재사용하세요.
4. 적절한 광고 위치
사용자 경험을 해치지 않는 선에서 자연스럽게 광고를 배치하세요.
- 콘텐츠 피드: 5~10개 아이템마다 1개의 광고
- 상세 화면: 콘텐츠 하단에 배치
- 스크롤 뷰: 자연스러운 콘텐츠 흐름에 맞춰 배치
5. 에러 처리
광고 로드 실패에 대비하여 적절한 폴백 처리를 구현하세요.
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
when (errorCode) {
AdropErrorCode.ERROR_CODE_AD_NO_FILL -> {
// 광고 없음 - 광고 영역 숨기기
nativeAdView.visibility = View.GONE
}
AdropErrorCode.ERROR_CODE_NETWORK -> {
// 네트워크 오류 - 재시도 로직
Handler(Looper.getMainLooper()).postDelayed({
nativeAd.load()
}, 3000)
}
else -> {
Log.e("Adrop", "광고 로드 실패: $errorCode")
}
}
}
완전한 예제
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import io.adrop.ads.model.AdropErrorCode
import io.adrop.ads.nativeAd.AdropMediaView
import io.adrop.ads.nativeAd.AdropNativeAd
import io.adrop.ads.nativeAd.AdropNativeAdListener
import io.adrop.ads.nativeAd.AdropNativeAdView
class NativeAdExampleActivity : AppCompatActivity() {
private lateinit var nativeAd: AdropNativeAd
private lateinit var nativeAdView: AdropNativeAdView
private lateinit var loadingView: View
private lateinit var errorView: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_native_ad_example)
nativeAdView = findViewById(R.id.native_ad_view)
loadingView = findViewById(R.id.loading_view)
errorView = findViewById(R.id.error_view)
findViewById<Button>(R.id.retry_button).setOnClickListener {
loadNativeAd()
}
loadNativeAd()
}
private fun loadNativeAd() {
showLoading()
nativeAd = AdropNativeAd(
context = this,
unitId = "PUBLIC_TEST_UNIT_ID_NATIVE"
)
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
Log.d("Adrop", "광고 수신: unitId=${ad.unitId}, txId=${ad.txId}")
populateNativeAdView(ad)
showAd()
}
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {
Log.e("Adrop", "광고 수신 실패: $errorCode")
showError()
}
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "광고 클릭: txId=${ad.txId}")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "광고 노출: txId=${ad.txId}")
}
}
nativeAd.load()
}
private fun populateNativeAdView(ad: AdropNativeAd) {
// 전체 클릭 영역 활성화 (선택사항)
// nativeAdView.isEntireClick = true
// 프로필 로고
val profileLogoView = findViewById<ImageView>(R.id.ad_profile_logo)
Glide.with(this)
.load(ad.profile.displayLogo)
.circleCrop()
.into(profileLogoView)
nativeAdView.setProfileLogoView(profileLogoView)
// 프로필 이름
val profileNameView = findViewById<TextView>(R.id.ad_profile_name)
profileNameView.text = ad.profile.displayName
nativeAdView.setProfileNameView(profileNameView)
// 제목
val headlineView = findViewById<TextView>(R.id.ad_headline)
headlineView.text = ad.headline
nativeAdView.setHeadLineView(headlineView)
// 미디어 (이미지 또는 동영상)
val mediaView = findViewById<AdropMediaView>(R.id.ad_media)
nativeAdView.setMediaView(mediaView)
// 본문
val bodyView = findViewById<TextView>(R.id.ad_body)
bodyView.text = ad.body
nativeAdView.setBodyView(bodyView)
// 광고주
val advertiserView = findViewById<TextView>(R.id.ad_advertiser)
advertiserView.text = ad.profile.displayName
nativeAdView.setAdvertiserView(advertiserView) {
Log.d("Adrop", "광고주 클릭: ${ad.profile.displayName}")
}
// CTA 버튼
val ctaView = findViewById<Button>(R.id.ad_call_to_action)
ctaView.text = ad.callToAction
nativeAdView.setCallToActionView(ctaView)
// 광고 데이터 바인딩
nativeAdView.setNativeAd(ad)
}
private fun showLoading() {
loadingView.visibility = View.VISIBLE
nativeAdView.visibility = View.GONE
errorView.visibility = View.GONE
}
private fun showAd() {
loadingView.visibility = View.GONE
nativeAdView.visibility = View.VISIBLE
errorView.visibility = View.GONE
}
private fun showError() {
loadingView.visibility = View.GONE
nativeAdView.visibility = View.GONE
errorView.visibility = View.VISIBLE
}
override fun onDestroy() {
nativeAdView.destroy()
nativeAd.destroy()
super.onDestroy()
}
}
일괄 로드 (loads)
AdropNativeAd.loads()를 사용하면 한 번의 호출로 여러 네이티브 광고를 요청할 수 있습니다. 피드·캐러셀·페이지네이션 리스트에 삽입할 광고 풀을 프리페칭하는 데 유용합니다.
시그니처
AdropNativeAd.loads(
context: Context,
unitId: String,
contextId: String? = null,
listener: AdropNativeAdListener,
)
사용 방법
AdropNativeAd.loads(
context = this,
unitId = "YOUR_UNIT_ID",
contextId = null,
listener = object : AdropNativeAdListener {
// 배치 성공 — 각 광고는 렌더링이 완료된 상태로 전달됩니다.
override fun onAdsReceived(ads: List<AdropNativeAd>) {
ads.forEach { ad ->
// AdropNativeAdView에 바인딩하거나 라이프사이클용으로 보관
bindToView(ad)
}
}
// 배치 실패
override fun onAdsFailedToReceive(errorCode: AdropErrorCode) {
Log.e("Adrop", "배치 로드 실패: $errorCode")
}
// 단건 콜백은 loads 경로에서 호출되지 않습니다.
override fun onAdReceived(ad: AdropNativeAd) { /* batch path */ }
override fun onAdFailedToReceive(ad: AdropNativeAd, errorCode: AdropErrorCode) {}
// 클릭/노출/비디오 콜백은 각 인스턴스에 대해 정상 호출됩니다.
override fun onAdClicked(ad: AdropNativeAd) {
Log.d("Adrop", "네이티브 클릭: ${ad.creativeId}")
}
override fun onAdImpression(ad: AdropNativeAd) {
Log.d("Adrop", "네이티브 노출: ${ad.creativeId}")
}
},
)
리스너 콜백
| 콜백 | 호출 시점 |
|---|
onAdsReceived(ads) | 배치 전달 완료. 모든 광고가 완전히 렌더링된 상태로 전달됩니다. |
onAdsFailedToReceive(errorCode) | 요청 거부, 네트워크 실패 또는 표시 가능한 광고가 없는 경우(ERROR_CODE_AD_NO_FILL). |
onAdClicked / onAdImpression / onAdVideoStart / onAdVideoEnd | 단건 load()와 동일하게 각 광고별로 호출됩니다. |
단건 onAdReceived / onAdFailedToReceive 콜백은 loads() 경로에서 호출되지 않습니다. 배치 결과는 반드시 onAdsReceived / onAdsFailedToReceive로만 수신하세요.
제약 사항
- 호출당 최대 5개. 서버가 그 이상을 반환하더라도 앞의 5개만 전달됩니다.
- 백필은 적용되지 않습니다. 직광고 채움이 없으면 백필 설정과 무관하게
onAdsFailedToReceive가 ERROR_CODE_AD_NO_FILL로 호출됩니다.
onAdsReceived 안에서 destroy()를 호출하지 마세요. 각 광고를 AdropNativeAdView.setNativeAd(...)로 바인딩하거나 라이프사이클용으로 보관한 후, 부모 Activity.onDestroy()(또는 동등한 시점)에서 destroy() 하세요.
AdropBanner.loads()와 달리, 네이티브 배치는 모든 광고의 렌더링이 끝날 때까지 기다린 후 onAdsReceived를 호출합니다. 전달 시점에 각 인스턴스는 완전히 렌더링된 상태입니다.
백필 광고
백필 광고가 활성화된 경우, 직광고가 없을 때 자동으로 백필 광고가 로드됩니다. isBackfilled 프로퍼티로 백필 광고 여부를 확인할 수 있습니다.
nativeAd.listener = object : AdropNativeAdListener {
override fun onAdReceived(ad: AdropNativeAd) {
if (ad.isBackfilled) {
println("백필 광고가 로드되었습니다")
} else {
println("직광고가 로드되었습니다")
}
}
override fun onAdFailedToReceive(ad: AdropNativeAd, 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 의존성을 추가해야 합니다. 시작하기를 참고하세요.
다음 단계