앱개발 종합반 4주차

홍영훈·2021년 8월 4일
0

앱과 서버

앱에 모든 데이터를 담을 수 없다. 이유는?

  1. 앱 용량이 커질 수 있다.
  2. 앱 개발자가 새로운 데이터를 사용자에게 제공하려면, 새로운 데이터를 담아 다시 배포해야 한다.

그래서, 앱이 아닌 서버에 데이터를 저장하고, 서버에서 데이터를 가져오고, 변경한다. 서버를 통해 하는

  • 데이터 생성
  • 데이터 조회
  • 데이터 삭제/수정

을 제공해 주는 서비스들이 존재한다. 이를 서버리스라고 부르기도 한다. 대표적인 것이 구글에서 제공해주는 파이어베이스이다.

서버 쪽에서 정한 규칙을 API(Application Programming interface)라고 한다. API의 형태(Request)는,

서버가 제공하는 도메인

www.sparta.com/getdata ←- 데이터 조회 API 
www.sparta.com/setData ←- 데이터 저장 API

서버가 만들어 놓은 함수

db.ref('/like/').on('value') ←- 데이터 조회 API 
db.ref('/like/').set(new_like); <-- 데이터 저장 API

서버에서 주는(Response) 데이터 형식 : JSON (list, dictionary의 복합 구조)

리액트 네이티브에서 주로 데이터를 준비하는 시점은,

  • 앱 화면이 그려진 다음 데이터를 준비 : useEffect (앱 화면을 보자마자 실행되어야 하는 작업들이 모여 있는 곳)
useEffect(()=>{
  //서버 API 사용
  //이 화면에서 사용 할 데이터 준비 등... 
},[])
  • 앱에서 사용자가 저장 버튼을 눌렀을 때

휴대폰 위치 가져오기

Expo 에서는 현재 위치 데이터를 얻게 해 주는 도구를 제공하고 있다.

이 도구를 사용하기 위해서는 설치해야 한다.

expo install expo-location

MainPage.js

...

import * as Location from "expo-location";

