본문으로 건너뛰기

네이티브 템플릿 광고

NAM SDK 8.5.0 버전부터는 네이티브 심플 광고 형식을 통해 복잡한 설정 및 추가 작업 없이 네이티브 광고를 적용할 수 있습니다. 이 가이드에서는 네이티브 심플 광고 형식으로 네이티브 광고를 로드 및 게재하는 방식인 네이티브 템플릿 옵션에 대해서 설명합니다.

네이티브 템플릿은 네이티브 광고를 좀 더 빠르게 구현하고 손쉽게 수정할 수 있게 고안된 것으로 네이티브 템플릿을 사용하면 처음 만드는 네이티브 광고도 수 분안에 구현할 수 있고, 많은 코드 없이도 디자인과 스타일을 빠르게 맞춤설정할 수 있습니다.

이 가이드를 효과적으로 적용하려면 네이티브 광고를 구현하는 것에 익숙해야 합니다.


시작하기 앞서

네이티브 심플 광고 가이드를 참고하여 네이티브 심플 광고를 로드 및 게재하는 방법을 알아보세요.


네이티브 템플릿 옵션

네이티브 심플 광고 형식을 통해 네이티브 광고를 로드 및 게재할 경우, GfpNativeSimpleAdOptions.Builder().setNativeTemplateOptions() 메서드를 사용하여 네이티브 템플릿 사용을 선언할 수 있습니다.

GfpNativeSimpleAdOptions 를 빌드하면서 GfpNativeTemplateOptions 를 설정하지 않으면, 해당 GfpNativeSimpleAdOptions 로 로드되는 네이티브 심플 광고는 네이티브 템플릿 광고를 사용하지 않는 것으로 간주됩니다. 이 경우 일반적인 네이티브 심플 광고 사용과 동일하게 네이티브 심플 광고 로드만 수행됩니다.

아래 예시는 GfpNativeTemplateOptions 을 기본값으로 적용하여 네이티브 템플릿을 적용하는 예시를 보여줍니다.

val nativeTemplateOptions = GfpNativeTemplateOptions.Builder().build()
val nativeSimpleAdOptions = GfpNativeSimpleAdOptions.Builder()
.setNativeTemplateOptions(nativeTemplateOptions)
.build()
val adLoader = GfpAdLoader.Builder(this, adParam)
...
.withNativeSimpleAd(nativeSimpleAdOptions) { nativeSimpleAd ->
...
}
.build()

커스텀 네이티브 템플릿

위 예시와 같이 기본 값의 GfpNativeTemplateOptions 을 적용할 경우, SDK 에 내장된 기본 템플릿을 통해 아래 이미지와 같이 네이티브 광고를 렌더링하게 됩니다.

image

네이티브 템플릿을 사용하면서 네이티브 광고의 레이아웃을 처음부터 직접 디자인하고 싶다면 아래 예시를 따라주시면 됩니다.

1. 네이티브 템플릿 레이아웃 정의

네이티브 광고의 레이아웃을 아래 예시와 같이 정의합니다.

정보

네이티브 템플릿을 위한 광고 레이아웃을 정의할 때, 정의된 최상위 ViewGroup 은 SDK에 의해 자동으로 GfpNativeAdView의 자식 뷰로 추가됩니다. 따라서 기존의 네이티브 광고 레이아웃과 달리, 광고 에셋을 GfpNativeAdView 내부에 직접 포함할 필요가 없습니다.

custom_native_template.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="ContentDescription,RtlHardcoded,SpUsage,RtlSymmetry"
tools:parentTag="com.naver.example.CustomNativeTemplateAdView">

<LinearLayout
android:id="@+id/ad_top_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp">

<ImageView
android:id="@+id/ad_icon"
android:layout_width="35dp"
android:layout_height="35dp" />

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="5dp"
android:paddingRight="5dp">

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

<TextView
android:id="@+id/ad_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1"
android:text="AD"
android:textColor="@android:color/darker_gray"
android:textSize="12dp" />

</LinearLayout>

<com.naver.gfpsdk.GfpAdChoicesView
android:id="@+id/ad_choices"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

<com.naver.gfpsdk.GfpMediaView
android:id="@+id/ad_media"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ad_top_container"
android:gravity="center" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ad_media"
android:orientation="horizontal"
android:padding="5dp">

<TextView
android:id="@+id/ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:ellipsize="end"
android:gravity="center_vertical"
android:lines="2"
android:textColor="@android:color/black"
android:textSize="12dp" />

<Button
android:id="@+id/ad_call_to_action"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="#4286F4"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:textColor="@android:color/white"
android:textSize="12sp" />

</LinearLayout>
</merge>

2. 네이티브 템플릿 뷰 정의

