[React Native] AppLovin MAX 연동하기

aborile·2023년 9월 22일
0

React Native

목록 보기
8/8
post-thumbnail

AdMob에서 광고 게재 제한의 철퇴를 받았다... 언제까지나 게재 제한인 상태로 광고를 아예 죽여둘 수는 없어서 다른 광고 서비스를 찾아보다가, 시행착오 끝에 AppLovin을 적용하기로 결정했다.

후보로 고려했던 서비스로는 Meta Audience Network, IronSource, Unity Ads, AppLovin이 있는데, 이 중 React Native 연동이 가장 용이해 보여 AppLovin으로 최종 결정되었다.

AppLovin MAX 연동

React Native 라이브러리 설치

yarn install react-native-applovin-max

Ad Review 설정

MAX Ad Review는 유저의 더 나은 광고 경험을 위한 서비스이다. 어플리케이션에 표시된 광고를 보고 문제가 있는 광고 소재를 추적할 수 있다.

Android

android/build.gradle

buildscript {
    repositories {
        maven { url 'https://artifacts.applovin.com/android' }
    }
    dependencies {
        classpath "com.applovin.quality:AppLovinQualityServiceGradlePlugin:+"
    }
}

android/app/build.gradle

apply plugin: 'applovin-quality-service'
applovin {
    apiKey "YOUR_AD_REVIEW_KEY_HERE"    
}

iOS

AppLovinQualityServiceSetup-ios.rb 파일을 다운로드 받은 뒤 ios/ 폴더 안에 넣고 해당 디렉토리 내에서 다음 명령어를 실행한다. (이 파일은 AppLovin 대시보드에 로그인되어 있어야 정상적으로 다운로드 받을 수 있다고 한다.)

ruby AppLovinQualityServiceSetup-ios.rb

Initialize SDK

import AppLovinMAX from "react-native-applovin-max";

AppLovinMAX.initialize("YOUR_SDK_KEY_HERE").then(configuration => {
  // SDK is initialized, start loading ads
}).catch(error => {
  // Failed to initialize SDK
});

더 나은 사용자 경험을 위해서는 광고 로딩에 걸리는 시간을 최대한 사용자에게 보이지 않게 해야 한다. 따라서 네트워크에 광고를 캐시할 수 있는 시간을 최대한 제공하기 위해 AppLovin SDK의 초기화는 앱이 시작할 때 이루어지는 것이 좋다고 한다.

iOS 15 Global SKAdNetwork

iOS 15부터 Apple은 개발자가 SKAdNetwork 설치 포스트백 사본을 원하는 엔드포인트로 보낼 수 있도록 허용한다. MAX는 개발자가 한 곳에서 모든 네트워크 파트너의 SKAdNetwork 데이터에 액세스할 수 있도록 글로벌 SKAdNetwork 보고서를 제공하고, MAX > 중재 > 분석 > 글로벌 SKA 보고서에서 확인할 수 있다고 한다.

	<key>NSAdvertisingAttributionReportEndpoint</key>
	<string>https://postbacks-app.com</string>

MAX 광고 적용

설치까지 쉽게한 건 좋았는데 최대의 문제점... react-native-applovin-max 라이브러리는 타입이 아예 없다.

AppLovin을 적용하는 게 맞나 싶은 최대의 시련이었지만 일단은 참아내고 임시로 index.d.ts에 정의를 추가해서 import 시 타입 에러가 뜨지 않게 조치했다. 추후에 시간이 날 때 사용하는 함수 위주로 천천히 타이핑을 추가해 보려 한다.

index.d.ts

declare module "react-native-applovin-max";

배너 광고

import AppLovinMAX from "react-native-applovin-max";

<AppLovinMAX.AdView
  adUnitId="BANNER_AD_UNIT_ID"
  adFormat={AppLovinMAX.AdFormat.MREC}
  onAdLoadFailed={onApplovinFailedToLoad}
/>

배너 광고의 경우 컴포넌트 형태로 제공한다. 광고 id인 adUnitId와 사이즈, 그리고 실패 시 핸들링 함수를 넘겨 주면 기본적으로 구현이 가능하다.

전면 광고

import AppLovinMAX from "react-native-applovin-max";

