앱 오프닝 광고

이 가이드는 Google 모바일 광고 Android SDK를 사용하여 앱 오프닝 광고를 통합하려는 게시자를 대상으로 합니다.

앱 오프닝 광고는 앱 로드 화면을 통해 수익을 창출하려는 게시자를 위한 특별한 광고 형식입니다. 앱 오프닝 광고는 언제든지 닫을 수 있으며, 사용자가 앱을 포그라운드로 가져올 때 표시되도록 설계되었습니다.

앱 오프닝 광고는 사용자가 앱을 사용 중임을 알 수 있도록 작은 브랜딩 영역을 자동으로 표시합니다. 앱 오프닝 광고의 예는 다음과 같습니다.

개략적으로 다음과 같은 중요한 단계가 있습니다.

  1. Application 클래스를 확장하여 Google 모바일 광고 SDK를 초기화합니다.
  2. 광고를 게재하기 전에 로드하는 유틸리티 클래스를 만듭니다.
  3. 광고를 로드합니다.
  4. ActivityLifecycleCallbacks. 듣기
  5. 광고를 표시하고 콜백을 처리합니다.
  6. LifecycleObserver 인터페이스를 구현하고 등록하여 포그라운드 이벤트 중에 광고를 표시합니다.

기본 요건

  • Google 모바일 광고 SDK 19.4.0 이상
  • 시작 가이드의 설정 안내를 따릅니다.

항상 테스트 광고로 테스트

앱을 제작하고 테스트할 때는 운영 중인 실제 광고 대신 테스트 광고를 사용하세요. 이렇게 하지 않으면 계정이 정지될 수 있습니다.

테스트 광고를 로드하는 가장 쉬운 방법은 앱 오프닝 광고 전용 테스트 광고 단위 ID를 사용하는 것입니다.

/6499/example/app-open

이 ID는 모든 요청에 대해 테스트 광고를 반환하도록 특별히 구성되었으며, 코딩, 테스트 및 디버깅 중에 앱에서 자유롭게 사용할 수 있습니다. 앱을 게시하기 전에 이 ID를 자체 광고 단위 ID로 바꿔야 합니다.

모바일 광고 SDK의 테스트 광고가 작동하는 방식을 자세히 알아보려면 테스트 광고를 참고하세요.

Application 클래스 확장

Application 클래스를 확장하는 새 클래스를 만들고 다음 코드를 추가하여 Google 모바일 광고 SDK를 초기화합니다.

Java

/** Application class that initializes, loads and show ads when activities change states. */
public class MyApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    MobileAds.initialize(
        this,
        new OnInitializationCompleteListener() {
          @Override
          public void onInitializationComplete(InitializationStatus initializationStatus) {}
        });
  }
}

Kotlin

/** Application class that initializes, loads and show ads when activities change states. */
class MyApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    MobileAds.initialize(this) {}
  }
}

이렇게 하면 SDK가 초기화되고 나중에 앱 포그라운드 이벤트에 등록할 스켈레톤이 제공됩니다.

다음으로, AndroidManifest.xml에 다음 코드를 추가합니다.

<application
    android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>

실제 패키지 이름을 참조해야 합니다.

유틸리티 클래스 구현

광고는 빠르게 게재되어야 하므로 광고를 게재하기 전에 로드하는 것이 좋습니다. 이렇게 하면 사용자가 앱을 시작하자마자 바로 광고를 게재할 수 있습니다. 광고를 표시해야 하기 전에 미리 광고를 요청하는 유틸리티 클래스를 구현합니다.

MyApplication 클래스 내에 AppOpenAdManager라는 새 클래스를 만들고 다음과 같이 작성합니다.

Java

public class MyApplication extends Application {
  ...
  /** Inner class that loads and shows app open ads. */
  private class AppOpenAdManager {
    private static final String LOG_TAG = "AppOpenAdManager";
    private static final String AD_UNIT_ID = "ca-app-pub-3940256099942544/9257395921";

    private AppOpenAd appOpenAd = null;
    private boolean isLoadingAd = false;
    private boolean isShowingAd = false;

    /** Constructor. */
    public AppOpenAdManager() {}

    /** Request an ad. */
    private void loadAd(Context context) {
      // We will implement this below.
    }

