Unity 인앱 광고 (AdMob)
Unity 앱인토스 SDK를 사용하면 웹뷰 SDK나 JavaScript 브릿지를 직접 구현하지 않아도,
C# 코드만으로 인앱 광고(AdMob)를 로드하고 표시할 수 있어요.
현재는 전면 광고(Interstitial) 와 보상형 광고(Rewarded) 를 제공해요.
개요
Unity SDK는 내부적으로 다음 작업을 자동으로 처리해요.
- Unity ↔ WebView ↔ Apps In Toss Web SDK 간 브릿지 구성
- 광고 로드 / 표시 상태 및 이벤트 전달
- Web SDK의 Promise 결과를 Unity 콜백 형태로 변환
따라서 JavaScript 코드를 작성하거나 웹뷰 SDK를 직접 다룰 필요 없이, Unity SDK API만 호출하면 됩니다.
csharp
AIT.GoogleAdMobLoadAppsInTossAdMob(...)
AIT.GoogleAdMobShowAppsInTossAdMob(...)사전 준비
- Unity 앱인토스 SDK 패키지 설치
- WebGL 빌드 환경 설정 완료
- 앱인토스 콘솔에서 AdMob 광고 그룹 생성
테스트용 광고 그룹 ID
테스트 환경에서는 테스트용 광고 그룹 ID를 사용할 수 있어요.
- 전면형 광고:
ait-ad-test-interstitial-id - 리워드 광고:
ait-ad-test-rewarded-id
인앱 광고는 샌드박스 앱에서 테스트할 수 없어요.
불편하시겠지만 콘솔 내 QR 코드로 테스트를 진행해 주세요.
광고 호출 흐름
인앱 광고는 반드시 아래 순서로 호출해야 해요.
- 광고 로드
- 광고 표시
- (광고 종료 후) 다시 로드
한 번에 하나의 광고만 로드할 수 있어요.
주의하세요
페이지 별로 광고를 미리 로드해 주세요.
광고가 로드되지 않은 상태에서 호출하면 오류가 발생해 fill-rate가 낮아질 수 있어요.
iOS에서 로드가 되지 않나요?
앱 추적 모드가 켜져있을 경우, 광고 로드가 정상적으로 동작하지 않을 수 있어요.
앱 추적 모드를 해제한 뒤 다시 시도해 주세요.
지원 광고 타입
Unity SDK에서는 다음 AdMob 광고 타입을 지원해요.
| 광고 타입 | 설명 |
|---|---|
| Interstitial | 화면 전환 시 표시되는 전면 광고 |
| Rewarded | 광고 시청 후 보상을 지급하는 광고 |
광고 이벤트 처리
광고 표시 과정에서 아래와 같은 이벤트가 순차적으로 발생할 수 있어요. Unity SDK는 이 이벤트들을 콜백 형태로 전달해요.
| 이벤트 타입 | 설명 |
|---|---|
| requested | 광고 표시 요청이 정상적으로 전달됨 |
| show | 광고 컨텐츠가 화면에 표시됨 |
| impression | 광고가 실제로 노출됨 |
| clicked | 사용자가 광고를 클릭함 |
| userEarnedReward | 보상형 광고 시청 완료 후 보상 지급 |
| dismissed | 사용자가 광고를 닫음 |
| failedToShow | 광고를 표시하지 못함 |
주의사항
userEarnedReward이벤트는 보상형 광고에서만 발생해요.- 보상 지급 로직은
userEarnedReward이벤트 기준으로 처리해주세요. failedToShow발생 시에는 광고를 다시 로드한 후 재시도해야 해요.
샘플 코드
csharp
using System;
using System.Collections.Generic;
using UnityEngine;
using AppsInToss;
/// <summary>
/// AdMob 광고 테스터 컴포넌트
/// GoogleAdMobLoadAppsInTossAdMob/GoogleAdMobShowAppsInTossAdMob API를 테스트합니다.
/// </summary>
public class AdV2Tester : MonoBehaviour
{
// 테스트용 광고 ID (CI에서 빌드 시 sed로 치환됨)
// 문서 참조: https://developers-apps-in-toss.toss.im/bedrock/reference/framework/광고/loadAppsInTossAdMob.html
private const string TEST_INTERSTITIAL_AD_ID = "ait-ad-test-interstitial-id";
private const string TEST_REWARDED_AD_ID = "ait-ad-test-rewarded-id";
// 광고 상태
private string adStatus = "";
private bool isAdLoaded = false;
private string selectedAdType = "interstitial"; // interstitial 또는 rewarded
private List<string> adEventLog = new List<string>();
/// <summary>
/// 마지막 작업 상태 메시지
/// </summary>
public string Status => adStatus;
/// <summary>
/// AdMob 테스터 UI를 렌더링합니다.
/// </summary>
public void DrawUI(
GUIStyle boxStyle,
GUIStyle groupHeaderStyle,
GUIStyle labelStyle,
GUIStyle buttonStyle,
GUIStyle textFieldStyle,
GUIStyle fieldLabelStyle,
GUIStyle callbackLabelStyle)
{
GUILayout.BeginVertical(boxStyle);
// 섹션 헤더
GUILayout.Label("AdMob Tester", groupHeaderStyle);
GUILayout.Label("loadAppsInTossAdMob/showAppsInTossAdMob API를 테스트합니다.", labelStyle);
GUILayout.Label("Load → Show 순서로 호출해야 합니다.", labelStyle);
GUILayout.Space(10);
// 상태 표시
string loadStatus = isAdLoaded ? "Loaded" : "Not Loaded";
GUILayout.Label($"Status: {loadStatus}", labelStyle);
if (!string.IsNullOrEmpty(adStatus))
{
GUILayout.Label($" {adStatus}", callbackLabelStyle);
}
// 이벤트 로그 표시 (최근 5개)
if (adEventLog.Count > 0)
{
GUILayout.Label("Event Log:", labelStyle);
int startIndex = Math.Max(0, adEventLog.Count - 5);
for (int i = startIndex; i < adEventLog.Count; i++)
{
GUILayout.Label($" {adEventLog[i]}", callbackLabelStyle);
}
}
GUILayout.Space(10);
// 광고 타입 선택
GUILayout.Label("Ad Type:", fieldLabelStyle);
GUILayout.BeginHorizontal();
if (GUILayout.Button(
selectedAdType == "interstitial" ? "[Interstitial]" : "Interstitial",
buttonStyle, GUILayout.Height(36), GUILayout.ExpandWidth(true)))
{
selectedAdType = "interstitial";
}
if (GUILayout.Button(
selectedAdType == "rewarded" ? "[Rewarded]" : "Rewarded",
buttonStyle, GUILayout.Height(36), GUILayout.ExpandWidth(true)))
{
selectedAdType = "rewarded";
}
GUILayout.EndHorizontal();
// 현재 선택된 광고 ID 표시
string currentAdId = selectedAdType == "interstitial" ? TEST_INTERSTITIAL_AD_ID : TEST_REWARDED_AD_ID;
GUILayout.Label($"Ad ID: {currentAdId}", callbackLabelStyle);
GUILayout.Space(10);
// Step 1: 광고 로드
GUILayout.Label("Step 1: Load Ad", fieldLabelStyle);
if (GUILayout.Button("loadAppsInTossAdMob(...)", buttonStyle, GUILayout.Height(40)))
{
ExecuteLoadAd();
}
GUILayout.Space(10);
// Step 2: 광고 표시
GUILayout.Label("Step 2: Show Ad", fieldLabelStyle);
GUI.enabled = isAdLoaded;
if (GUILayout.Button("showAppsInTossAdMob(...)", buttonStyle, GUILayout.Height(40)))
{
ExecuteShowAd();
}
GUI.enabled = true;
if (!isAdLoaded)
{
GUILayout.Label("(광고를 먼저 로드해주세요)", callbackLabelStyle);
}
GUILayout.Space(10);
// 로그 초기화
if (adEventLog.Count > 0)
{
if (GUILayout.Button("Clear Log", buttonStyle, GUILayout.Height(32)))
{
adEventLog.Clear();
adStatus = "";
}
}
GUILayout.EndVertical();
}
private Action _loadUnsubscribe;
private void ExecuteLoadAd()
{
string adId = selectedAdType == "interstitial" ? TEST_INTERSTITIAL_AD_ID : TEST_REWARDED_AD_ID;
adStatus = $"Loading {selectedAdType} ad...";
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] loadAppsInTossAdMob(adGroupId: {adId})");
// 기존 구독 해제
_loadUnsubscribe?.Invoke();
// GoogleAdMobLoadAppsInTossAdMob API 호출
#pragma warning disable CS0618 // Obsolete 경고 무시
_loadUnsubscribe = AIT.GoogleAdMobLoadAppsInTossAdMob(
options: new LoadAdMobOptions { AdGroupId = adId },
onEvent: (result) =>
{
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Load event: {result.Type}");
if (result.Type == "loaded")
{
isAdLoaded = true;
adStatus = $"{selectedAdType} ad loaded";
if (result.Data != null)
{
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] AdGroupId: {result.Data.AdGroupId}, AdUnitId: {result.Data.AdUnitId}");
}
}
},
onError: (error) =>
{
isAdLoaded = false;
adStatus = $"Error: {error.Message}";
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Error: {error.ErrorCode} - {error.Message}");
}
);
#pragma warning restore CS0618
}
private Action _showUnsubscribe;
private void ExecuteShowAd()
{
if (!isAdLoaded)
{
adStatus = "Please load ad first";
return;
}
string adId = selectedAdType == "interstitial" ? TEST_INTERSTITIAL_AD_ID : TEST_REWARDED_AD_ID;
adStatus = $"Showing {selectedAdType} ad...";
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] showAppsInTossAdMob(adGroupId: {adId})");
// 기존 구독 해제
_showUnsubscribe?.Invoke();
// GoogleAdMobShowAppsInTossAdMob API 호출
#pragma warning disable CS0618 // Obsolete 경고 무시
_showUnsubscribe = AIT.GoogleAdMobShowAppsInTossAdMob(
options: new ShowAdMobOptions { AdGroupId = adId },
onEvent: (result) =>
{
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Show event: {result.Type}");
if (result.Type == "dismissed")
{
// 광고 표시 후 다시 로드 필요 (한 번에 1개만 로드 가능)
isAdLoaded = false;
adStatus = $"{selectedAdType} ad shown (reload required for next ad)";
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Success: Ad dismissed");
}
else if (result.Type == "userEarnedReward" && result.Data != null)
{
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Reward: {result.Data.UnitAmount} {result.Data.UnitType}");
}
},
onError: (error) =>
{
adStatus = $"Error: {error.Message}";
adEventLog.Add($"[{DateTime.Now:HH:mm:ss}] Error: {error.ErrorCode} - {error.Message}");
}
);
#pragma warning restore CS0618
}
private void OnDestroy()
{
// 컴포넌트 제거 시 구독 해제
_loadUnsubscribe?.Invoke();
_showUnsubscribe?.Invoke();
}
}주의사항
TIP
- 광고는 반드시 Load → Show 순서로 호출해야 해요.
- 광고를 한 번 표시하면 다시 로드해야 다음 광고를 표시할 수 있어요.
- 한 번에 하나의 광고만 로드할 수 있어요.
- 보상형 광고의 보상 지급은
userEarnedReward이벤트 기준으로 처리해주세요. - 인앱 광고는 샌드박스 앱에서 테스트할 수 없어요.
인앱 광고 테스트는 QR 코드를 통해 토스 앱에서 실행한 경우에만 가능해요.
