본문 바로가기
React-Native

React-Native : 애드몹(AdMob) iOS 14 적용하기

by Dokon Jang 2021. 3. 27.
반응형

iOS 14에서 고주용 식별자(IDFA)에 액세스하려는 앱의 경우 앱 추적 투명성(ATT) 프레임워크라는 메시지를 통해 사용자에게 액세스 권한을 요청해야 합니다.
애드몹 사이트에서 아래와 같이 2가지 알림이 표시되는데 해당 건을 처리하는 방법입니다.

[권한 요청 화면]


[React-Native 모듈 버전]

react : "16.11.0"
react-native : "0.62.2"
react-native-admob : "2.0.0-beta.6" 

 

1. Info.plist 수정
(1) SKAdNetworkItems 추가

<key>SKAdNetworkItems</key>
<array>
<dict>
  <key>SKAdNetworkIdentifier</key>
  <string>cstr6suwn9.skadnetwork</string>
</dict>
</array

(2) NSUserTrackingUsageDescription 추가

<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>

 

2. Podfile 수정

(1) <리액트 네이티브 프로젝트>/ios/Podfile.lock에서 Google-Mobile-Ads-SDK 버전 7.64.0 이상인지 확인합니다.

(2) <리액트 네이티브 프로젝트>/ios/Podfile에 Google-Mobile-Ads-SDK 버전을 7.69.0으로 지정한다.

   (Google-Mobile-Ads-SDK 8.x.x에서는 react-native-admob 2.0.0-beta.6에서 컴파일 오류발생합니다.)

 

pod 'Google-Mobile-Ads-SDK','~> 7.69.0'

 

3. 추적 투명성 대화상자를 위한 react-native-tracking-transparency 모듈 설치

npm install react-native-tracking-transparency
cd ios
pod install

 

4. 앱 화면 소스
(1) 배너 전역 변수 선언

state={
	...
	banner : null,
}

 

(2) componentWillMount에서 추적 투명성 대화상자 요청

componentWillMount() {
  requestTrackingPermission().then((status) => {
    this.state.banner = <AdMobBanner
	                    adSize="smartBannerPortrait"
	                    adUnitID={ADMOB_BANNER_ID}
	                    testDevices={[AdMobBanner.simulatorId]}/>;
    this.setState(this.state.banner);
  });
}

 

(3) 앱 화면에 배너 표시

render() {
  return (
      <View style={{flex: 1, backgroundColor:'#eeeeee'}}>
        ...
        {this.state.banner}
    </View>
  );
}

 

[전체소스]

import React, { Component } from 'react';
import {View, Text, Switch, FlatList, TouchableOpacity, Image, Alert, AsyncStorage, AppState} from 'react-native';
import styles, {KEY_BOOKMARK, ADMOB_BANNER_ID,} from '../Common';
import { AdMobBanner, } from 'react-native-admob';
import { requestTrackingPermission, getTrackingStatus, } from 'react-native-tracking-transparency';

export default class BookmarkScreen extends Component{

  state={
    bookmarkList:[],
    appState: AppState.currentState,
    banner : null,
  }

  componentWillMount() {
    requestTrackingPermission().then((status) => {
      this.state.banner = <AdMobBanner
						  adSize="smartBannerPortrait"
						  adUnitID={ADMOB_BANNER_ID}
						  testDevices={[AdMobBanner.simulatorId]}/>;
      this.setState(this.state.banner);
    });
  }

  componentDidMount() {
    this.search();
  }

