본문 바로가기

[Flutter] admob(애드몹) - 앱 만들어서 네이티브 광고로 돈 벌기, 수익 내기(Native Ads)

ironwhale 2023. 3. 20.

애드몹(admob) 네이티브 광고(Native ads)란?

Admob에는 배너광고, 전면광고, 보상형광고 그리고 오늘의 주제인 네이티브 광고(Native Ads)가 있습니다. 애드몹 공식 사이트에는 네이티브 광고를 다음과 같이 설명하고 있습니다.

 

앱의 디자인과 스타일에 어울리게 설정할 수 있는 맞춤 광고입니다. 광고 배치 방법 및 위치를 정할 수 있으므로 광고 레이아웃과 앱 디자인 간의 일관성 유지가 가능합니다.

 

네 맞습니다. 애드몹 네이티브 광고는 우리가 만든 앱에 디자인과 유사하게 만들어 자연스럽게 융화되어  광고를 할 수 있는 맞춤형 애드몹 광고 중 하나입니다. 

 

 시작하기에 앞서 이 포스팅의 대상은 애드몹 배너 광고등 다른 애드몹 광고를 만들어본 경험이 어느 정도 있는 분들을 대상으로 쓰여있어 기본적인 애드몹 가입, 플러터에 앱 아이디 입력 등 기본적인 Android 세팅하는 법 광고 ID 입력하는 법, admob 플러그인인 Google Mobile Ads 설치, 초기화 등 기본적인 내용을 생략되어 있습니다. 


구글에서 말하는 네이티브 광고의 장점은 아래와 같습니다. 

- 일반적으로 네이티브 광고는 인앱 광고 시 디자인이 가장 자연스럽습니다.
- 연구에 따르면 사용자는 배너 광고보다 네이티브 광고가 덜 산만하고
   전반적으로 더 일관된 경험을 제공한다고 합니다.
- 다른 광고 형식보다 네이티브 광고를 앱에 더 유연하게 통합할 수 있습니다.
출처: https://admob.google.com/intl/ko/home/resources/native-ads-playbook/

 

배너광고와 네이티브 광고 비교 

 아래 비교 캡쳐 본에서 보이듯이 배너광고는 몬가 붕 떠있는 느낌인데 반해 네이티브 광고는 훨씬 기존 디자인(UI)에 잘 어울리는 것을 보실 수 있습니다. 다만 광고인 것을 숨기면 안되기 때문에 광고임을 표시하는 그림이 왼쪽에 떠있습니다.

배너광고, banner ads, admob
네이티브 광고, native ads, admob
 배너광고(banner ads) 네이티브 광고(Native ads)

Flutter로 네이티브광고 하기

 구글에서 제공하는 공식 예제 중 안드로이드에 관련된 부분만 해보도록 하겠습니다. 기존 배너 광고는 별도의 설정없이 손쉽게 만들 수 있었는데 네이티브 광고는 플러터와 안드로이드를 연결해주는 설정과정이 필요합니다. 그래서 조금 어렵게 느껴질 수 있기만 구글에서 제공하는 예제가 있기 때문에 차근차근 하다보면 저처럼 하실수 있을실 겁니다. 


 순서

1.  my_native_ad.xml 만들기 

2.  ListTileNativeAdFactory.kt 만들기

3.  MainActivity.kt 수정하기

4. Native 광고 위젯 만들기


1.  my_native_ad.xml 만들기 

  •  - 우선 프로젝트 폴더/android/app/arc/main/res 폴더로 갑니다
  •  res폴더에 layout 폴더를 만들고 my_native_ad.xml 이라는 파일을 만듭니다. 

