본문 바로가기

[flutter, admob] 2024년 최신 애드몹 추가 다시 정리

ironwhale 2024. 12. 3.

1인 앱 개발로 돈 버는 법은 여러가지가 있지만 가장 쉽게 접근할 수 있는 것은 역시 애드몹(admob) 광고 수익일 것입니다. 저는 지금 7개의 앱을 출시했고 이 중 정기적으로 수익이 나는 앱은 1~2개 정도입니다. 이번에는 그동안 플러터도 애드몹도 업데이트가 되었기 때문에 초기설정부터 네이티브 애드몹 광고를 설정하는 방법을 정리해 보겠습니다. 

안드로이드 초기 설정

AndroidManifest는 android/app/src/main/ 경로에 있으며 여기서 네이티브 광고 검증기 끄기, 앱의 광고 아이디 설정 등을 할수 있습니다.  관련 코드는 아래와 같습니다.

  <!-- 애드몹 광고 설정 시작-->
        <meta-data
            android:name="cohttp://m.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-XXXXXXXXXXX~XXXXXXX" />

        <!--  네이티브 광고 검증기 끄기-->
        <meta-data
            android:name="cohttp://m.google.android.gms.ads.flag.NATIVE_AD_DEBUGGER_ENABLED"
            android:value="false" />

        <!--  없으면 에러남-->
        <property
            android:name="android.adservices.AD_SERVICES_CONFIG"
            android:resource="@xml/gma_ad_services_config"
            tools:replace="android:resource" />
  <!-- 광고 설정 끝-->


android13 버전 이상부터는 없으면 에러남 부분을 추가하여 별도의 권한 설정을 해주어야 합니다. 그래서 추가한 코드 이니 추가하시기 바랍니다.   


ios 초기 설정

Info.plist 파일에 아래와 같은 코드를 추가 해주었습니다. 위에 설정과 마찬가지로 앱의 아이디와 네이티브 광고 검증기를 끄는 설정을 하였습니다. 

<key>GADNativeAdValidatorEnabled</key>
<false/>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-XXXXXXXXXXX~XXXXXXX</string>
<key>SKAdNetworkItems</key>
     <array>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>cstr6suwn9.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>4fzdc2evr5.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>4pfyvq9l8r.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>2fnua5tdw4.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>ydx93a7ass.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>5a6flpkh64.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>p78axxw29g.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>v72qych5uu.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>ludvb6z3bs.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>cp8zw746q7.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>c6k4g5qg8m.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>s39g8k73mm.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>3qy4746246.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>3sh42y64q3.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>f38h382jlk.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>hs6bdukanm.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>prcb7njmu6.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>v4nxqhlyqp.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>wzmmz9fp6w.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>yclnxrl5pm.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>t38b2kh725.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>7ug5zh24hu.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>9rd848q2bz.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>y5ghdn5j9k.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>n6fk4nfna4.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>v9wttpbfk9.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>n38lu8286q.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>47vhws6wlr.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>kbd757ywx3.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>9t245vhmpl.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>a2p9lx4jpn.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>22mmun2rn5.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>4468km3ulz.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>2u9pt9hc89.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>8s468mfl3y.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>av6w8kgt66.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>klf5c3l5u5.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>ppxm28t8ap.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>424m5254lk.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>ecpz2srf59.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>uw77j35x4d.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>mlmmfzh3r3.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>578prtvx9j.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>4dzt52r2t5.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>gta9lk7p23.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>e5fvkxwrpn.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>8c4e2ghe7u.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>zq492l623r.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>3rd42ekr43.skadnetwork</string>
       </dict>
       <dict>
         <key>SKAdNetworkIdentifier</key>
         <string>3qcr597p9d.skadnetwork</string>
       </dict>
     </array>

네이티브 광고 위젯 만들기

