메인 콘텐츠로 건너뛰기

개요

백필 광고는 직광고가 없을 때 대체 광고를 자동으로 노출하여 수익을 극대화하는 기능입니다. Adrop은 AdMob, Pangle 등 주요 광고 네트워크를 백필 광고로 지원합니다.
백필 광고를 사용하려면 네이티브 플랫폼별로 추가 설정이 필요합니다.

Android 설정

1. Gradle 설정

settings.gradle.kts

Pangle 광고 네트워크 저장소를 추가합니다:
settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://artifact.bytedance.com/repository/pangle") }
    }
}

build.gradle.kts

백필 광고 의존성을 추가합니다:
dependencies {
    implementation("io.adrop:adrop-ads:1.7.2")
    implementation("io.adrop:adrop-ads-backfill:1.7.2")
}

2. AndroidManifest.xml 설정

AdMob을 백필 광고로 사용하는 경우, AdMob App ID를 AndroidManifest.xml에 추가합니다:
AndroidManifest.xml
<manifest>
    <application>
        <!-- AdMob App ID -->
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>
    </application>
</manifest>
ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy를 실제 AdMob App ID로 교체해야 합니다.

3. ProGuard 설정

ProGuard를 사용하는 경우 다음 규칙을 추가합니다:
proguard-rules.pro
-keep class io.adrop.** { *; }
-dontwarn io.adrop.**

iOS 설정

1. Podfile 수정

백필 광고 의존성을 Podfile에 추가합니다:
Podfile
target 'Runner' do
  use_frameworks!
  use_modular_headers!

  # Adrop SDK
  pod 'adrop-ads'

  # 백필 광고 SDK
  pod 'adrop-ads-backfill'

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
의존성을 추가한 후 다음 명령어를 실행합니다:
cd ios && pod install --repo-update && cd ..

2. Info.plist 설정

AdMob을 백필 광고로 사용하는 경우, AdMob App ID를 Info.plist에 추가합니다:
Info.plist
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>
ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy를 실제 AdMob App ID로 교체해야 합니다.

콘솔 설정

Adrop 콘솔에서 백필 광고를 활성화합니다:
  1. 애드컨트롤 콘솔에 로그인
  2. 광고 유닛 메뉴로 이동
  3. 백필 광고를 사용할 광고 유닛 선택
  4. 백필 설정 섹션에서 백필 광고 활성화
  5. 사용할 백필 광고 네트워크 선택 (AdMob, Pangle 등)
  6. 광고 네트워크별 설정 입력 (예: AdMob Ad Unit ID)

Flutter에서 사용하기

백필 광고 확인

광고가 백필 광고인지 확인하려면 isBackfilled 프로퍼티를 사용합니다.

네이티브 광고

네이티브 광고의 경우, 백필 광고 여부에 따라 미디어 뷰를 다르게 렌더링해야 합니다.
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';
import 'package:webview_flutter/webview_flutter.dart';

class NativeAdWithBackfill extends StatefulWidget {
  const NativeAdWithBackfill({super.key});

  @override
  State<NativeAdWithBackfill> createState() => _NativeAdWithBackfillState();
}

class _NativeAdWithBackfillState extends State<NativeAdWithBackfill> {
  bool isLoaded = false;
  AdropNativeAd? nativeAd;
  late final WebViewController webViewController;

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