xml 파일에 아래의 코드를 입력합니다. 공식 예제에 나온 코드를 그대로 복사 붙여넣기 하였습니다.  

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.ads.nativead.NativeAdView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_small"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#F19938"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:textSize="12sp" />

        <ImageView
            android:id="@+id/iv_list_tile_native_ad_icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:scaleType="fitXY"
            tools:background="#EDEDED" />

        <TextView
            android:id="@+id/tv_list_tile_native_ad_attribution_large"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:background="#F19938"
            android:gravity="center"
            android:text="Ad"
            android:textColor="#FFFFFF"
            android:visibility="invisible" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="80dp"
            android:layout_marginLeft="80dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_list_tile_native_ad_headline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#000000"
                android:textSize="16sp"
                tools:text="Headline" />

            <TextView
                android:id="@+id/tv_list_tile_native_ad_body"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:lines="1"
                android:maxLines="1"
                android:textColor="#828282"
                android:textSize="14sp"
                tools:text="body" />

        </LinearLayout>

    </FrameLayout>

</com.google.android.gms.ads.nativead.NativeAdView>

 


2.  ListTileNativeAdFactory.kt 만들기

  • 프로젝트 폴더/android/app/arc/main/kotlin/패키지경로(ex com.test.train) 갑니다.
  • ListTileNativeAdFactory.kt를 만듭니다.

그리고 아래 코드를 입력합니다. 

package 패키지

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
import com.honeybee.moor_train.R
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin

class ListTileNativeAdFactory(val context: Context) : GoogleMobileAdsPlugin.NativeAdFactory {

    override fun createNativeAd(
        nativeAd: NativeAd,
        customOptions: MutableMap<String, Any>?
    ): NativeAdView {
        val nativeAdView = LayoutInflater.from(context)
            .inflate(R.layout.my_native_ad, null) as NativeAdView

        with(nativeAdView) {
            val attributionViewSmall =
                findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_small)
            val attributionViewLarge =
                findViewById<TextView>(R.id.tv_list_tile_native_ad_attribution_large)

            val iconView = findViewById<ImageView>(R.id.iv_list_tile_native_ad_icon)
            val icon = nativeAd.icon
            if (icon != null) {
                attributionViewSmall.visibility = View.VISIBLE
                attributionViewLarge.visibility = View.INVISIBLE
                iconView.setImageDrawable(icon.drawable)
            } else {
                attributionViewSmall.visibility = View.INVISIBLE
                attributionViewLarge.visibility = View.VISIBLE
            }
            this.iconView = iconView

            val headlineView = findViewById<TextView>(R.id.tv_list_tile_native_ad_headline)
            headlineView.text = nativeAd.headline
            this.headlineView = headlineView

            val bodyView = findViewById<TextView>(R.id.tv_list_tile_native_ad_body)
            with(bodyView) {
                text = nativeAd.body
                visibility = if (nativeAd.body.isNullOrEmpty()) View.INVISIBLE else View.VISIBLE
            }
            this.bodyView = bodyView

            setNativeAd(nativeAd)
        }

        return nativeAdView
    }
}

3.  MainActivity.kt 수정하기

  • 프로젝트 폴더/android/app/arc/main/kotlin/패키지경로(ex com.test.train) 갑니다.
  • MainActivity.kt을 엽니다.

그리고 아래 처럼 코드를 수정합니다. 여기 눈여겨 보실 점은 flutterEngine 옆에 adFractoryExample 입니다.

이것은 팩토리 아이디로 플러터(flutter)에 가서 애드몹 네이티브광고를 구현할 때 안드로이드와 플러터를 연결시켜줄 키워드 입니다. 

package 패키지 명

import com.codelab.flutter.admobinlineads.ListTileNativeAdFactory
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin

class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        GoogleMobileAdsPlugin.registerNativeAdFactory(
            flutterEngine, "adFactoryExample", ListTileNativeAdFactory(context)
        )
    }

    override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine)

        GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, "adFactoryExample")
    }
}

여기 까지가 플러터로 네이티브광고를 구현하기 위한 기본적인 설정입니다.  이제 플러터로 가서 본격적으로 네이티브 광고를 구현해보도록 하겠습니다. 


4. 애드몹 네이티브 광고(Admob Native ads) 구현하기 