// AppLovin 광고 로드
useEffect(() => {
  function loadInterstitial() {
    AppLovinMAX.loadInterstitial("INTERSTITIAL_AD_UNIT_ID");
  }

  AppLovinMAX.addInterstitialAdFailedToDisplayEventListener((adInfo) => {
    console.log(adInfo.adUnitId);
    loadInterstitial();
  });
  AppLovinMAX.addInterstitialHiddenEventListener(() => {
    loadInterstitial();
  });

  loadInterstitial();

  return () => {
    AppLovinMAX.removeInterstitialAdFailedToDisplayEventListener();
    AppLovinMAX.removeInterstitialHiddenEventListener();
  };
}, []);

const showInterstitialAd = useCallback(async () => {
  const isAppLovinLoaded = await AppLovinMAX.isInterstitialReady("INTERSTITIAL_AD_UNIT_ID");
  if (isAppLovinLoaded) {
    AppLovinMAX.showInterstitial("INTERSTITIAL_AD_UNIT_ID");
  }
}, []);

전면 광고의 구현은 약간 더 복잡하다. 로딩은 AppLovinMAX.loadInterstitial() 함수로, 광고 표시는 AppLovinMAX.showInterstitial() 함수로 하면 되지만 이외의 핸들링은 전부 이벤트 리스너를 달아주어야 한다.

addInterstitialAdFailedToDisplayEventListener, addInterstitialHiddenEventListener 등 다양한 이벤트 리스너를 제공하는데, 별도로 정리되어 있는 문서도 없고 타이핑도 없어서 코드를 일일이 읽어가며 필요한 리스너를 찾는 과정이 조금 힘들었다. ^^;

대부분의 리스너에서는 adInfo 인자를 넘겨 주어서 이벤트를 발생시킨 광고의 adUnitId가 무엇인지, 어느 네트워크인지 등을 알 수 있다.

AdInfo Type

interface AdInfoType {
  rewardLabel: string;
  networkName: string;
  waterfall: {
    testName: string;
    latencyMillis: number;
    name: string;
    networkResponses: {
      mediatedNetwork: {
        adapterClassName: string;
        name: string;
        sdkVersion: string;
        adapterVersion: string;
      };
      latencyMillis: number;
      credentials: {
        placement_id: string;
        app_id: string;
        always_reward_user: boolean;
      };
      adLoadState: number;
    }[];
  };
  dspName: string;
  creativeId: string;
  placement: string;
  revenue: number;
  adUnitId: string;
  rewardAmount: string;
}

리스너 목록

addInterstitialAdLoadedEventListener
removeInterstitialAdLoadedEventListener

addInterstitialAdLoadFailedEventListener
removeInterstitialAdLoadFailedEventListener

addInterstitialAdClickedEventListener
removeInterstitialAdClickedEventListener

addInterstitialAdDisplayedEventListener
removeInterstitialAdDisplayedEventListener

addInterstitialAdFailedToDisplayEventListener
removeInterstitialAdFailedToDisplayEventListener

addInterstitialHiddenEventListener
removeInterstitialHiddenEventListener

addInterstitialAdRevenuePaidListener
removeInterstitialAdRevenuePaidListener

리워드형 광고

import AppLovinMAX from "react-native-applovin-max";

// AppLovin 광고 로드
useEffect(() => {
  function loadRewardedAd() {
    AppLovinMAX.loadRewardedAd("REWARDED_AD_UNIT_ID");
  }

  AppLovinMAX.addRewardedAdLoadedEventListener(() => {
    // Rewarded ad is ready to show. AppLovinMAX.isInterstitialReady(REWARDED_AD_UNIT_ID) now returns 'true'
    setIsAppLovinLoaded(true);
  });
  AppLovinMAX.addRewardedAdFailedToDisplayEventListener(() => {
    loadRewardedAd();
  });
  AppLovinMAX.addRewardedAdHiddenEventListener(() => {
    loadRewardedAd();
  });

  loadRewardedAd();

  return () => {
    AppLovinMAX.removeRewardedAdLoadedEventListener();
    AppLovinMAX.removeRewardedAdFailedToDisplayEventListener();
    AppLovinMAX.removeRewardedAdHiddenEventListener();
  };
}, []);

const showRewardedAd = useCallback(async () => {
  if (isAppLovinLoaded) {
    AppLovinMAX.showRewardedAd("REWARDED_AD_UNIT_ID", null, customData);
  }
}, [isAppLovinLoaded, customData]);