    /** Check if ad exists and can be shown. */
    private boolean isAdAvailable() {
      return appOpenAd != null;
    }
  }
}

Kotlin

private const val String LOG_TAG = "AppOpenAdManager"
private const val String AD_UNIT_ID = "ca-app-pub-3940256099942544/9257395921"

public class MyApplication extends Application {
  ...
  /** Inner class that loads and shows app open ads. */
  private inner class AppOpenAdManager {
    private var appOpenAd: AppOpenAd? = null
    private var isLoadingAd = false
    var isShowingAd = false

    /** Request an ad. */
    fun loadAd(context: Context) {
      // We will implement this below.
    }

    /** Check if ad exists and can be shown. */
    private fun isAdAvailable(): Boolean {
      return appOpenAd != null
    }
  }
}

이제 유틸리티 클래스가 있으므로 MyApplication 클래스에서 이를 인스턴스화할 수 있습니다.

Java

public class MyApplication extends Application {

  private AppOpenAdManager appOpenAdManager;

  @Override
  public void onCreate() {
    super.onCreate();
    MobileAds.initialize(
        this,
        new OnInitializationCompleteListener() {
          @Override
          public void onInitializationComplete(InitializationStatus initializationStatus) {}
        });
    appOpenAdManager = new AppOpenAdManager(this);
  }
}

Kotlin

class MyApplication : Application() {

  private lateinit var appOpenAdManager: AppOpenAdManager

  override fun onCreate() {
    super.onCreate()
    MobileAds.initialize(this) {}
    appOpenAdManager = AppOpenAdManager()
  }
}

광고 로드

다음 단계는 loadAd() 메서드를 작성하는 것입니다.

Java

private class AppOpenAdManager {
  ...
  /** Request an ad. */
  public void loadAd(Context context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return;
    }

    isLoadingAd = true;
    AdRequest request = new AdRequest.Builder().build();
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        new AppOpenAdLoadCallback() {
          @Override
          public void onAdLoaded(AppOpenAd ad) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.");
            appOpenAd = ad;
            isLoadingAd = false;
          }

          @Override
          public void onAdFailedToLoad(LoadAdError loadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.getMessage());
            isLoadingAd = false;
          }
        });
  }
  ...
}

Kotlin

private inner class AppOpenAdManager {
  ...
  /** Request an ad. */
  fun loadAd(context: Context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return
    }

    isLoadingAd = true
    val request = AdRequest.Builder().build()
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        object : AppOpenAdLoadCallback() {

          override fun onAdLoaded(ad: AppOpenAd) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.")
            appOpenAd = ad
            isLoadingAd = false
          }

          override fun onAdFailedToLoad(loadAdError: LoadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.message)
            isLoadingAd = false;
          }
        })
  }
  ...
}

AppOpenAdLoadCallback에는 AppOpenAd 로드가 완료될 때 호출되는 메서드가 있습니다.

현재 활동 추적

광고를 표시하려면 Activity 컨텍스트가 필요합니다. 사용자가 사용 중인 최신 활동을 추적하려면 Application 클래스에서 Application.ActivityLifecycleCallbacks를 구현합니다.

Java

public class MyApplication extends Application implements ActivityLifecycleCallbacks {

  private Activity currentActivity;

  ...

  /** ActivityLifecycleCallback methods. */
  @Override
  public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

  @Override
  public void onActivityStarted(Activity activity) {
    // Updating the currentActivity only when an ad is not showing.
    if (!appOpenAdManager.isShowingAd) {
      currentActivity = activity;
    }
  }

  @Override
  public void onActivityResumed(Activity activity) {}

  @Override
  public void onActivityStopped(Activity activity) {}

  @Override
  public void onActivityPaused(Activity activity) {}

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}

  @Override
  public void onActivityDestroyed(Activity activity) {}
}

Kotlin

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

  private var currentActivity: Activity? = null

  ...

  /** ActivityLifecycleCallback methods. */
  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

  override fun onActivityStarted(activity: Activity) {
    // Updating the currentActivity only when an ad is not showing.
    if (!appOpenAdManager.isShowingAd) {
      currentActivity = activity
    }
  }

  override fun onActivityResumed(activity: Activity) {}

  override fun onActivityPaused(activity: Activity) {}

  override fun onActivityStopped(activity: Activity) {}

  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

  override fun onActivityDestroyed(activity: Activity) {}
}