위에 복잡한 설정이 끝나면 이제 플러터로 돌아와 google_mobile_ads 패키지를 이용해 기존 애드몹 배너광고를 구현하듯이 애드몹 네이티브 광고를 구현하면 됩니다. 저는 각각의 페이지 마다 배너 광고 위젯을 각각 만들지 않고 광고 위젯 하나 만들어서 각각의 페이지에 추가하는 방식을 사용하고 있습니다.

 

중요부분

위에서 설정한 factoryId가 여기에 쓰이네요 이렇게 NativeAd 인스턴스를 만들어서 _ad.load() 를 해주면 애드몹 광고를 우리의 앱에서 보여줄 수 있게 됩니다. 

_ad = NativeAd(
      adUnitId: NATIVE_ID[os == TargetPlatform.iOS ? 'ios' : 'android']!,
      factoryId: "adFactoryExample",
      request: const AdRequest(),
      listener: NativeAdListener(onAdLoaded: (ad) {
        setState(() {
          _ad = ad as NativeAd;
          isLoaded = true;
        });
      }, onAdFailedToLoad: (ad, error) {
        ad.dispose();
      }),
    );
    _ad.load();

 

 

전체 애드몹 네이티브 광고 위젯 전체 코드

import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

import '../util/logger.dart';
import 'ad_id.dart';

class NativeAds extends StatefulWidget {
  NativeAds({Key? key}) : super(key: key);

  @override
  State<NativeAds> createState() => _NativeAdsState();
}

class _NativeAdsState extends State<NativeAds> {
  late NativeAd _ad;
  TargetPlatform? os;
  bool isLoaded = false;

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    os = Theme.of(context).platform;
    logger.d("native didChangeDependencies");
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _ad.dispose();
  }

  @override
  void initState() {
    super.initState();

    logger.d("native init");
    _ad = NativeAd(
      adUnitId: NATIVE_ID[os == TargetPlatform.iOS ? 'ios' : 'android']!,
      factoryId: "adFactoryExample",
      request: const AdRequest(),
      listener: NativeAdListener(onAdLoaded: (ad) {
        setState(() {
          _ad = ad as NativeAd;
          isLoaded = true;
        });
      }, onAdFailedToLoad: (ad, error) {
        ad.dispose();
      }),
    );
    _ad.load();
  }

  @override
  Widget build(BuildContext context) {
    if (isLoaded == true) {
      logger.d("native build");
      return SizedBox(height: 60, child: AdWidget(ad: _ad));
    } else {
      return const SizedBox(height: 0);
    }
  }
}

Admob Native ads 구현을 마치며

기존의 애드몹(Admob) 배너 광고와 달리  네이티브 광고는 구현하는 과정이 어렵지만 자연스러운 디자인으로 제가 만든 앱이 녹아든다는 장점이 있습니다.  

 

위의 내용은 거의 구글 공식 가이드를 복사 붙여넣기 하였지만 더 공부를 한다면 layout을 이용해 제가 원하는 디자인의 애드몹광고를 하여 자연스럽게 광고인듯 아닌듯 할 수 도 있을거 같습니다. 

 

긴글 읽어주셔서 감사합니다. 


참고자료

- 구글 공식 가이드

https://codelabs.developers.google.com/codelabs/admob-inline-ads-in-flutter#7

 

Adding an AdMob banner and native inline ads to a Flutter app  |  Google Codelabs

In this codelab, you’ll learn how to add AdMob inline ads (Banner and Native) to a flutter app.

codelabs.developers.google.com

https://admob.google.com/intl/ko/home/resources/native-ads-playbook/

 

Google AdMob 네이티브 광고 플레이북 | Google AdMob

네이티브 광고를 사용하면 앱에 표시되는 광고를 맞춤설정할 수 있습니다. 권장사항을 알아보고 네이티브 광고를 앱에 사용해야 하는 이유를 확인해 보세요.

admob.google.com

- 코딩파파님의 애드몹 유료 강의 

 

댓글