    // WebView 컨트롤러 초기화
    webViewController = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted);

    _createNativeAd();
  }

  void _createNativeAd() {
    nativeAd = AdropNativeAd(
      unitId: 'YOUR_UNIT_ID',
      useCustomClick: true,
      listener: AdropNativeListener(
        onAdReceived: (ad) {
          debugPrint('광고 수신: ${ad.isBackfilled ? "백필" : "직광고"}');

          // 직광고인 경우 WebView에 HTML 크리에이티브 로드
          if (!ad.isBackfilled && ad.properties.creative != null) {
            webViewController.loadHtmlString(ad.properties.creative!);
          }

          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToReceive: (ad, errorCode) {
          if (errorCode == AdropErrorCode.backfillNoFill) {
            debugPrint('백필 광고도 없습니다');
          } else {
            debugPrint('광고 로드 실패: $errorCode');
          }
        },
      ),
    );

    nativeAd?.load();
  }

  @override
  Widget build(BuildContext context) {
    if (!isLoaded) return const SizedBox.shrink();

    return AdropNativeAdView(
      ad: nativeAd,
      child: Container(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 헤드라인
            if (nativeAd?.properties.headline != null)
              Text(
                nativeAd!.properties.headline!,
                style: const TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            const SizedBox(height: 8),

            // 미디어 뷰 - 백필 여부에 따라 다르게 렌더링
            _buildMediaView(),

            // CTA 버튼
            if (nativeAd?.properties.callToAction != null)
              Padding(
                padding: const EdgeInsets.only(top: 8),
                child: ElevatedButton(
                  onPressed: () {},
                  child: Text(nativeAd!.properties.callToAction!),
                ),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildMediaView() {
    if (nativeAd?.isBackfilled == true) {
      // 백필 광고: 이미지 에셋 사용
      return Image.network(
        nativeAd?.properties.asset ?? '',
        width: double.infinity,
        height: 200,
        fit: BoxFit.cover,
        loadingBuilder: (context, child, loadingProgress) {
          if (loadingProgress == null) return child;
          return const SizedBox(
            height: 200,
            child: Center(child: CircularProgressIndicator()),
          );
        },
        errorBuilder: (context, error, stackTrace) {
          return const SizedBox(
            height: 200,
            child: Center(child: Icon(Icons.error)),
          );
        },
      );
    } else if (nativeAd?.properties.creative != null) {
      // 직광고: WebView로 HTML 크리에이티브 렌더링
      return SizedBox(
        width: double.infinity,
        height: 300,
        child: WebViewWidget(controller: webViewController),
      );
    } else {
      return const SizedBox.shrink();
    }
  }
}
네이티브 광고에서 백필 광고가 아닌 경우, 동영상 광고는 WebView를 사용하여 직접 렌더링해야 합니다.

배너 광고

배너 광고에서는 메타데이터를 통해 백필 여부를 확인할 수 있습니다.
bannerView = AdropBannerView(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropBannerListener(
    onAdReceived: (unitId, metadata) {
      final isBackfilled = metadata?['isBackfilled'] ?? false;
      if (isBackfilled == true) {
        debugPrint('백필 광고가 로드되었습니다');
      } else {
        debugPrint('직광고가 로드되었습니다');
      }
    },
    onAdFailedToReceive: (unitId, errorCode) {
      if (errorCode == AdropErrorCode.backfillNoFill) {
        debugPrint('백필 광고도 없습니다');
      }
    },
  ),
);

전면/리워드/팝업 광고

전면, 리워드, 팝업 광고에서도 유사하게 백필 여부를 확인할 수 있습니다.
interstitialAd = AdropInterstitialAd(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropInterstitialListener(
    onAdReceived: (ad) {
      debugPrint('광고 로드 완료');
      // 전면 광고의 경우 별도의 isBackfilled 프로퍼티는 없지만
      // 콘솔에서 백필 설정이 되어 있으면 자동으로 백필 광고가 로드됩니다
    },
    onAdFailedToReceive: (ad, errorCode) {
      if (errorCode == AdropErrorCode.backfillNoFill) {
        debugPrint('직광고와 백필 광고 모두 없습니다');
      }
    },
  ),
);

광고 노출 흐름

백필 광고는 다음과 같은 순서로 노출됩니다:

에러 코드

백필 광고 관련 에러 코드입니다.
에러 코드설명
adNoFill직광고를 받을 수 없음 (백필 광고 시도)
backfillNoFill백필 광고도 받을 수 없음
onAdFailedToReceive: (ad, errorCode) {
  switch (errorCode) {
    case AdropErrorCode.adNoFill:
      debugPrint('직광고가 없습니다. 백필 광고를 시도합니다.');
      break;
    case AdropErrorCode.backfillNoFill:
      debugPrint('백필 광고도 없습니다.');
      break;
    default:
      debugPrint('광고 로드 실패: ${errorCode.code}');
  }
}

지원 광고 형식

백필 광고는 다음 형식을 지원합니다:
광고 형식지원 여부설명
배너 (Banner)지원고정 크기 배너 광고
네이티브 (Native)지원커스터마이징 가능한 네이티브 광고
전면 (Interstitial)지원전체 화면 광고
리워드 (Rewarded)지원보상형 광고
팝업 (Popup)지원팝업 형태 광고

모범 사례

백필 광고 활성화

모든 광고 유닛에서 백필 광고를 활성화하여 광고 노출률과 수익을 극대화하세요.

적절한 타임아웃 설정

직광고와 백필 광고의 적절한 타임아웃을 설정하여 사용자 경험을 개선하세요.

백필 광고 분석

isBackfilled 프로퍼티를 사용하여 직광고와 백필 광고의 비율을 추적하고 분석하세요.

네이티브 광고 미디어 처리

네이티브 광고에서는 백필 여부에 따라 Image.network 또는 WebView를 적절히 사용하세요.

주의사항

  • Android: 백필 광고를 사용하려면 반드시 io.adrop:adrop-ads-backfill:1.7.2 의존성을 추가해야 합니다.
  • iOS: 백필 광고를 사용하려면 반드시 pod 'adrop-ads-backfill'를 Podfile에 추가해야 합니다.
  • AdMob을 사용하는 경우 각 플랫폼의 매니페스트 파일에 AdMob App ID를 반드시 추가해야 합니다.
  • 네이티브 광고에서 백필 광고가 아닌 경우 WebView로 미디어를 렌더링해야 합니다.
  • 백필 광고 네트워크의 정책을 준수해야 합니다.

다음 단계