본문으로 건너뛰기

네이티브 광고

네이티브 광고는 플랫폼이 제공하는 UI 구성요소를 통해 기존에 앱의 시각적 디자인과 일관된 방식으로 앱을 수익화 할 수 있습니다.

네이티브 광고가 로드되면 광고 에셋이 포함된 광고 객체를 앱에서 수신하며, SDK 를 연동한 앱에서 해당 에셋들을 활용하여 광고 콘텐츠를 렌더링합니다.


시작하기 앞서

  • 광고 호출을 위해 Ad Unit ID 가 필요합니다.
    • NAM Admin 을 통해 광고 공급자 설정, Inventory 설정, 광고 유닛 등록 등의 과정을 마무리하여 주시기 바랍니다.
    • 관련 내용은 NAM 관리자에게 문의 부탁드립니다.
  • 광고를 오버레이로 덮는 다른 View 가 있을 경우, 경우에 따라서 노출 측정이 제대로 되지 않아 성과 지표 측정에 불이익이 있을 수 있습니다.

[Step 1] NAM SDK 적용 완료

공통 통합 내용 을 참고해 주세요.

이하 내용은 NAM SDK 적용이 완료된 상태를 가정하고 진행합니다.


[Step 2] 광고 뷰 컨테이너 정의

먼저, 네이티브 광고의 컨테이너가 될 ViewGroup 을 레이아웃 XML 파일에 추가해야 합니다.

아래 예제에서는 네이티브 광고가 게재될 ViewGroup 으로서 native_ad_container 의 id 를 가지는 ViewGroup 을 선언했습니다.

<!-- native ad view container -->
<FrameLayout
android:id="@+id/native_ad_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

[Step 3] GfpNativeAdView 정의

네이티브 광고가 게재될 ViewGroup 레이아웃을 정의했다면 이어서 네이티브 광고가 렌더링 될 레이아웃을 구성해야 합니다.

GfpNativeAdViewGfpNativeAd 에 매핑되는 ViewGroup 으로서 GfpNativeAd 를 통해서 제공되는 광고의 에셋들을 렌더링하는데 사용되는 개별 View 들은 GfpNativeAdView 의 자식 뷰여야만 합니다.

다음은 assets_container 의 id 를 가지는 FrameLayout 으로 광고 에셋들을 표시하는 레이아웃 구성 예시입니다.

<?xml version="1.0" encoding="utf-8"?>
<com.naver.gfpsdk.GfpNativeAdView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gfp_native_ad"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<FrameLayout
android:id="@+id/assets_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#FFFFFF"
android:minHeight="50dp"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:orientation="horizontal">

<ImageView
android:id="@+id/ad_icon"
android:layout_width="46dp"
android:layout_height="46dp"
android:layout_marginRight="10dp"
android:adjustViewBounds="true" />

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">

<TextView
android:id="@+id/ad_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:ellipsize="end"
android:lines="1"
android:textColor="#000000"
android:textSize="12dp" />

<TextView
android:id="@+id/ad_sponsored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#999999"
android:textSize="12dp" />

</LinearLayout>

<com.naver.gfpsdk.GfpAdChoicesView
android:id="@+id/ad_choices_view"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:id="@+id/ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="#000000"
android:textSize="15dp" />

<com.naver.gfpsdk.GfpMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="3"
android:orientation="vertical">

<TextView
android:id="@+id/ad_advertiser"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
android:textColor="@android:color/darker_gray"
android:textSize="13dp" />

<TextView
android:id="@+id/ad_social_context"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
android:lines="1"
android:textColor="@android:color/black"
android:textSize="13dp" />

</LinearLayout>

<Button
android:id="@+id/ad_call_to_action"
android:layout_width="100dp"
android:layout_height="40dp"
android:background="#3c3c3c"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:textColor="#ffffff"
android:textSize="13dp" />

</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</com.naver.gfpsdk.GfpNativeAdView>

주의할 점

GfpNativeAdView 가 포함된 레이아웃을 구성할 때는 다음 제약 사항을 반드시 준수해야 합니다.

  • 최상위 뷰는 GfpNativeAdView 여야 합니다.
  • GfpNativeAdView 의 하위 뷰는 에셋을 담을 ViewGroup 으로 지정해야 합니다.
  • AdChoices 가 렌더링되는 GfpAdChoiceView 를 포함해야 합니다.
    • DFP 모듈이 제공하는 광고는 DFP SDK 에 의해 별도의 뷰로 AdChoices 가 자동으로 렌더링됩니다.
    • 그 외 모듈로 제공되는 광고의 AdChoices 는 해당 영역에 렌더링됩니다.
    • GfpAdChoiceView 의 너비와 높이는 20dp 이상이어야 하며, 권장 값은 너비와 높이 모두 20dp 입니다.
  • 아이콘이 아닌 메인 이미지나 비디오가 렌더링될 영역은 GfpMediaView 로 선언해야 합니다.