현재 활동을 추적하여 광고를 표시하는 데 사용할 컨텍스트를 확보할 수 있습니다. 이제 registerActivityLifecycleCallbacks 메서드를 사용하여 이 인터페이스를 등록해야 합니다.

Java

public class MyApplication extends Application {
  ...
  @Override
  public void onCreate() {
    super.onCreate();
    this.registerActivityLifecycleCallbacks(this);
    MobileAds.initialize(
        this,
        new OnInitializationCompleteListener() {
          @Override
          public void onInitializationComplete(InitializationStatus initializationStatus) {}
        });
    appOpenAdManager = new AppOpenAdManager();
  }
}

Kotlin

class MyApplication : Application() {
  ...
  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(this)
    MobileAds.initialize(this) {}
    appOpenAdManager = AppOpenAdManager()
  }
}

registerActivityLifecycleCallbacks를 사용하면 모든 Activity 이벤트를 수신 대기할 수 있습니다. 활동이 시작되고 소멸될 때를 수신 대기하면 현재 Activity에 대한 참조를 추적할 수 있으며 이 참조는 앱 오프닝 광고를 표시하는 데 사용됩니다.

광고 표시 및 전체 화면 콜백 이벤트 처리

다음 코드는 광고를 게재한 후 다시 로드해야 하는 경우를 보여줍니다.

Java

public class MyApplication extends Application {
  ...
  /** Interface definition for a callback to be invoked when an app open ad is complete. */
  public interface OnShowAdCompleteListener {
    void onShowAdComplete();
  }

  private class AppOpenAdManager {
    ...

    /** Shows the ad if one isn't already showing. */
    public void showAdIfAvailable(
        @NonNull final Activity activity,
        @NonNull OnShowAdCompleteListener onShowAdCompleteListener){
      // If the app open ad is already showing, do not show the ad again.
      if (isShowingAd) {
        Log.d(LOG_TAG, "The app open ad is already showing.");
        return;
      }

      // If the app open ad is not available yet, invoke the callback then load the ad.
      if (!isAdAvailable()) {
        Log.d(LOG_TAG, "The app open ad is not ready yet.");
        onShowAdCompleteListener.onShowAdComplete();
        loadAd(activity);
        return;
      }

      appOpenAd.setFullScreenContentCallback(
          new FullScreenContentCallback {

            @Override
            public void onAdDismissedFullScreenContent() {
              // Called when fullscreen content is dismissed.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, "Ad dismissed fullscreen content.");
              appOpenAd = null;
              isShowingAd = false;

              onShowAdCompleteListener.onShowAdComplete();
              loadAd(activity);
            }

            @Override
            public void onAdFailedToShowFullScreenContent(AdError adError) {
              // Called when fullscreen content failed to show.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, adError.getMessage());
              appOpenAd = null;
              isShowingAd = false;

              onShowAdCompleteListener.onShowAdComplete();
              loadAd(activity);
            }

            @Override
            public void onAdShowedFullScreenContent() {
              // Called when fullscreen content is shown.
              Log.d(LOG_TAG, "Ad showed fullscreen content.");
            }
          });
      isShowingAd = true;
      appOpenAd.show(activity);
    }
    ...
  }
}

Kotlin

class MyApplication : Application() {
  ...
  /** Interface definition for a callback to be invoked when an app open ad is complete. */
  interface OnShowAdCompleteListener {
    fun onShowAdComplete()
  }