  render() {
      return (
        <View style={{flex: 1, backgroundColor:'#eeeeee'}}>
          <FlatList style={styles.round_full_box}
              data={this.state.bookmarkList}
              renderItem={({item}) => (       
                    <TouchableOpacity  style={styles.round_card_box} onPress={() => this.onClickItem(item)} onLongPress={() => this.onLongClickItem(item)}>
                      <View style={{flexDirection: 'row', flex: 1, alignItems:'center'}}>
                        <Text style={{flex: 1, }}>
                          {item.type}
                        </Text>
                        <TouchableOpacity onPress={() => this.onClickUp(item)}>
                          <Image source={require('../images/arrow_up.png')} style={{width:20, height:20, margin:5}} />
                        </TouchableOpacity>
                        <TouchableOpacity onPress={() => this.onClickDown(item)}>
                          <Image source={require('../images/arrow_down.png')} style={{width:20, height:20, margin:5}} />
                        </TouchableOpacity>

                        <TouchableOpacity onPress={() => this.onClickRemove(item)}>
                          <Image source={require('../images/close.png')} style={{width:20, height:20, margin:5}} />
                        </TouchableOpacity>
                      </View>                      

                      <View style={{flex: 1, backgroundColor:'#aaaaaa', height:1, marginTop:5, marginBottom:5}}/>

                      <View style={{flexDirection: 'row', flex: 1, alignItems:'center'}}>
                        <Image source={require('../images/dot.png')} style={{width:10, height:10, margin:5}}/>
                        <Text style={{flex: 1, fontSize:20, color:'#336699'}}>
                          {item.name}
                        </Text>
                        <Text>
                          {item.direction}
                        </Text>
                      </View>

                      <View style={{flexDirection: 'row', flex: 1, alignItems:'center'}}>
                        <Text style={{flex: 1, marginLeft:20, fontSize:18}}>
                          {item.no}
                        </Text>
                        <Text>
                          {item.time}
                        </Text>
                      </View>
                    </TouchableOpacity>
              )}/>

              {this.state.banner}
              
      </View>
    );
  }

  onClickItem(item){
    if(item.type == '정류소'){
      this.props.navigation.navigate('BUSSTOPDETAIL', {id:item.id, name:item.name, busstopNo:item.no, gpsX:item.gpsX, gpsY:item.gpsY});
    }else if(item.type == '버스'){
      this.props.navigation.navigate('BUSLINE', {id:item.id, name:item.name, type:item.no, direction:item.direction, time:item.time});
    }
  }

  onClickRemove(item){
    Alert.alert(
      item.name + '(' + item.no + ')',
      '삭제하시겠습니까?',
      [
        {text: '아니요', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
        {text: '예', onPress: () => {
          let idx = this.state.bookmarkList.indexOf(item);
          this.state.bookmarkList.splice(idx, 1);
          this.setState(this.state.bookmarkList);

          this.state.bookmarkList.reverse();
          let bookmarkListStr = JSON.stringify(this.state.bookmarkList);
          AsyncStorage.setItem(KEY_BOOKMARK, bookmarkListStr, () => {
            this.search();
          });
        }},
      ],
      { cancelable: false }
    );
  }
  
  onClickUp(item){
    let idx = this.state.bookmarkList.indexOf(item);
    if(idx == 0) return;

    this.state.bookmarkList.splice(idx, 1);
    this.state.bookmarkList.splice(idx - 1, 0, item);
    
    this.state.bookmarkList.reverse();
    let bookmarkListStr = JSON.stringify(this.state.bookmarkList);
    AsyncStorage.setItem(KEY_BOOKMARK, bookmarkListStr, () => {
      this.search();
    });
  }

  onClickDown(item){
    let idx = this.state.bookmarkList.indexOf(item);
    if(idx == this.state.bookmarkList.length - 1) return;

    this.state.bookmarkList.splice(idx, 1);
    this.state.bookmarkList.splice(idx + 1, 0, item);
    
    this.state.bookmarkList.reverse();
    let bookmarkListStr = JSON.stringify(this.state.bookmarkList);
    AsyncStorage.setItem(KEY_BOOKMARK, bookmarkListStr, () => {
      this.search();
    });
  }

  search(){
    AsyncStorage.getItem(KEY_BOOKMARK, (err, value )=>{
      if(err == null){
        this.state.bookmarkList = JSON.parse(value);
        if(this.state.bookmarkList != null){
          this.state.bookmarkList.reverse();
          this.setState(this.state.bookmarkList);
        }
      }

   });
  }
}

 

※ iOS 앱을 심사신청하면 아래와 같이 NSUserTrackingUsageDescription 추가로 앱이 수집하는 개인정보를 수정한다.

앱이 수집하는 개인정보에서 광고데이터에 "추적 목적으로 사용됨"을 선택하면 됩니다.

반응형

댓글