[Step 4] GfpAdLoader 빌드

네이티브 광고는 GfpAdLoader 클래스를 통해 로드되며 GfpAdLoader 는 생성 과정 중에 다양한 맞춤 설정이 가능한 GfpAdLoader.Builder 클래스를 통해 생성할 수 있습니다. GfpAdLoader 를 빌드할 때 네이티브 광고를 로드하겠다는 선언으로 간주되는 withNativeAd() 메서드를 설정함으로서 네이티브 광고를 로드할 수 있습니다.

GfpAdLoader 를 빌드할 때는 광고 요청을 위한 필수 정보에 해당되는 Ad Unit ID 를 비롯하여, 단일 광고 요청에 대한 런타임 정보(예: 타겟팅 정보) 가 포함된 AdParam 객체를 생성 후 매개변수로 전달해야만 합니다. AdParam 에 대한 자세한 설명은 광고 요청 정보를 참고해 주시기 바랍니다.

// Create a new ad parameter.
val adParam = AdParam.Builder()
.setAdUnitId("YOUR_AD_UNIT_ID")
...
.build()

// Create a new ad loader.
val adLoader = GfpAdLoader.Builder(this, adParam)
.withNativeAd { nativeAd ->
// Show the native ad.
}
.withAdListener(object: AdEventListener() {
...
})
.build()

위 예시와 같이, withNativeAd() 메서드를 통해 네이티브 광고를 수신할 수 있는 GfpAdLoader 를 생성할 수 있습니다. 네이티브 광고가 성공적으로 로드되면 매개변수로 선언된 GfpNativeAd.OnNativeAdLoadedListener()onNativeAdLoaded() 메서드가 호출됩니다.


[Step 5] 광고 이벤트 수신 설정

GfpAdLoader.Builder 가 제공하는 withAdListener() 메서드를 통해 광고의 생명 주기에 해당되는 다양한 이벤트들을 수신할 수 있습니다.

수신하고자 하는 이벤트들이 있을 경우, AdEventListener 가 제공하는 메서드들을 선택적으로 오버라이드해서 구현하면 됩니다.

val adLoader = GfpAdLoader.Builder(this, adParam)
...
.withAdListener(object : AdEventListener() {
override fun onAdClicked() {
// Called when an ad is clicked.
}

override fun onAdImpression() {
// Called when an impression is recorded for an ad.
}

override fun onAdMuted() {
// Called when an ad is muted.
}

override fun onError(error: GfpError?, responseInfo: GfpResponseInfo?) {
// Called when an error happened while the banner ad is
// attempting to load or rendering an ad.
}
})
.build()

[Step 6] 광고 로드

GfpAdLoader 빌드가 마무리 되었다면, GfpAdLoaderloadAd() 메서드를 통해 광고를 로드할 수 있습니다.

주의

UI 스레드에서 광고를 로드해야 합니다.

주의

기선언된 GfpAdLoader 를 재사용하여 광고를 요청하려면 cancel() 호출 없이 loadAd() 만 호출해야만 합니다. 만약 cancel() 메서드 호출한 후 loadAd() 를 호출하면, 리소스가 해제된 상태로 요청이 발생하여 광고 이벤트를 수신하지 못하는 등의 이슈가 발생할 수 있습니다.

class ExampleActivity : AppCompatActivity() {
private lateinit var adLoader: GfpAdLoader

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_example)

// Create a new ad parameter
val adParam = AdParam.Builder()
.setAdUnitId("YOUR_AD_UNIT_ID")
.build()

// Create a new ad loader
adLoader = GfpAdLoader.Builder(this, adParam)
.withNativeAd { nativeAd ->
// Show the native ad.
}
.withAdListener(object: AdEventListener() {
...
})
.build()

// Load the native ad
adLoader.loadAd()
}
}

[Step 7] 광고 렌더링

네이티브 광고가 성공적으로 로드되면 GfpNativeAd 객체를 전달받게 됩니다. GfpNativeAd 가 제공하는 모든 네이티브 광고 에셋들은 GfpNativeAdView 하위에 에셋의 특징에 맞는 View 로 매핑하여 렌더링 해야합니다.