이번에는 플러터에서 지원하는 네이티브 광고 템플릿을 이용해 간단하게 구현 해보았습니다. 나중에 좀 더 앱에 사람들이 많이 유입된다면 이전과 같은 방식으로 바꾸어 볼 생각 입니다만, 지금은 출시가 우선이니 간단한게 구현했습니다. 

 

이전과는 달리 광고가 로드되었는지 실패했는지 로딩 중인지 각 상태에 따라 UI를 다르게 적용하였고 실패를 했을 경우 다시 로드하는 코드를 추가하였습니다. 그리고 3번까지만 실패 후 로딩을 하고 그다음에는 그냥 광고가 잡아먹는 공간을 없애는 식으로 예전 방식에서 변화를 주었습니다. 

enum NativeAdStatus { load, fail, loading }

class NativeTemplateAds extends ConsumerStatefulWidget {
  const NativeTemplateAds({super.key, required this.nativeTemplateStyle});

  final TemplateType nativeTemplateStyle;

  @override
  ConsumerState createState() => _NativeTempleState();
}

class _NativeTempleState extends ConsumerState<NativeTemplateAds> {
  late NativeAd _ad;
  late final String _adUnitId;
  NativeAdStatus nativeAdStatus = NativeAdStatus.loading;

  int retryAttempt = 0;
  final int maxRetry = 3;

  void loadNativeAd() {
    _ad = NativeAd(
      nativeTemplateStyle:
          NativeTemplateStyle(templateType: widget.nativeTemplateStyle),
      adUnitId: _adUnitId,
      listener: NativeAdListener(
        onAdLoaded: (ad) {
          setState(() {
            nativeAdStatus = NativeAdStatus.load;
            retryAttempt = 0; // 성공 시 재시도 횟수 초기화
            logger.d("NativeAd loaded successfully.");
          });
        },
        onAdFailedToLoad: (ad, error) {
          ad.dispose();
          nativeAdStatus = NativeAdStatus.fail;
          if (retryAttempt < maxRetry) {
            retryAttempt++;
            Future.delayed(Duration(seconds: retryAttempt), loadNativeAd);
          }
        },
      ),
      request: const AdRequest(),
    );
    _ad.load();
  }

  @override
  void initState() {
    super.initState();
    // TODO: implement initState
    String whichOs = Platform.isAndroid ? "android" : "ios";
    _adUnitId = NATIVE_ID[whichOs]!;
    loadNativeAd();
  }

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

  @override
  Widget build(BuildContext context) {
    final BoxConstraints constraints =
        widget.nativeTemplateStyle == TemplateType.medium
            ? const BoxConstraints(
                maxWidth: double.infinity,
                maxHeight: 400,
                minWidth: 320,
                minHeight: 320)
            : const BoxConstraints(
                maxWidth: double.infinity,
                maxHeight: 120,
                minHeight: 90,
                minWidth: double.infinity);

    return Container(
      constraints: constraints,
      child: nativeAdStatus == NativeAdStatus.load
          ? AdWidget(ad: _ad)
          : nativeAdStatus == NativeAdStatus.loading
              ? const Center(
                  child: Text(
                    'Long Press = Delete',
                    style: TextStyle(fontSize: 16),
                  ),
                )
              : const SizedBox.shrink(),
    );
  }
}

관련링크

2024.01.05-[플러터, 애드몹] 네이티브 광고로 mx 플레이어와 같은 광고 구현하기- 1탄- 애드몹 기본설정하기

 

[플러터, 애드몹] 네이티브 광고로 mx 플레이어와 같은 광고 구현하기- 1탄- 애드몹 기본설정하기

애드몹(admob)에는 규격화된 광고 형태인 배너광고, 전면광고, 앱오닝광고, 리워드 광고가 있습니다. 하지만 앱의 UI와 잘 어울릴 수도 있지만 아직 규격화 되어 있어 어울리지 않을 수도 있고 커

jh-industry.tistory.com

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

 

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

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

jh-industry.tistory.com

 

댓글