앞서 정의한 레이아웃을 사용하여 커스텀한 네이티브 템플릿 뷰를 정의합니다.

CustomNativeTemplateAdView.kt
package com.naver.example

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import com.naver.example.R
import com.naver.gfpsdk.GfpAdChoicesView
import com.naver.gfpsdk.GfpMediaView
import com.naver.gfpsdk.GfpNativeAdView
import com.naver.gfpsdk.mediation.NativeAssetProvider
import com.naver.gfpsdk.mediation.NativeTemplateAdView

class CustomNativeTemplateAdView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr), NativeTemplateAdView {
class Factory : NativeTemplateAdView.Factory {
override fun create(context: Context): NativeTemplateAdView {
return CustomNativeTemplateAdView(context)
}
}

private val iconView: ImageView
private val advertiserView: TextView
private val bodyView: TextView
private val callToActionView: Button
private val adChoicesView: GfpAdChoicesView
private val mediaView: GfpMediaView

init {
LayoutInflater.from(context).inflate(R.layout.custom_native_template_ad_view, this)
iconView = findViewById(R.id.ad_icon)
advertiserView = findViewById(R.id.ad_advertiser)
adChoicesView = findViewById(R.id.ad_choices)
mediaView = findViewById(R.id.ad_media)
bodyView = findViewById(R.id.ad_body)
callToActionView = findViewById(R.id.ad_call_to_action)
}

override fun bind(
nativeAdView: GfpNativeAdView,
nativeAssetProvider: NativeAssetProvider,
renderingOptions: NativeTemplateAdView.RenderingOptions
) {
val icon = nativeAssetProvider.icon
if (icon != null) {
iconView.visibility = View.VISIBLE
iconView.setImageDrawable(icon.drawable)
nativeAdView.iconView = iconView
} else {
iconView.visibility = View.GONE
}

val advertiser = nativeAssetProvider.advertiserName
if (advertiser != null) {
advertiserView.visibility = View.VISIBLE
advertiserView.text = advertiser
nativeAdView.advertiserView = advertiserView
} else {
advertiserView.visibility = View.GONE
}

val body = nativeAssetProvider.body
if (body != null) {
bodyView.visibility = View.VISIBLE
bodyView.text = body
nativeAdView.bodyView = bodyView
} else {
bodyView.visibility = View.GONE
}

val callToAction = nativeAssetProvider.callToAction
if (callToAction != null) {
callToActionView.visibility = View.VISIBLE
callToActionView.text = callToAction
nativeAdView.callToActionView = callToActionView
} else {
callToActionView.visibility = View.GONE
}

nativeAdView.adChoicesView = adChoicesView
nativeAdView.mediaView = mediaView
}

override fun onUserShowInterestChanged(
showInterest: Boolean,
nativeAssetProvider: NativeAssetProvider,
renderingOptions: NativeTemplateAdView.RenderingOptions
) {
// do nothing
}
}

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

1. NativeTemplateAdView 를 상속하는 커스텀 뷰를 생성

커스텀 템플릿용 뷰를 생성할 때는 아래 예시와 같이 반드시 NativeTemplateAdView 를 implement 해야 합니다. 또한, 앞서 생성한 레이아웃을 사용하여 뷰를 생성합니다.

class CustomNativeTemplateAdView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr),
NativeTemplateAdView {
...

private val iconView: ImageView
private val advertiserView: TextView
private val bodyView: TextView
private val callToActionView: Button
private val adChoicesView: GfpAdChoicesView
private val mediaView: GfpMediaView

init {
LayoutInflater.from(context).inflate(R.layout.custom_native_template_ad_view, this)
iconView = findViewById(R.id.ad_icon)
advertiserView = findViewById(R.id.ad_advertiser)
adChoicesView = findViewById(R.id.ad_choices)
mediaView = findViewById(R.id.ad_media)
bodyView = findViewById(R.id.ad_body)
callToActionView = findViewById(R.id.ad_call_to_action)
}
}

2. bind() 메서드에서 광고 에셋을 각 View에 매핑

NativeTemplateAdView 의 bind() 메서드를 오버라이드하여 NativeAssetProvider 가 제공하는 광고 에셋을 각 View에 바인딩합니다.

정보
  • GfpAdChoicesView 와 GfpMediaView: 별도 설정 없이 GfpNativeAdView 에 등록만 하면 됩니다.
  • 기타 광고 에셋: 이미지나 텍스트를 먼저 설정한 후 GfpNativeAdView 에 등록해야 합니다.