다음은 GfpNativeAdView 를 생성하고 여기에 GfpNativeAd 를 적용하는 코드의 예입니다.

class ExampleActivity : AppCompatActivity() {
private lateinit var adLoader: GfpAdLoader

override fun onCreate(savedInstanceState: Bundle?) {
...

// Create a new ad loader
adLoader = GfpAdLoader.Builder(this, adParam)
.withNativeAd { nativeAd ->
// Assumes you have a native ad container FrameLayout in your View layout
val nativeAdContainer: FrameLayout = findViewById(R.id.native_ad_container)

// This method sets the assets into the ad view
inflateAd(nativeAdContainer, nativeAd)
}
.withAdListener(object: AdEventListener() {
...
})
.build()


// Load the native ad
adLoader.loadAd()
}

private fun inflateAd(parent: ViewGroup, nativeAd: GfpNativeAd) {
// Assumes that your native ad layout is in a file call native_ad.xml
val nativeAdView = layoutInflater.inflate(R.layout.native_ad_layout, parent) as GfpNativeAdView

// Add the GfpAdChoicesView
val adChoicesView: GfpAdChoicesView = nativeAdView.findViewById(R.id.ad_choices_view)
nativeAdView.adChoicesView = adChoicesView

// You need to register the ViewGroup that contains all the views mapped to the native ad assets as assetsContainer.
// This ViewGroup should be provided as a separate ViewGroup and must be a subview of GfpNativeAdView.
val assetsContainer: FrameLayout = nativeAdView.findViewById(R.id.assets_container)
nativeAdView.assetsContainer = assetsContainer

// Locate the ImageView that will hold the ad icon, set its image, and call the
// GfpNativeAdView's setIconView method to register it.
val iconView: ImageView = nativeAdView.findViewById(R.id.ad_icon)
val icon = nativeAd.icon
if (icon != null) {
iconView.setImageDrawable(icon.drawable)
iconView.visibility = View.VISIBLE
nativeAdView.iconView = iconView
} else {
iconView.visibility = View.GONE
}

// You're required to use the GfpMediaView instead of the ImageView or VideoView
// if you want to include a main image or video asset in the layout for your native ad.
val mediaView: GfpMediaView = nativeAdView.findViewById(R.id.ad_media)
nativeAdView.mediaView = mediaView

// Create native UI with text asset
val titleView: TextView = nativeAdView.findViewById(R.id.ad_title)
val bodyView: TextView = nativeAdView.findViewById(R.id.ad_body)
val advertiserView: TextView = nativeAdView.findViewById(R.id.ad_advertiser)
val callToActionView: Button = nativeAdView.findViewById(R.id.ad_call_to_action)

// Set the Text, and and use the GfpNativeView's properties to register it.
titleView.text = nativeAd.title
nativeAdView.titleView = titleView

bodyView.text = nativeAd.body
nativeAdView.bodyView = bodyView

advertiserView.text = nativeAd.advertiserName
nativeAdView.advertiserView = advertiserView

callToActionView.text = nativeAd.callToAction
nativeAdView.callToActionView = callToActionView

// Call the GfpNativeAdView's setNativeAd method to register the native ad object.
nativeAdView.setNativeAd(nativeAd)

// Ensure that the adContainer view doesn't already contain an ad view.
nativeAdContainer.removeAllViews()

// Place the native ad view into the adContainer
nativeAdContainer.addView(nativeAdView)
}
}

각 작업은 다음과 같습니다:

1. GfpNativeAdView 를 inflate

아래 코드는 네이티브 광고를 표시하는 뷰가 포함된 XML 레이아웃을 inflate 한 다음 GfpNativeAdView 에 대한 참조를 찾습니다. Activity 나 Fragment 에 GfpNativeAdView 가 있거나 레이아웃 파일을 사용하지 않고 동적으로 인스턴스를 만든 경우, 기존의 객체를 재사용할 수 있습니다.

val nativeAdView = layoutInflater.inflate(R.layout.native_ad_layout, parent) as GfpNativeAdView

2. AdChoices 를 포함하는 GfpAdChoicesView 를 등록