리워드형 광고의 구현은 전면 광고와 비슷하다. AppLovinMAX.loadRewardedAd 함수와 AppLovinMAX.showRewardedAd 함수로 광고를 보여줄 수 있고, 마찬가지로 이벤트 리스너를 통해 핸들링해야 한다.

S2S Rewarded Callbacks

위의 예시 코드를 보면 알 수 있지만 showRewardedAd 함수의 세번째 인자로 customData를 넘길 수 있다. 해당 함수의 정확한 타입은 다음과 같은데, 서버 단의 인증이나 처리가 필요한 경우 customData를 넘겨서 핸들링할 수 있다. showInterstitial 함수도 동일한 타입을 가진다.

showRewardedAd(adUnitId: string, placement?: string | null, customData?: string): void

AppLovin MAX에서는 서버 단으로 callback을 넘겨 주는 것을 S2S Rewarded Callbacks라고 부르는데, 광고를 생성하거나 수정할 때에 광고 타입 선택지 내 Rewarded 버튼 우측의 Add S2S Reward Callback 버튼을 눌러 Server Side Callback URL과 리워드 형식을 설정할 수 있다.

S2S Callback API 공식 가이드를 보면 URL 예시를 https://myrewardedserver.com/rewards?idfa={IDFA}&user_id={USER_ID}&event={EVENT_ID}&token={EVENT_TOKEN}로 기재해 두었는데, 예시에서 확인할 수 있듯 정해진 키워드(매크로)를 URL에 지정하면 callback이 실행될 때 실제 유저의 정보를 넘겨주어 서버 단에서 로직을 처리할 때에 사용할 수 있다.

리스너 목록

addRewardedAdLoadedEventListener
removeRewardedAdLoadedEventListener

addRewardedAdLoadFailedEventListener
removeRewardedAdLoadFailedEventListener

addRewardedAdClickedEventListener
removeRewardedAdClickedEventListener

addRewardedAdDisplayedEventListener
removeRewardedAdDisplayedEventListener

addRewardedAdFailedToDisplayEventListener
removeRewardedAdFailedToDisplayEventListener

addRewardedAdHiddenEventListener
removeRewardedAdHiddenEventListener

addRewardedAdReceivedRewardEventListener
removeRewardedAdReceivedRewardEventListener

addRewardedAdRevenuePaidListener
removeRewardedAdRevenuePaidListener

MAX Mediation

Android

위 Mediation 가이드에 들어가서 연동할 네트워크를 선택한 뒤 해당하는 dependencies를 추가해 주면 된다. 나의 경우 구글 AdMob만 우선 진행하였다.

android/app/build.gradle

dependencies {
    implementation "com.applovin:applovin-sdk:+"
    implementation "com.applovin.mediation:google-adapter:+"
}

AndroidManifest에 Google AdMob / Google Ad Manager App ID를 추가해야 한다.

android/app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest  >
    <application ...>
      <!-- AppLovin MAX - AdMob mediation integration -->
      <meta-data
          android:name="com.google.android.gms.ads.APPLICATION_ID"
          android:value="ADMOB_APPLICATION_ID"/>
    </application>
</manifest>

iOS

iOS 역시 마찬가지로 위 Mediation 가이드에 들어가서 연동할 네트워크를 선택한 뒤 해당하는 dependencies를 Podfile 내에 추가해 주면 된다.

ios/Podfile

target 'YOUR_PROJECT_NAME' do
  # AppLovin Mediated Networks
  pod 'AppLovinSDK'
  pod 'AppLovinMediationGoogleAdapter'
  # ...

또한 Info.plist 내에 Google AdMob / Google Ad Manager App ID를 추가해야 한다. 만약 App Transport Security(ATS)가 설정되어 있지 않다면 NSAppTransportSecurityNSAllowsArbitraryLoadsYES로 추가하여 설정해 주어야 한다.

그리고 SKAdNetwork를 구성해아 하는데, SKAdNetwork Info.plist Generator에서 해당하는 네트워크를 선택하면 SKAdNetworkIdentifier lists를 자동으로 생성해 주니 이를 복사해서 Info.plist 내에 넣으면 된다.

Info.plist

    ...
	<key>GADApplicationIdentifier</key>
	<string>ADMOB_APPLICATION_ID</string>
    
	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
    	...
	</dict>
    
	<key>SKAdNetworkItems</key>
	<array>
    	{...SKAdNetworkIdentifiers}
	</array>

References

profile
기록하고 싶은 것을 기록하는 저장소

0개의 댓글