class CustomNativeTemplateAdView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr),
NativeTemplateAdView {
...

override fun bind(
nativeAdView: GfpNativeAdView,
nativeAssetProvider: NativeAssetProvider,
renderingOptions: NativeTemplateAdView.RenderingOptions
) {
val icon = nativeAssetProvider.icon
if (icon != null) {
iconView.visibility = View.VISIBLE
iconView.setImageDrawable(icon.drawable)
nativeAdView.iconView = iconView
} else {
iconView.visibility = View.GONE
}

val advertiser = nativeAssetProvider.advertiserName
if (advertiser != null) {
advertiserView.visibility = View.VISIBLE
advertiserView.text = advertiser
nativeAdView.advertiserView = advertiserView
} else {
advertiserView.visibility = View.GONE
}

...

nativeAdView.adChoicesView = adChoicesView
nativeAdView.mediaView = mediaView
}
}

3. Factory 클래스 생성

커스텀 네이티브 템플릿 뷰를 생성하려면 반드시 NativeTemplateAdView.Factory 를 상속하는 Factory 클래스가 필요합니다. 생성된 Factory 객체는 GfpNativeTemplateOptions 에 등록하여 커스텀 네이티브 템플릿 뷰로 사용할 수 있습니다.

class CustomNativeTemplateAdView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr),
NativeTemplateAdView {
...

class Factory : NativeTemplateAdView.Factory {
override fun create(context: Context): NativeTemplateAdView {
return CustomNativeTemplateAdView(context)
}
}
}

4. (Optional) onUserShowInterestChanged() 메서드

NativeTemplateAdView 의 onUserShowInterestChanged() 메서드는 사용자의 광고 관심도 변화 시점에 호출됩니다.

호출 시점:

  • 관심 시작: 광고 영역의 50% 이상이 1초 동안 노출될 때, showInterest 값이 true 로 전달됨.
  • 관심 종료: 광고 영역이 더 이상 노출되지 않을 때, showInterest 값이 false 로 전달됨.

활용 예시: 사용자가 관심을 보이는 시점에 Call to Action 버튼의 색상을 변경하여 광고 주목도를 높이는 등의 동적 UI 처리가 가능합니다.

class CustomNativeTemplateAdView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr),
NativeTemplateAdView {
...

override fun onUserShowInterestChanged(
showInterest: Boolean,
nativeAssetProvider: NativeAssetProvider,
renderingOptions: NativeTemplateAdView.RenderingOptions
) {
if (showInterest) {
callToActionView.setBackgroundColor(highlightedBgColor)
} else {
callToActionView.setBackgroundColor(defaultBgColor)
}
}
}

3. 네이티브 템플릿 등록

커스텀 네이티브 템플릿 뷰를 사용하려면 GfpNativeTemplateOptions 에 등록해야 합니다.

다음과 같이 Factory 를 등록합니다:

  • addNativeTemplateAdViewFactory() 메서드 사용
  • GfpNativeTemplateOptions.DEFAULT_VISUAL_KEY 를 비주얼 키로 설정
  • 앞서 생성한 커스텀 네이티브 템플릿 뷰 Factory 객체 전달
val nativeTemplateOptions = GfpNativeTemplateOptions.Builder()
.addNativeTemplateAdViewFactory(
GfpNativeTemplateOptions.DEFAULT_VISUAL_KEY, CustomNativeTemplateAdView.Factory()
)
.build()
val nativeSimpleAdOptions = GfpNativeSimpleAdOptions.Builder()
.setNativeTemplateOptions(nativeTemplateOptions)
...
.build()
val adLoader = GfpAdLoader.Builder(this, adParam)
...
.withNativeSimpleAd(nativeSimpleAdOptions) { nativeSimpleAd ->
...
}
.build()

4. 광고 요청 및 렌더링

위 과정까지 마쳤다면, 이후의 광고 요청 및 렌더링 그리고 광고 리소스 해제 같은 처리는 기존 네이티브 심플 광고와 사용이 동일합니다. 단, 네이티브 템플릿을 사용할 경우는 네이티브 광고가 네이티브 심플 광고 형식을 통해 처리되므로 GfpAdLoader 를 빌드하는 과정에서 네이티브 광고 로드시 필요한 아래와 같은 처리가 포함되지 말아야 합니다.

val nativeTemplateOptions = GfpNativeTemplateOptions.Builder()
.addNativeTemplateAdViewFactory(
GfpNativeTemplateOptions.DEFAULT_VISUAL_KEY, CustomNativeTemplateAdView.Factory()
)
.build()
val nativeSimpleAdOptions = GfpNativeSimpleAdOptions.Builder()
.setNativeTemplateOptions(nativeTemplateOptions)
...
.build()
val adLoader = GfpAdLoader.Builder(this, adParam)
...
.withNativeSimpleAd(nativeSimpleAdOptions) { nativeSimpleAd ->
...
}
.withNativeAd { nativeAd ->
// 네이티브 템플릿 사용시 withNativeAd() 는 포함되지 말아야 합니다.
}
.build()