  private inner class AppOpenAdManager {
    ...

    /** Shows the ad if one isn't already showing. */
    fun showAdIfAvailable(
        activity: Activity,
        onShowAdCompleteListener: OnShowAdCompleteListener) {
      // If the app open ad is already showing, do not show the ad again.
      if (isShowingAd) {
        Log.d(LOG_TAG, "The app open ad is already showing.")
        return
      }

      // If the app open ad is not available yet, invoke the callback then load the ad.
      if (!isAdAvailable()) {
        Log.d(LOG_TAG, "The app open ad is not ready yet.")
        onShowAdCompleteListener.onShowAdComplete()
        loadAd(activity)
        return
      }

      appOpenAd?.setFullScreenContentCallback(
          object : FullScreenContentCallback() {

            override fun onAdDismissedFullScreenContent() {
              // Called when full screen content is dismissed.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, "Ad dismissed fullscreen content.")
              appOpenAd = null
              isShowingAd = false

              onShowAdCompleteListener.onShowAdComplete()
              loadAd(activity)
            }

            override fun onAdFailedToShowFullScreenContent(adError: AdError) {
              // Called when fullscreen content failed to show.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, adError.message)
              appOpenAd = null
              isShowingAd = false

              onShowAdCompleteListener.onShowAdComplete()
              loadAd(activity)
            }

            override fun onAdShowedFullScreenContent() {
              // Called when fullscreen content is shown.
              Log.d(LOG_TAG, "Ad showed fullscreen content.")
            }
          })
      isShowingAd = true
      appOpenAd?.show(activity)
    }
    ...
  }
}

FullScreenContentCallback는 광고가 표시되거나 표시되지 않을 때, 광고가 닫히는 등의 이벤트를 처리합니다. 사용자가 앱 오프닝 광고를 클릭하여 앱을 떠났다가 다시 돌아오면 다른 앱 오프닝 광고가 표시되지 않도록 합니다.

앱 포그라운드 이벤트 수신 대기

gradle 파일에 라이브러리 추가

앱 포그라운드 이벤트에 관한 알림을 받으려면 LifecycleObserver를 등록해야 합니다. 먼저 LifecycleObserver 라이브러리를 포함하도록 애플리케이션 수준 build.gradle 파일을 수정합니다.

apply plugin: 'com.android.application'

dependencies {
   implementation 'androidx.appcompat:appcompat:1.3.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

   implementation 'com.google.android.gms:play-services-ads:23.0.0'

   def lifecycle_version = "2.3.1"
   implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
   implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
   annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}

LifecycleObserver 인터페이스 구현

LifecycleObserver 인터페이스를 구현하여 Application 클래스에서 포그라운드 이벤트를 수신 대기할 수 있습니다.

Java

public class MyApplication extends Application
    implements ActivityLifecycleCallbacks, LifecycleObserver { {
  ...
  @Override
  public void onCreate() {
    super.onCreate();
    this.registerActivityLifecycleCallbacks(this);
    MobileAds.initialize(
        this,
        new OnInitializationCompleteListener() {
          @Override
          public void onInitializationComplete(InitializationStatus initializationStatus) {}
        });
    ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    appOpenAdManager = new AppOpenAdManager();
  }

  /** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
  @OnLifecycleEvent(Event.ON_START)
  protected void onMoveToForeground() {
    // Show the ad (if available) when the app moves to foreground.
    appOpenAdManager.showAdIfAvailable(currentActivity);
  }

  /** Show the ad if one isn't already showing. */
  private void showAdIfAvailable(@NonNull final Activity activity) {
      showAdIfAvailable(
          activity,
          new OnShowAdCompleteListener() {
            @Override
            public void onShowAdComplete() {
              // Empty because the user will go back to the activity that shows the ad.
            }
          });
  }
}

Kotlin

class MyApplication : Application(),
    Application.ActivityLifecycleCallbacks, LifecycleObserver {
  ...
  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(this)
    MobileAds.initialize(this) {}
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    appOpenAdManager = AppOpenAdManager()
  }

  /** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
  @OnLifecycleEvent(Lifecycle.Event.ON_START)
  fun onMoveToForeground() {
    // Show the ad (if available) when the app moves to foreground.
    currentActivity?.let {
      appOpenAdManager.showAdIfAvailable(it)
    }
  }

  /** Show the ad if one isn't already showing. */
  fun showAdIfAvailable(activity: Activity) {
    showAdIfAvailable(
        activity,
        object : OnShowAdCompleteListener {
          override fun onShowAdComplete() {
            // Empty because the user will go back to the activity that shows the ad.
          }
        })
  }
}

LifecycleObserver를 등록하면 앱에 앱 실행 및 포그라운드 이벤트가 전달되며 적절한 시점에 광고를 게재할 수 있습니다.

광고 만료 고려

