앱개발 종합반 5주차 개발일지
앱에서 수익을 내는 방법:
배너 수익 방식
1. 배너 클릭
2. 배너 광고 시청
3. 배너 광고 사용자 참여
이 3가지 모두 앱에 쉽게 적용할 수 있는 방법을 구글 애드몹 광고 서비스에서 제공해줍니다.
애드몹(AdMob) - 설정
구글 애드몹에서는 총 4가지의 광고 유형을 제공합니다.
앱에 애드몹 사용 준비하기
Expo에서의 애드몹
👉 구글 애드몹 역시 Expo에서 지원해줍니다.
하지만 공식 문서에서 애드몹의 사용가능 플랫폼을 보면, 모두 가능하지만 웹에서는 안된다는 것을 확인할 수 있습니다. 웹의 경우 구글에서는 구글 애드센스를 지원하기 때문인데, 우린 앱개발 중이니 참고만 합니다!
앱에 애드몹 설치하기
expo install expo-ads-admob
👉 설치가 완료 됐으면 지금껏 2주차에서 딱 한 번만 열어봤던 app.json을 열어 하단에 android, ios 부분을 추가해줍니다.
ios 부분은
"ios": { "supportsTablet": true },
이미 있기 때문에, 덮어 씁니다.
중요한 점은 ios의 bundleIdentifier와 googleMobileAdsAppId android의 package와 googleMobileAdsAppId는 여러분 값이 되어야 한다는 겁니다
app.json 하단 추가 부분
"ios": {
"supportsTablet": true,
"buildNumber": "1.0.0",
"bundleIdentifier": "com.myhoneytip.gun",
"config": {
"googleMobileAdsAppId": ""
}
},
"android": {
"package": "com.myhoneytip.gun",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
}
},
최종 app.json 코드
{
"expo": {
"name": "sparta-myhoneytip-gun",
"slug": "sparta-myhoneytip-gun",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"web": {
"favicon": "./assets/favicon.png"
},
"ios": {
"supportsTablet": true,
"buildNumber": "1.0.0",
"bundleIdentifier": "com.myhoneytip.gun",
"config": {
"googleMobileAdsAppId": ""
}
},
"android": {
"package": "com.myhoneytip.gun",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
}
},
}
}
👉 bundleIdentifier, package는 일반적으로 com.company.appname형식을 따릅니다. 여러분만의 값으로 구성해서 넣어주세요. 영어로...
이 둘 값은 앱 마켓들과 구글에 여러분 앱 이름을 알려주는 부분입니다.
ios, android의 각각의 googleMobileAdsAppId 는 05. [구글광고] 애드몹(AdMob) - 가로 배너 생성 강의에서 적용합니다.
애드몹(AdMob) - 가로 배너 생성
앱에 애Expo에서의 애드몹
👉 구글 애드몹 역시 Expo에서 지원해줍니다.
Admob - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
하지만 공식 문서에서 애드몹의 사용가능 플랫폼을 보면, 모두 가능하지만 웹에서는 안된다는 것을 확인할 수 있습니다. 웹의 경우 구글에서는 구글 애드센스를 지원하기 때문인데, 우린 앱개발 중이니 참고만 합니다!
앱에 애드몹 설치하기
expo install expo-ads-admob
👉 설치가 완료 됐으면 지금껏 2주차에서 딱 한 번만 열어봤던 app.json을 열어 하단에 android, ios 부분을 추가해줍니다.
ios 부분은
"ios": { "supportsTablet": true },
이미 있기 때문에, 덮어 씁니다.
중요한 점은 ios의 bundleIdentifier와
googleMobileAdsAppId android의 package와
googleMobileAdsAppId는 여러분 값이 되어야 한다는 겁니다
app.json 하단 추가 부분
"ios": {
"supportsTablet": true,
"buildNumber": "1.0.0",
"bundleIdentifier": "com.myhoneytip.gun",
"config": {
"googleMobileAdsAppId": ""
}
},
"android": {
"package": "com.myhoneytip.gun",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
}
},
최종 app.json 코드
{
"expo": {
"name": "sparta-myhoneytip-gun",
"slug": "sparta-myhoneytip-gun",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"web": {
"favicon": "./assets/favicon.png"
},
"ios": {
"supportsTablet": true,
"buildNumber": "1.0.0",
"bundleIdentifier": "com.myhoneytip.gun",
"config": {
"googleMobileAdsAppId": ""
}
},
"android": {
"package": "com.myhoneytip.gun",
"versionCode": 1,
"config": {
"googleMobileAdsAppId": ""
}
},
}
}
👉 bundleIdentifier, package는 일반적으로 com.company.appname형식을 따릅니다. 여러분만의 값으로 구성해서 넣어주세요. 영어로...
즉,sparta-myhoneytip-kim이라던가 com.myhoneytip-kim으로 바꿔준다.
이 둘 값은 앱 마켓들과 구글에 여러분 앱 이름을 알려주는 부분입니다.
ios, android의 각각의 googleMobileAdsAppId 는 05. [구글광고] 애드몹(AdMob) - 가로 배너 생성 강의에서 적용합니다.
애드몹(AdMob) - 가로 배너 생성
앱 하단 가로 배너 광고 생성 및 적용
1) 애드몹 가로 배너 광고 단위 설정 2) 앱 상에 적용
https://docs.expo.io/versions/latest/sdk/admob/
Admob - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
애드몹 가로 배너 광고 단위 설정:
👉 적용 할 앱을 선택한 후 광고 단위 추가 버튼을 클릭합니다
👉 배너 생성시 마다 어디에 쓸 배너 광고인지 구분하면, 추후에 각 배너별 추적 분석이 용이합니다
두 키 코드값은 앱 상에서 배너를 적용할 때 필요합니다.
👉 첫 번째 키 값은 app.json에 비워 뒀던 android의 googleMobileAdsAppId 에 적용합니다.
👉 iOS도 앱생성 → 광고 단위 생성 → 가로배너 생성 → 키값 순서대로 진행 한 후, app.json에 적용해주세요!
앱 상에 적용
👉 광고 단위 생성 후 부여 받은 두 번째 키값은 app.json이 아닌 실제 Main.js 코드 단에서 사용될 키 값입니다.
구글에서 부여한 가로배너를 앱과 연결하는 정보가 담겨 있는데요!
다음 Main.js 코드를 적용해준다.
적용하기 전에 코드 단에서 부르는 가로 배너의 이름은 AdMobBanner 입니다.
광고 이름들이 정해져 있어요!
반드시 애드몹의 여러분들 키 값을 적용해야 보여요!
MainPage.js
import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
const main = '<https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmain.png?alt=media&token=8e5eb78d-19ee-4359-9209-347d125b322c>'
import data from '../data.json';
import Card from '../components/Card';
import Loading from '../components/Loading';
import { StatusBar } from 'expo-status-bar';
import * as Location from "expo-location";
import axios from "axios"
import {firebase_db} from "../firebaseConfig"
import {
setTestDeviceIDAsync,
AdMobBanner,
AdMobInterstitial,
PublisherBanner,
AdMobRewarded
} from 'expo-ads-admob';
export default function MainPage({navigation,route}) {
//useState 사용법
//[state,setState] 에서 state는 이 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
//setState는 state를 변경시킬때 사용해야하는 함수
//모두 다 useState가 선물해줌
//useState()안에 전달되는 값은 state 초기값
const [state,setState] = useState([])
const [cateState,setCateState] = useState([])
//날씨 데이터 상태관리 상태 생성!
const [weather, setWeather] = useState({
temp : 0,
condition : ''
})
//하단의 return 문이 실행되어 화면이 그려진다음 실행되는 useEffect 함수
//내부에서 data.json으로 부터 가져온 데이터를 state 상태에 담고 있음
const [ready,setReady] = useState(true)
useEffect(()=>{
navigation.setOptions({
title:'나만의 꿀팁'
})
//뒤의 1000 숫자는 1초를 뜻함
//1초 뒤에 실행되는 코드들이 담겨 있는 함수
setTimeout(()=>{
firebase_db.ref('/tip').once('value').then((snapshot) => {
console.log("파이어베이스에서 데이터 가져왔습니다!!")
let tip = snapshot.val();
setState(tip)
setCateState(tip)
getLocation()
setReady(false)
});
// getLocation()
// setState(data.tip)
// setCateState(data.tip)
// setReady(false)
},1000)
},[])
const getLocation = async () => {
//수많은 로직중에 에러가 발생하면
//해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행
try {
//자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await
await Location.requestForegroundPermissionsAsync();
const locationData= await Location.getCurrentPositionAsync();
console.log(locationData)
console.log(locationData['coords']['latitude'])
console.log(locationData['coords']['longitude'])
const latitude = locationData['coords']['latitude']
const longitude = locationData['coords']['longitude']
const API_KEY = "cfc258c75e1da2149c33daffd07a911d";
const result = await axios.get(
`http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
);
console.log(result)
const temp = result.data.main.temp;
const condition = result.data.weather[0].main
console.log(temp)
console.log(condition)
//오랜만에 복습해보는 객체 리터럴 방식으로 딕셔너리 구성하기!!
//잘 기억이 안난다면 1주차 강의 6-5를 다시 복습해보세요!
setWeather({
temp,condition
})
} catch (error) {
//혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다
Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?");
}
}
const category = (cate) => {
if(cate == "전체보기"){
//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
setCateState(state)
}else{
setCateState(state.filter((d)=>{
return d.category == cate
}))
}
}
//data.json 데이터는 state에 담기므로 상태에서 꺼내옴
// let tip = state.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"
//return 구문 밖에서는 슬래시 두개 방식으로 주석
return ready ? : (
/*
return 구문 안에서는 {슬래시 + * 방식으로 주석
*/
{/* 나만의 꿀팁 */}
오늘의 날씨: {weather.temp + '°C ' + weather.condition}
{navigation.navigate('AboutPage')}}>
소개 페이지
{category('전체보기')}}>전체보기
{category('생활')}}>생활
{category('재테크')}}>재테크
{category('반려견')}}>반려견
{navigation.navigate('LikePage')}}>꿀팁 찜
{/* 하나의 카드 영역을 나타내는 View */}
{
cateState.map((content,i)=>{
return ()
})
}
{/*
ca-app-pub-5579008343368676/9202552776
ca-app-pub-5579008343368676/6885179499
*/}
{Platform.OS === 'ios' ? (
) : (
)}
)
}
const styles = StyleSheet.create({
container: {
//앱의 배경 색
backgroundColor: '#fff',
},
title: {
//폰트 사이즈
fontSize: 20,
//폰트 두께
fontWeight: '700',
//위 공간으로 부터 이격
marginTop:50,
//왼쪽 공간으로 부터 이격
marginLeft:20
},
weather:{
alignSelf:"flex-end",
paddingRight:20
},
mainImage: {
//컨텐츠의 넓이 값
width:'90%',
//컨텐츠의 높이 값
height:200,
//컨텐츠의 모서리 구부리기
borderRadius:10,
marginTop:20,
//컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
//각 속성의 값들은 공식문서에 고대로~ 나와 있음
alignSelf:"center"
},
middleContainer:{
marginTop:20,
marginLeft:10,
height:60
},
middleButtonAll: {
width:100,
height:50,
padding:15,
backgroundColor:"#20b2aa",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton01: {
width:100,
height:50,
padding:15,
backgroundColor:"#fdc453",
borderColor:"deeppink",
borderRadius:15,
margin:7
},
middleButton02: {
width:100,
height:50,
padding:15,
backgroundColor:"#fe8d6f",
borderRadius:15,
margin:7
},
middleButton03: {
width:100,
height:50,
padding:15,
backgroundColor:"#9adbc5",
borderRadius:15,
margin:7
},
middleButton04: {
width:100,
height:50,
padding:15,
backgroundColor:"#f886a8",
borderRadius:15,
margin:7
},
middleButtonText: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
middleButtonTextAll: {
color:"#fff",
fontWeight:"700",
//텍스트의 현재 위치에서의 정렬
textAlign:"center"
},
cardContainer: {
marginTop:10,
marginLeft:10
},
aboutButton: {
backgroundColor:"pink",
width:100,
height:40,
borderRadius:10,
alignSelf:"flex-end",
marginRight:20,
marginTop:10
},
aboutButtonText: {
color:"#fff",
textAlign:"center",
marginTop:10
},
banner:{
width:"100%",
height:100
}
});
적용 모습
코드 리뷰
👉 사용하기 위해선 항상 상단에 애드몹 라이브러리를 가져와 준비해놔야 합니다. 실제론 이 중에서 사용이 될 라이브러리 이름만 거론하면 되지만, 사용방법을 익히기 위해 이렇게 전부 가져와 살펴보고 있습니다.
//애드몹 설정을 위한 엑스포 애드몹 라이브라리 임포트!
import {
setTestDeviceIDAsync,
AdMobBanner,
AdMobInterstitial,
PublisherBanner,
AdMobRewarded
} from 'expo-ads-admob';
👉 이렇게 쉽게 광고 배너가 부착이 됩니다! 참 매력적입니다. 그런데 정말 Expo + 애드몹의 꽃은 실제 코드를 적용하는 곳에 있습니다.
{Platform.OS === 'ios' ? (
<AdMobBanner
bannerSize="fullBanner"
servePersonalizedAds={true}
adUnitID="ca-app-pub-3271224099084995/4041258226"
onDidFailToReceiveAdWithError={bannerError}
style={styles.banner}
/>
) : (
<AdMobBanner
bannerSize="fullBanner"
servePersonalizedAds={true}
adUnitID="ca-app-pub-3271224099084995/5120026862"
onDidFailToReceiveAdWithError={bannerError}
style={styles.banner}
/>
)}
👉 아이폰이냐 안드로이드냐에 따라 광고 설정을 달리할 수 있기 때문에 두 플랫폼에 대응할 수 있는 광고도 설정이 가능합니다.
또한 배너에 스타일을 적용하여 앱 디자인에 알맞게 적용할 수 있습니다.
banner: {
//배너 스타일!
}
결과 확인
👉 이렇게 가로 배너를 설치하게되면, 사용자들이 누른 순간 우린 광고 수익을 얻게 됩니다. 광고 수익 현환은 애드몹 메인 화면에서 확인이 가능합니다
Expo APP 에서도 광고가 보이지 않으시나요? 애드몹 광고가 승인되었다고 하더라도, 애플리케이션이 스토어에 개제되지 않으면(안올렸거나 심사중) 광고가 보이지 않습니다. 즉 다음과 같이 동작합니다.
- Expo APP 은 스토어에 있으므로, 테스트 광고와 실제 광고 모두 잘 떠야 합니다. 다만 배너광고와 전면광고를 바꿔끼거나 오타를 내시는 등이면 광고가 안뜹니다.
- APK 빌드 한 것인 경우, 그 앱을 플레이스토어에 아직 올리지 않으셨거나 심사중인경우 광고가 뜨지 않습니다.
- app.json 의 애드몹 키 값과, 배너 코드 부분의 키값이 정상적으로 들어 갔는지 확인 부탁드립니다!
[구글광고] 애드몹(AdMob) - 전면 배너 생성
나만의 꿀팁 앱 팁 선택시 디테일 페이지 가기전에 광고 달기!
적용 모습
카드를 누르면!
전면 광고 배너 실행 순서는 이렇습니다
1) 메인페이지에서 꿀팁을 하나 누르면
2) 전면 광고가 뜨고
3) 전면 광고 노출 일정시간 후
4) 디테일 페이지로 이동!
그럼 Card.js에 기능을 달아야 겠죠?
애드몹 전면 광고 배너 설정
👉 애드몹에서 전면 광고 유형을 활성화 해야합니다.
👉 아이폰용 전면 광고 활성화 절차도 동일합니다.
👉 전면광고는 가로배너보다는 비교적 까다롭습니다. 그래도 설명서대로 따라해보면 어려움이 없습니다.
Admob - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
전면 광고를 사용자들에게 보여주는 상황은 " 팁을 누르고! 디테일 화면으로 넘어가기 바로전!" 입니다.
그럼 Card.js 광고를 달아야겠네요!
코드 단에 들어가기 전에 사용 할 전면 광고의 코드단에서의 이름은 interstitial 입니다.
Card.js
import React, { useEffect } from 'react';
import {View, Image, Text, StyleSheet,TouchableOpacity,Platform} from 'react-native'
import {
setTestDeviceIDAsync,
AdMobBanner,
AdMobInterstitial,
PublisherBanner,
AdMobRewarded
} from 'expo-ads-admob';
//MainPage로 부터 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function Card({content,navigation}){
useEffect(()=>{
// Card.js에 들어오자마자 전면 광고 준비하느라 useEffect에 설정
//애드몹도 외부 API 이므로 실행 순서를 지키기위해 async/await 사용!
//안드로이드와 IOS 각각 광고 준비 키가 다르기 때문에 디바이스 성격에 따라 다르게 초기화 시켜줘야 합니다.
Platform.OS === 'ios' ? AdMobInterstitial.setAdUnitID("ca-app-pub-5579008343368676/6838730428") : AdMobInterstitial.setAdUnitID("ca-app-pub-5579008343368676/4903859898")
AdMobInterstitial.addEventListener("interstitialDidLoad", () =>
console.log("interstitialDidLoad")
);
AdMobInterstitial.addEventListener("interstitialDidFailToLoad", () =>
console.log("interstitialDidFailToLoad")
);
AdMobInterstitial.addEventListener("interstitialDidOpen", () =>
console.log("interstitialDidOpen")
);
AdMobInterstitial.addEventListener("interstitialDidClose", () => {
//광고가 끝나면 다음 코드 줄이 실행!
console.log("interstitialDidClose")
});
},[])
const goDetail = async () =>{
try {
await AdMobInterstitial.requestAdAsync({ servePersonalizedAds: true});
await AdMobInterstitial.showAdAsync();
await navigation.navigate('DetailPage',{idx:content.idx})
} catch (error) {
console.log(error)
await navigation.navigate('DetailPage',{idx:content.idx})
}
}
return(
//카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
<TouchableOpacity style={styles.card} onPress={()=>{goDetail()}}>
<Image style={styles.cardImage} source={{uri:content.image}}/>
<View style={styles.cardText}>
<Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text>
<Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
<Text style={styles.cardDate}>{content.date}</Text>
</View>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
card:{
flex:1,
flexDirection:"row",
margin:10,
borderBottomWidth:0.5,
borderBottomColor:"#eee",
paddingBottom:10
},
cardImage: {
flex:1,
width:100,
height:100,
borderRadius:10,
},
cardText: {
flex:2,
flexDirection:"column",
marginLeft:10,
},
cardTitle: {
fontSize:20,
fontWeight:"700"
},
cardDesc: {
fontSize:15
},
cardDate: {
fontSize:10,
color:"#A6A6A6",
}
});
코드 리뷰:
import {
setTestDeviceIDAsync,
AdMobBanner,
AdMobInterstitial,
PublisherBanner,
AdMobRewarded
} from 'expo-ads-admob';
👉 꿀팁을 눌렀을 때 실행되는 goDetail 함수에 다음과 같은 전면 광고를 실행시키는 애드몹 API를 추가했습니다 이 부분은 실행 순서가 지켜져야 하기 때문에 async await 자바스크립트 문법을 사용했습니다
const goDetail = async () =>{
try {
await AdMobInterstitial.requestAdAsync({ servePersonalizedAds: true});
await AdMobInterstitial.showAdAsync();
await navigation.navigate('DetailPage',{idx:content.idx})
} catch (error) {
console.log(error)
await navigation.navigate('DetailPage',{idx:content.idx})
}
}
👉 그리고 useEffect에는 Card.js에 들어오자마자 광고를 준비하는 코드를 그대로 가져와 복붙했고, 안에 키값만 여러분들의 키값으로 변경했습니다.
그리고 보여지는 AdMobInterstitial.addEventListener 코드들은
광고를 실행했을때
광고 실행이 실패했을때
광고가 열리고 난다음
광고가 끝난다음
각각 어떻게 처리할까?에 대한 애드몹 제공함수인데요!
우리가 정작쓰는건 광고가 끝난 다음의 모습이지만 여러분들이 뭘 좋아하실지 몰라 다 가져왔습니다.
useEffect(()=>{
// Card.js에 들어오자마자 전면 광고 준비하느라 useEffect에 설정
//애드몹도 외부 API 이므로 실행 순서를 지키기위해 async/await 사용!
//안드로이드와 IOS 각각 광고 준비 키가 다르기 때문에 디바이스 성격에 따라 다르게 초기화 시켜줘야 합니다.
Platform.OS === 'ios' ? AdMobInterstitial.setAdUnitID("ca-app-pub-5579008343368676/6838730428") : AdMobInterstitial.setAdUnitID("ca-app-pub-5579008343368676/4903859898")
AdMobInterstitial.addEventListener("interstitialDidLoad", () =>
console.log("interstitialDidLoad")
);
AdMobInterstitial.addEventListener("interstitialDidFailToLoad", () =>
console.log("interstitialDidFailToLoad")
);
AdMobInterstitial.addEventListener("interstitialDidOpen", () =>
console.log("interstitialDidOpen")
);
AdMobInterstitial.addEventListener("interstitialDidClose", () => {
//광고가 끝나면 다음 코드 줄이 실행!
console.log("interstitialDidClose")
});
},[])
다양한 광고 유형
👉 심리테스트 앱에는 현재 두 가지 유형의 광고가 연결되었습니다. 가로 배너와 전면 광고 유형인데요!
이 밖에도 보상형 광고라던지 앱 컨텐츠에 맞게 나타나는 광고까지 다양항 광고 유형이 존재합니다.
공식 문서를 보며 다양한 광고 유형을 적용해가면서, 추후에 여러분들이 만들 앱에는 어떤 광고 유형이 적합한지 상상해보세요!
Admob - Expo Documentation
Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.
docs.expo.dev
[배포하기] 배포를 위한 체크리스트