구글 애드몹을 운영하는 과정에서 "구글 애드몹: 광고 게재가 현재 제한적입니다. 무효 트래픽 우려로 인해 현재 제품에 대한 광고 게재가 제한되고 있습니다."라는 문제가 발생하였을 때 대처 방법에 대해 알아봅니다.
1) 구글 애드몹: 광고 게재가 현재 제한적입니다. 무효 트래픽 우려로 인해 현재 제품에 대한 광고 게재가 제한되고 있습니다.
💡 구글 애드몹: 광고 게재가 현재 제한적입니다. 무효 트래픽 우려로 인해 현재 제품에 대한 광고 게재가 제한되고 있습니다.
- 해당 메시지의 경우는 Admob 플랫폼이 앱에서 비정상적인 광고 트래픽을 감지했으며, 일시적으로 광고 게재를 제한하고 있다는 의미 합니다. 최대 30일까지 게재가 제한이 된다고 합니다.
💡 아래와 같은 경우에 발생한다고 합니다. - 게시자가 자신의 라이브 광고를 클릭하여 생성된 클릭이나 노출 - 한 명 이상의 사용자가 생성한 반복적인 광고 클릭 또는 노출 - 게시자가 자신의 광고 클릭을 유도함(예: 사용자의 광고 클릭을 유도하는 언어, 다량의 의도하지 않은 클릭을 유발할 수 있는 광고 구현 등) - 자동화된 클릭 도구나 트래픽 소스, 로봇, 기타 사기 소프트웨어
💡 광고 리 렌더링(재노출) 문제점 - 기존의 코드를 확인하면, 매번 페이지에서 '광고가 리렌더링이 발생하여 반복적으로 새로운 광고가 노출되는 문제'가 발생하였습니다. - 이러한 반복적인 렌더링은 문제가 발생할 수 있습니다. 이유는 리 렌더링이 수행되어서 새로운 광고가 노출이 되면, 짧은 시간 내에 광고가 바뀌어서 전달이 됩니다. - 평균적으로 ‘60초 내에서 리 렌더링’이 발생하면 안 된다고 합니다.
- 예를 들어서, A라는 리포트 화면에 배너 광고를 두고, B라는 홈화면에 배너 광고를 두었을 때, A, B를 반복적으로 탭을 이동하게 되면 매번 새로운 광고가 나오고 전송이 되기에 과도한 광고 요청이 발생합니다. - 이렇게 과도한 광고 요청이 발생한 경우 ‘무효한 트래픽’으로 발생할 수 있다고 합니다.
문제
설명
과도한 광고 요청
탭 이동, 스크롤, 재진입 등에서 광고 컴포넌트가 계속 리렌더링되면 광고 요청이 매번 새로 전송됨
실사용자의 ‘의도된’ 노출이 아님
사용자는 한 번만 본 셈인데, 서버에는 여러 번 노출된 것으로 기록됨
AdMob 정책 위반 가능성
Google은 광고가 비정상적으로 자주 로드되는 구조를 명시적으로 금지하고 있음
수익 회수 및 계정 정지 위험
광고 노출 대비 클릭률이 낮거나, 노출량이 급증하면 의심 트래픽으로 간주되어 수익 보류/차단됨
- 아래와 같이 각각 화면 내에 <AdmobBannerAd />를 호출해 왔습니다. 이 과정에 해당 화면이 매번 바뀌면서 광고가 다시 불러오게 되면서(광고 리렌더링), 탭으로 리포트 탭을 이동하면 ReportScreen 내에서 새로운 광고가 나오고 탭으로 홈으로 이동하면 Home 내에서 새로운 광고가 나오는 문제가 발생합니다.
- 그렇기에 짧은 구간에 탭을 이동한 경우 광고가 새로고침 되어서 매번 새로운 광고가 발생하기에 무효한 트래픽이 발생합니다.
💡 공통으로 구성한 AdmobBannerAd 코드
import React, { useRef } from 'react';
import { Dimensions, Platform, StyleSheet, View } from 'react-native';
import { BannerAd, BannerAdSize, TestIds, useForeground } from 'react-native-google-mobile-ads';
import { GOOGLE_ADMOV_ANDROID_BANNER, GOOGLE_ADMOV_IOS_BANNER } from '@env';
type AdUnitIdType = string;
const AD_UNIT_ID: AdUnitIdType = Platform.select({
ios: __DEV__ ? TestIds.BANNER : GOOGLE_ADMOV_IOS_BANNER!,
android: __DEV__ ? TestIds.BANNER : GOOGLE_ADMOV_ANDROID_BANNER!,
}) as AdUnitIdType;
interface AdmobBannerAdProps {
paramMarginTop?: number;
paramMarginBottom?: number;
}
/**
* [공통] 배너 광고
* @returns
*/
const AdmobBannerAd: React.FC<AdmobBannerAdProps> = ({ paramMarginTop = 0, paramMarginBottom = 20 }) => {
const bannerRef = useRef<BannerAd | null>(null);
const screenWidth = Dimensions.get('window').width;
/**
* 플랫폼 iOS에 대해서만 이를 적용합니다.
* - 앱이 "suspended state"(백그라운드 상태)에 있을 때 WKWebView가 종료될 수 있음
* - 이로 인해 앱이 포그라운드로 돌아올 때 배너 광고가 비어있을 수 있음
* - 이 문제를 해결하기 위해 앱이 포그라운드로 돌아올 때 수동으로 새로운 광고를 요청하는 것이 권장됨
*/
useForeground(() => {
if (Platform.OS === 'ios') {
bannerRef.current?.load();
}
});
// 태블릿 기준 너비 600 이상
const getBannerSize = () => {
if (screenWidth >= 600) {return BannerAdSize.FULL_BANNER;} // 468x60
if (screenWidth >= 480) {return BannerAdSize.LARGE_BANNER;} // 320x100
return BannerAdSize.BANNER; // 320x50
};
return (
<View style={[styles.container, { marginTop: paramMarginTop, marginBottom: paramMarginBottom }]}>
<BannerAd
ref={bannerRef}
unitId={AD_UNIT_ID}
size={getBannerSize()} // 환경에 따라 유동적인 변경
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
},
});
export default AdmobBannerAd;
💡 컴포넌트 내에서 위에서 구성한 AdmobBannerAd를 호출하여 사용하였습니다. - 그렇기에 해당 페이지에 접근할 때마다, 배너 광고가 리렌더링이 되면서 반복적으로 요청이 되는 문제가 발생합니다.
- App.tsx → ApplicationNavigator.tsx → StackNavigator.tsx를 호출하는 형태로 구성하였습니다. - Navigation 내에서는 페이지를 이동하면 mount → unMount 과정을 거치게 되면서 렌더링을 수행하게 됩니다. 그렇기에 상위가 되는 Navigation 내에 Layout을 구성하여서 최초 1회만 호출되도록 구성하였습니다.
return (
// Redux Stroe
<Provider store={Store}>
{/* Redux-Persist */}
<PersistGate persistor={persistor}>
{/* 다국어 적용 */}
<I18nextProvider i18n={i18n}>
{/* Main Navigation */}
<ApplicationNavigator />
</I18nextProvider>
{/* 필수 : 버전 관리 및 체크를 수행 */}
<VersionCheckModal />
</PersistGate>
</Provider>
);
2.2. ApplicationNavigator.tsx
💡 ApplicationNavigator.tsx
- 해당 페이지에서는 직접적으로 StackNavigation을 선택하여서 호출하였던 부분을 AppLayout를 추가하여서 두었습니다.
import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { StatusBar } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import AppLayout from './AppLayout';
import StackNavigator from './StackNavigator';
/**
* 모든 네비게이션에 대해 일괄 메인으로 관리합니다.
* @returns
*/
const ApplicationNavigator = () => {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaProvider>
<StatusBar translucent backgroundColor="transparent" barStyle="dark-content" />
<AppLayout />
</SafeAreaProvider>
</GestureHandlerRootView>
);
};
export default ApplicationNavigator;
2.3. AppLayout
💡 AppLayout
- 해당 페이지에서는 StackNavigator 위에 AdmobBannerAd를 추가하여서 보여주게 구성하였습니다. - 모든 페이지에서 해당 광고가 나오면 쉽게 StackNavigator 위에 출력을 하면 되지만, 모든 페이지의 광고가 나오기에 사용자의 피로감이 높을 수 있기에 특정 페이지에서만 출력되도록 구성되어야 합니다. 이런 경우에는 또한, 하위 속성 값이 바뀌게 되면 리 렌더링이 발생할 수 있기에 복잡합니다.
- 그렇기에 우선, AD_ALLOWED_ROUTES 값을 통해서, 광고가 노출될 라우팅에 대해서 정의하였습니다. - 그리고, currentRoute에 따라서 라우팅이 준비되었을 때와 경로가 바뀔 때마다 currentRoute를 갱신하도록 하였습니다. 또한, !shouldShowAd && { height: 0, opacity: 0 }]} 값을 통해서, 실제 화면인 경우 광고 영역을 감추도록 처리를 하였습니다.
- <AdmobBannerAd visible={shouldShowAd} />와 같이 해당 값은 AdmobBannerAd로 전달하였습니다.
- 해당 페이지에서는 전달받은 속성인 visible의 값에 따라 움직입니다. - 아래의 과정에서는 AdmobBannerAd에서 하단에 useMemo로 감싸서, 속성 값이 변하기 전까지 유지되도록 구성이 되었습니다. 그리고 visible에 따라서 스타일이 통제되어 숨겨지거나 보여주는 형태로 제공합니다.
- 실제 광고가 출력되는 BannerAd 태그 내에서 key, unitId, size가 변경되지 않으면, 렌더링이 수행되지 않고, 화면상에 광고는 유지된 채, 사용자에게는 보이고 감춰지는 형태로 제공이 됩니다.