만료된 광고가 표시되지 않도록 하려면 광고 참조가 로드된 후 경과한 시간을 확인하는 메서드를 AppOpenAdManager에 추가하세요. 그런 다음 이 메서드를 이용해 광고가 여전히 유효한지 확인하세요.

Java

private class AppOpenAdManager {
  ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private long loadTime = 0;

  /** Request an ad. */
  public void loadAd(Context context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return;
    }

    isLoadingAd = true;
    AdRequest request = new AdRequest.Builder().build();
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        new AppOpenAdLoadCallback() {
          @Override
          public void onAdLoaded(AppOpenAd ad) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.");
            appOpenAd = ad;
            isLoadingAd = false;
            loadTime = (new Date()).getTime();
          }

          @Override
          public void onAdFailedToLoad(LoadAdError loadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.getMessage());
            isLoadingAd = false;
          }
        });
  }
  ...

  /** Utility method to check if ad was loaded more than n hours ago. */
  private boolean wasLoadTimeLessThanNHoursAgo(long numHours) {
    long dateDifference = (new Date()).getTime() - this.loadTime;
    long numMilliSecondsPerHour = 3600000;
    return (dateDifference < (numMilliSecondsPerHour * numHours));
  }

  /** Check if ad exists and can be shown. */
  public boolean isAdAvailable() {
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
  }
}

Kotlin

private inner class AppOpenAdManager {
  ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private var loadTime: Long = 0

  /** Request an ad. */
  fun loadAd(context: Context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return
    }

    isLoadingAd = true
    val request = AdRequest.Builder().build()
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        object : AppOpenAdLoadCallback() {

          override fun onAdLoaded(ad: AppOpenAd) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.")
            appOpenAd = ad
            isLoadingAd = false
            loadTime = Date().time
          }

          override fun onAdFailedToLoad(loadAdError: LoadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.message)
            isLoadingAd = false;
          }
        })
  }
  ...

  private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {
    val dateDifference: Long = Date().time - loadTime
    val numMilliSecondsPerHour: Long = 3600000
    return dateDifference < numMilliSecondsPerHour * numHours
  }

  private fun isAdAvailable(): Boolean {
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4)
  }
}

콜드 스타트 및 로드 화면

지금까지 이 문서에서는 메모리에 정지된 앱을 사용자가 포그라운드로 가져올 때만 앱 오프닝 광고를 게재한다고 가정했습니다. '콜드 스타트'는 앱이 실행되었지만 이전에 메모리에서 정지되지 않은 경우에 발생합니다.

콜드 스타트의 예로는 사용자가 앱을 처음으로 열 때를 들 수 있습니다. 콜드 스타트를 사용하면 이전에 로드한 앱 오프닝 광고를 즉시 표시할 수 있습니다. 광고를 요청하고 광고를 다시 수신하는 시점 사이에 지연이 발생하면 사용자가 맥락을 벗어난 광고에 당황하기 전에 앱을 잠시 사용할 수 있는 상황이 발생할 수 있습니다. 이는 사용자 환경에 악영향을 미치므로 피해야 합니다.

콜드 스타트 시 앱 오프닝 광고를 사용하는 가장 좋은 방법은 로드 화면을 사용하여 게임 또는 앱 애셋을 로드하고 로드 화면에서만 광고를 표시하는 것입니다. 앱에서 로드가 완료되고 사용자를 앱의 주요 콘텐츠로 안내한 경우에는 광고를 게재하지 마세요.

권장사항

앱 오프닝 광고를 사용하면 앱이 처음 실행될 때나 앱을 전환할 때 앱의 로드 화면에서 수익을 창출할 수 있습니다. 하지만 사용자가 앱을 즐겁게 사용할 수 있도록 권장사항을 염두에 두는 것이 중요합니다. 가장 좋은 방법은 다음과 같습니다.

  • 사용자가 앱을 몇 번 사용한 후에 첫 앱 오프닝 광고를 게재하세요.
  • 사용자가 앱이 로드되기를 기다리는 동안 앱 오프닝 광고를 표시합니다.
  • 앱 오프닝 광고 아래에 로드 화면이 있고 광고가 닫히기 전에 화면 로드가 완료되면 onAdDismissedFullScreenContent() 메서드에서 로드 화면을 닫는 것이 좋습니다.

GitHub의 예

다음 단계

다음 주제를 살펴보세요.