...

  useEffect(()=>{ 
	    
    //뒤의 1000 숫자는 1초를 뜻함 
    //1초 뒤에 실행되는 코드들이 담겨 있는 함수 
    setTimeout(()=>{ 
        //헤더의 타이틀 변경 
        navigation.setOptions({ 
            title:'나만의 꿀팁' 
        }) 
        //꿀팁 데이터로 모두 초기화 준비 
        let tip = data.tip; 
        setState(tip) 
        setCateState(tip) 
        getLocation() 
        setReady(false) 
    },1000)

...


  const getLocation = async () => { 
    //수많은 로직중에 에러가 발생하면 
    //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행 
    try { 
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await 
      await Location.requestPermissionsAsync(); 
      const locationData= await Location.getCurrentPositionAsync(); 
      console.log(locationData) 
    } catch (error) { 
      //혹시나 위치를 못가져올 경우를 대비해서, 안내를 준비합니다 
      Alert.alert("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?"); 
    } 
  }

...

날씨 외부 API 사용

OpenWeather 에서 제공하는 Weather API를 사용할 것이다.

서버가 제공하는 도메인 형식의 API를 사용하려면, 사용을 위한 도두가 필요하다. 이는 Javascript에서 제공하는 도구로 axios 라고 부른다.

다음과 같이 설치를 진행한다.

yarn add axios

위도, 경도만 알면 날씨 데이터를 건네준다.
API_KEY : API 제공 업체 측에 가입을 한 다음 부여 받은 키 값

MainPage.js

...

import axios from "axios"

...

  //날씨 데이터 상태관리 상태 생성! 
  const [weather, setWeather] = useState({ 
    temp : 0, 
    condition : '' 
  })

...

  useEffect(()=>{ 
	    
    //뒤의 1000 숫자는 1초를 뜻함 
    //1초 뒤에 실행되는 코드들이 담겨 있는 함수 
    setTimeout(()=>{ 
        //헤더의 타이틀 변경 
        navigation.setOptions({ 
            title:'나만의 꿀팁' 
        }) 
        //꿀팁 데이터로 모두 초기화 준비 
        let tip = data.tip; 
        setState(tip) 
        setCateState(tip) 
        getLocation() 
        setReady(false) 
    },1000)    
  },[])

  const getLocation = async () => { 
    //수많은 로직중에 에러가 발생하면 
    //해당 에러를 포착하여 로직을 멈추고,에러를 해결하기 위한 catch 영역 로직이 실행 
    try { 
      //자바스크립트 함수의 실행순서를 고정하기 위해 쓰는 async,await 
      await Location.requestPermissionsAsync(); 
      const locationData= await Location.getCurrentPositionAsync(); 
      const latitude = locationData['coords']['latitude'] 
      const longitude = locationData['coords']['longitude'] 
      const API_KEY = "*********************************";  
      const result = await axios.get( 
        `http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric` 
      ); 
      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("위치를 찾을 수가 없습니다.", "앱을 껏다 켜볼까요?"); 
    } 
  } 
...

    <ScrollView style={styles.container}> 
        <StatusBar style="black" /> 
        {/* <Text style={styles.title}>나만의 꿀팁</Text> */} 
        <Text style={styles.weather}>오늘의 날씨: {weather.temp + '°C   ' + weather.condition} </Text> 

...

앱에 파이어베이스 도구 설치 및 연결

파이어베이스를 사용할 수 있게 도와주는 expo 도구를 설치해야 한다.

설치

expo install firebase

설치가 완료되면, firebaseConfig.js 파일을 하나 생성해야 한다. (최상위 위치에 생성) 그리고 파이어베이스에 연결할 코드를 이 파일에 입력하여 저장한다.

firebaseConfig.js

//import * as firebase from 'firebase/app'; 
import firebase from 'firebase/app'; 
// 사용할 파이어베이스 서비스 주석을 해제합니다 
//import "firebase/auth"; 
import "firebase/database"; 
//import "firebase/firestore"; 
//import "firebase/functions"; 
import "firebase/storage"; 
// Initialize Firebase 
//파이어베이스 사이트에서 봤던 연결정보를 여기에 가져옵니다 
const firebaseConfig = { 
    apiKey: "**********************************",  
    authDomain: "sparta.firebaseapp.com", 
    databaseURL: "https://sparta.asia-southeast1.firebasedatabase.app/",
    projectId: "sparta", 
    storageBucket: "sparta.appspot.com", 
    messagingSenderId: "62045555555",  
    appId: "1:620434444448:web:dc48bbrrrrrrrrrrf",  
    measurementId: "G-G55555555"  
}; 
//사용 방법입니다.  
//파이어베이스 연결에 혹시 오류가 있을 경우를 대비한 코드로 알아두면 됩니다. 
if (!firebase.apps.length) { 
    firebase.initializeApp(firebaseConfig); 
} 
export const firebase_db = firebase.database()

앱에서 Realtime Database 사용하기

데이터 가져오기 함수는 다음과 같다.

firebase_db.ref('/tip').once('value').then((snapshot) => { 
   let tip = snapshot.val(); 
})

MainPage.js

...

import {firebase_db} from "../firebaseConfig"

...

    useEffect(()=>{ 
      //헤더의 타이틀 변경 
        navigation.setOptions({ 
            title:'나만의 꿀팁' 
        }) 
        firebase_db.ref('/tip').once('value').then((snapshot) => { 
          console.log("파이어베이스에서 데이터 가져왔습니다!!") 
          let tip = snapshot.val(); 
          setState(tip) 
          setCateState(tip) 
          getLocation() 
          setReady(false) 
        }); 
        // setTimeout(()=>{ 
        //     let tip = data.tip; 
        //     setState(tip) 
        //     setCateState(tip) 
        //     getLocation() 
        //     setReady(true) 
        // },500) 
    },[])

Realtime database, 특정 데이터 읽기

큰 데이터(전체 데이터)가 이동하는 것은 앱 퍼포먼스의 저하 원인이 된다. 그러므로, idx 번호만 넘겨 필요한 데이터만 서버로부터 가져오는 것이 앱 퍼포먼스에 낫다.

Card.js

...

<TouchableOpacity style={styles.card} onPress={() => { navigation.navigate('DetailPage', {idx:content.idx}) }}>

...

DetailPage.js

...
import {firebase_db} from "../firebaseConfig"
// firebase로부터 데이터를 가져오기 위해 연결 과정이 필요. firebaseConfig를 import

...

    useEffect(()=>{
        console.log(route)
        navigation.setOptions({
            title:route.params.title,
            headerStyle: {
                backgroundColor: '#000',
                shadowColor: "#000",
            },
            headerTintColor: "#fff",
        })
        //넘어온 데이터는 route.params에 들어 있습니다.
        const { idx } = route.params;
        firebase_db.ref('/tip/'+idx).once('value').then((snapshot) => {
            let tip = snapshot.val();
            setTip(tip)
        });
    },[])

...

Reatime Database, 쓰기

필요한 데이터 구조

  • 어떠한 꿀팁을
    - 꿀팁 번호 : idx
    - 꿀팁 이미지 : image
    - 꿀팁 제목 : title
    - 꿀팁 내용 : desc
  • 누가
    - 수많은 사람들이 사용
    - 사용자마다 고유 ID 값 정도의 데이터가 필요. 그래야 구분이 가능
    - Expo는 앱을 사용할 사용자들의 고유 아이디를 생성해서 알려준다. 이를 통해 사용자들마다 고유한 ID 값으로 데이터를 관리할 수 있다.
  • 실제 DB에 이렇게 저장하다

Expo-Constants

사용자마다 고유한 ID 값으로 데이터를 관리할 수 있도록 해 줌.

도구 설치가 필요하다.

expo install expo-constants

실제 코드가 필요하다.

import Constants from 'expo-constants';
console.log(Constants.installationId)

Firebase 저장 구현

DetailPage.js,

...
import Constants from 'expo-constants';

export default function DetailPage({navigation,route}) {
    let user_idx = Constants.installationId
    console.log(user_idx)

... useEffect 는 앞서 수정한 코드 그대로 ...

    const like = () => {
        
        // like 방 안에
        // 특정 사용자 방안에
        // 특정 찜 데이터 아이디 방안에
        // 특정 찜 데이터 몽땅 저장!
        // 찜 데이터 방 > 사용자 방 > 어떤 찜인지 아이디
        const user_id = Constants.installationId;
        firebase_db.ref('/like/'+user_id+'/'+ tip.idx).set(tip,function(error){
            console.log(error)
            Alert.alert("찜 완료!")
        });
    }

...

            <TouchableOpacity style={styles.button} onPress={() => like()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>

...

팁을 저장하는 곳은

  • '/찜 데이터 방/ + 사용자 방/ + 어떤 찜인지 방 아이디(번호)
  • '/like/'+user_id+'/'+ tip.idx
    Reatime Database는 JSON 형태로 관리되기 때문에 저장할 때도 이에 맞게 JSON 형태로 저장해야 한다. 이미 DetailPage tip 상태에서 idx로 조회한 찜 데이터는 딕셔너리 데이터로 저장관리하고 있다.
profile
코딩을 처음 배워가는 코린이?

0개의 댓글