AdChoices 는 업계 표준 아이콘으로, 광고 개인 최적화 관리 링크 또는 사용자가 불법 또는 정책 위반 콘텐츠를 신고할 수 있는 역할을 수행합니다. 따라서, AdChoices 가 포함될 수 있는 GfpAdChoicesView 는 레이아웃 구성시 반드시 포함되어야 하며, GfpNativeAdViewadChoicesView 를 사용하여 반드시 등록 되어야 합니다.

주의

adChoicesView 를 등록하는 작업이 누락될 경우, Exception 이 발생합니다.

val adChoicesView: GfpAdChoicesView = nativeAdView.findViewById(R.id.ad_choices_view)
nativeAdView.adChoicesView = adChoicesView

3. 광고 에셋들을 포함하는 ViewGroup 을 등록

네이티브 광고 에셋이 매핑된 모든 View 가 포함된 ViewGroup 을 GfpNativeAdViewassetsContainer 로 등록해야 합니다. 이 뷰그룹은 별도의 ViewGroup 으로 제공되어야 하며, GfpNativeAdView 의 하위 View 여야 합니다.

주의

assetsContainer 를 등록하는 작업이 누락될 경우, Exception 이 발생합니다.

val assetsContainer: FrameLayout = nativeAdView.findViewById(R.id.assets_container)
nativeAdView.assetsContainer = assetsContainer

4. Icon 에셋 등록

Icon 에셋은 광고주를 대표하는 아이콘입니다. Icon 에셋은 광고 제공자나 광고 소재에 따라 포함되지 않을 수 있으므로 이를 유념하여 처리해야 합니다.

val iconView: ImageView = nativeAdView.findViewById(R.id.ad_icon)
val icon = nativeAd.icon
if (icon != null) {
iconView.setImageDrawable(icon.drawable)
iconView.visibility = View.VISIBLE
nativeAdView.iconView = iconView
} else {
iconView.visibility = View.GONE
}

5. GfpMediaView 등록

네이티브 광고 레이아웃에 이미지 또는 동영상 에셋을 포함하려면 반드시 GfpMediaView 를 사용해야 합니다.

GfpMediaView 는 이미지 또는 동영상을 표시하도록 설계된 특수 View 로서 다른 네이티브 광고 에셋에 매핑되는 View 들과는 달리 별도로 광고 에셋을 매핑하는 작업 없이 GfpNativeAdView 에 등록 작업만 수행하면 됩니다.

val mediaView: GfpMediaView = nativeAdView.findViewById(R.id.ad_media)
nativeAdView.mediaView = mediaView

6. 텍스트로 구성된 나머지 에셋들을 등록

텍스트로 구성된 나머지 에셋 View 들에 문자열을 설정하며, 이들을 GfpNativeAdView 에 등록합니다.

// Create native UI with text asset
val titleView: TextView = nativeAdView.findViewById(R.id.ad_title)
val bodyView: TextView = nativeAdView.findViewById(R.id.ad_body)
val advertiserView: TextView = nativeAdView.findViewById(R.id.ad_advertiser)
val callToActionView: Button = nativeAdView.findViewById(R.id.ad_call_to_action)

// Set the Text, and and use the GfpNativeView's properties to register it.
titleView.text = nativeAd.title
nativeAdView.titleView = titleView

bodyView.text = nativeAd.body
nativeAdView.bodyView = bodyView

advertiserView.text = nativeAd.advertiserName
nativeAdView.advertiserView = advertiserView

callToActionView.text = nativeAd.callToAction
nativeAdView.callToActionView = callToActionView

7. GfpNativeAd 등록

마지막 단계로서, GfpNativeAd 객체를 등록합니다.

nativeAdView.setNativeAd(nativeAd)

[Step 8] 광고 리소스 해제

네이티브 광고의 게재가 끝나면 광고에 할당된 리소스가 해제될 수 있도록 cancel() 메서드를 호출해야 합니다.

주의

기선언된 GfpAdLoader 를 재사용하여 광고를 요청하려면 cancel() 호출 없이 loadAd() 만 호출해야만 합니다. 만약 cancel() 메서드 호출한 후 loadAd() 를 호출하면, 리소스가 해제된 상태로 요청이 발생하여 광고 이벤트를 수신하지 못하는 등의 이슈가 발생할 수 있습니다.

정보

cancel() 메서드는 사용되거나 참조되지 않은 경우에도 모든 광고에서 호출되어야 합니다.

아래 예시는 ActivityonDestroy() 메서드에서 Activity 내에서 사용한 모든 GfpAdLoader 참조를 해제하는 예시입니다.

override fun onDestroy() {
adLoader?.destroy()
super.onDestroy()
}