본문 바로가기

코딩 개발일지

[개발일지]스파르타코딩클럽_앱개발 종합반 4 Firebase

//드디어 적용해보는 Firebase

 

파이어베이스는 구글에서 만든 서버리스 서비스입니다.

서버에 대한 지식이 그렇게 깊지 않아도 서버적인 기능들을 사용할 수 있게끔 도와주는 서비스입니다.

 

1. 파이어베이스 가입

 -> 프로젝트 생성 등 완료하면 코드를 받게됨

톱니바퀴 -> 프로젝트 설정 부분에 가보면 확인가능

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey
: "Abcdefg~~",
  authDomain
: "sparta-myhoneytipxxx.firebaseapp.com",
  databaseURL
: "https://sparta-myhoneytipxxx-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId
: "sparta-myhoneytipxxxx",
  storageBucket
: "sparta-myhoneytipxxxx.appspot.com",
  messagingSenderId
: "xxxx",
  appId
: "1:xxxxx",
  measurementId
: "G-xxxxx"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);

 -> 여기서 firebaseConfig부분이 필요함

 

2. 파이어베이스 설치

사이트에 이렇게 적혀 있는데,  npm install firebase

expo install firebase 로 설치한다

3. firebaseConfig.js 파일생성 후 받은 프로젝트 코드를 복사해서 넣는다.

import firebase from "firebase/compat/app";

// 사용할 파이어베이스 서비스 주석을 해제합니다
//import "firebase/compat/auth";
import "firebase/compat/database";
//import "firebase/compat/firestore";
//import "firebase/compat/functions";
import "firebase/compat/storage";

// Initialize Firebase
//파이어베이스 사이트에서 봤던 연결정보를 여기에 가져옵니다
const firebaseConfig = {
    apiKey: "Abcdefg~~",
  authDomain
: "sparta-myhoneytipxxx.firebaseapp.com",
  databaseURL
: "https://sparta-myhoneytipxxx-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId
: "sparta-myhoneytipxxxx",
  storageBucket
: "sparta-myhoneytipxxxx.appspot.com",
  messagingSenderId
: "xxxx",
  appId
: "1:xxxxx",
  measurementId
: "G-xxxxx"
  };

//사용 방법입니다.
//파이어베이스 연결에 혹시 오류가 있을 경우를 대비한 코드로 알아두면 됩니다.
if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}

export const firebase_db = firebase.database()

강의노트에 주어진 양식에서 내 정보만 적용해보자

 

4. [이미지 저장소 만들기]

파이어베이스 사이트에서 Storage를 만든다. 무거운 이미지들을 파이어베이스에 저장해서 보관할것이므로

-> images 폴더를 만들어서 이미지를 저장한다

이미지를 올린 후 해당 이미지를 눌러보면 파일 위치 - 저장소 위치를 볼수 있는데

앞으로 해당 위치의 이미지 주소를 활용해서 이미지를 올리면 된다

 

5. [리얼타임 데이터베이스 만들기]

리얼타임 데이터베이스? 우리가 배운 리스트, 딕셔너리 구조, 즉 JSON 형태로 저장/관리되는 데이터베이스 서비스 

다른말로 nosql 이라고 한다.

앞에 데이터베이스를 만든 후 파이어베이스 내 정보가 담긴 연결 정보에서

databaseURL이 리얼타임데이터베이스의 주소와 같다. 만약 없다면 직접 추가해야함 

 

위에 탭에 보면 데이터 / 규칙 / 백업 /사용량 탭이 있는데

규칙 탭에서 읽기와 쓰기를 true로 바꿔준다.

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

외부에 공개하겠다는 뜻으로, true로 해 놔야 리얼타임데이터베이스의 데이터를 가지고오고 수정,변경을 할수 있다.

변경 후 꼭 '게시'를 눌러 준다

 

데이터 탭에서 ...에서 JSON 가져오기를 해서 그동안 만들었던 data.json파일을 가져오면 완료.

 

6. [데이터 가져오기]

이제 파이어베이스에 데이터를 저장했으니 데이터를 가져와야 한다.

데이터를 가져오는 방법은 파이어바에스 사이트 우측 상단에 '문서로 이동' 을 눌러보면 파이어베이스 사용에 대한 문서들이 많이 나오는데, 

강의에서 나오는것과 사이트 내용이 바뀌어 있다.

최상위 Docs  ->  Reference  ->  JavaScript - version 8  -> firebase.database  -> DataSnapshot 으로 들어가보면

예제들이 나와있다.

//이번에 알게 된것은 지금 배우고 있는게 JavaScript 8 버전 기반이라는것인데, 9버전이라면 또 뭔가 달라질것 같다.

https://firebase.google.com/docs/reference/js/v8/firebase.database.DataSnapshot?hl=ko&authuser=0 

 

DataSnapshot | JavaScript SDK  |  Firebase JavaScript API reference

Reference for DataSnapshot

firebase.google.com

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

해석.

ref('/tip') 이 부분에서 /tip 영역에 가지고 오고 싶은 데이터의 주소를 넣어주면 됩니다. 이 주소 앞부분에는 https://sparta-myhoneytip.firebaseio.com/ 과 같은 기본 주소가 생략되어 있습니다.

firebaseConfig.js에서 이미 파이어베이스 계정을 세팅했기 때문에, 기본 주소와 정보들은 앱 내에서 사용하는 파이어베이스 함수들이 알고 있는 상태입니다


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

이 코드는 서버리스를 이용하여 데이터베이스를 조회하기 위해,
파이어베이스 측에서 정해놓은 API 사용방법입니다. 따라서 우린 공식 문서 그대로 사용 방법을 적용해야 합니다.

조회한 데이터는 snapshot 부분에 담겨서 {} 내부에서 사용할 수 있는데, 그 중 실제 우리에게 필요한 데이터는 snapshot.val()로 가져와 변수에 담아 사용할 수 있습니다.

이제 MainPage.js에 적용해야 하는데, 앞서 아까전에 firebaseConfig.js 파일에 복사해서 넣은 코드를 보면 가장 하단에 

export const firebase_db = firebase.database()

이렇게 익스포트해둔 코드가 있는데 이 코드가 우리가 사용할 파이어베이스 서비스의 정보를 (데이터베이스, 스토리지 등등) 함축적으로 하나의 firebase_db라는 변수에 넣어둔것을 내보내고 있는것이다. 

 

MainPage.js

import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';

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"

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 ? <Loading/> :  (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */

    <ScrollView style={styles.container}>
      <StatusBar style="light" />
      {/* <Text style={styles.title}>나만의 꿀팁</Text> */}
      <Text style={styles.weather}>오늘의 날씨: {weather.temp + '°C   ' + weather.condition} </Text>
       <TouchableOpacity style={styles.aboutButton} onPress={()=>{navigation.navigate('AboutPage')}}>
          <Text style={styles.aboutButtonText}>소개 페이지</Text>
        </TouchableOpacity>
      <Image style={styles.mainImage} source={{uri:main}}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
      <TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04} onPress={()=>{navigation.navigate('LikePage')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         {
          cateState.map((content,i)=>{
            return (<Card content={content} key={i} navigation={navigation}/>)
          })
        }
       
      </View>
   
    </ScrollView>)
}

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
  }
});

가장 상단에 import {firebase_db} from "../firebaseConfig" 파이어베이스를 임포트 부터 해 두고,

useEffect부분을 보면

useEffect(()=>{
    navigation.setOptions({
      title:'나만의 꿀팁'
    })  
    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)
  },[])

기존에 setState에서 바로 data.tip에서 데이터를 가져왔던 부분은 주석처리 되었고, 위에

 firebase_db.ref('/tip').once('value').then((snapshot) => {
          console.log("파이어베이스에서 데이터 가져왔습니다!!")
          let tip = snapshot.val();

부분이 추가 되었다.  

firebase_db에서 .ref('') 부분은 파이어베이스 내의 위치를 말함, .once('value')는 값을 가져오겠다는 규칙, .then( (snapshot) 부분은 파이어베이스에서 가져온 데이터값이 저장되는 매개변수다.

snapshot에는 파이어베이스의 데이터를 담고 있는데 거기서 구체적인 값을 가져오기위해 snapshot.val() 까지 연결해서 tip변수에 값을 담고 있다. (공식문서에 적혀있는 공식이므로 그대로 가져다 쓰면 된다)

 

아래 상태 관리부분에선 setState(tip)으로 상태관리를 하는데,

기존에는 data.json파일에서 tip 위치의 데이터를 바로 넣었다면, 이번엔 파이어베이스 서버에서 firebase_db로 데이터를 가져와서 snapshot.val에 담겨진 데이터를 tip에 넣어서 상태관리를 하고 있다.

-------------------------

하단 코드에    },1000)  부분이 있는데 지연시간을 주는 부분으로 사용자의 스마트폰 성능에따라 앱을 원활하게 돌아가도록 일부러 주어지는 부분이다.

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)
      // },1000)

  },[])

그리고 파이어베이스에서 데이터를 가져올때 setTimeout를 쓸 필요가 없다.

강의노트 내용 참고,

사용자들 마다 네트워크 상태가 모두 다를 것이기 때문에, 무조건 몇초 뒤에 실행 시키는 setTimeout 함수 기능보다, 파이어베이스 API의 실행 완료 시간에 맡겨두는 편히 더 나아보입니다. 이러한 이유로 setTimeout 함수를 사용하지 더이상 사용하지 않습니다. 따라서 파이어베이스로부터 즉, 서버로부터 데이터를 가져와 준비할 때까지 로딩 화면을 보여줬다가, 데이터가 준비가되면 실제 본 화면을 보여주는 자연스러운 서비스 환경을 사용자들에게 제공해줄 수 있습니다.

 

7. 특정 데이터 읽기

 기존 Card.js를 보면

export default function Card({content,navigation}){
    return(
        //카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
        <TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',content)}}></TouchableOpacity>

navigation 함수로 디테일페이지로 넘어가고 content 즉 전체 데이터를 넘기고 있다. 데이터가 몇개 없으면 괜찮겠지만,

데이터가 많아진다면 화면이 바뀌는데 오래 걸릴 것이기 때문에 정확히 특정한 데이터만 넘겨줄 필요가 있다.

특정 데이터 넘기는 Card.js

export default function Card({content,navigation}){
    return(
        //카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
        <TouchableOpacity style={styles.card} onPress={()=>{navigation.navigate('DetailPage',{idx:content.idx})}}>

코드를 보면 기존엔 content 전체 값을 넘겼다면, 이번엔 idx 값을 지정해서 특정한 데이터만 넘기고 있다.

DetailPage를 보면

 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)
        });
    },[])

네비게이션으로 넘어온 route.params에 있는 idx 데이터를 idx로 다시 선언해주고, 파이어베이스에 있는 데이터베이스에서 특정 주소에 있는 데이터를 가져오는데, /tip주소/ + idx (예를들어 2번카드를 눌렀다면 2가 넘어갈것이고) 까지 주소로 지정이 되어서 tip/2 위치의 데이터만 특정해서 가져오게 된다.

 

7. 데이터 쓰기 - 사용자 고유정보 가져오기

이용자들이 꿀팀 찜하기 버튼을 눌러서 찜한 데이터만 보고자 한다면,사용자의 고유 ID 값 정도의 정보를 알아야 누가 무엇을 찜해두었는지 정보를 저장해 둔 후에 그 사람이 찜한 목록만 보여줄수 있다.

그렇다면 사용자의 고유한ID는 어떻게 알수 있을까? -> Expo API를 이용하면 사용자의 ID를 생성해서 누가 찜했는지 알수 있게 해준다.

https://docs.expo.dev/versions/latest/sdk/application/#api

 

Application - 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-application 로 설치하고, 아래 코드를 사용할 것인데,

import * as Application from 'expo-application';
const isIOS = Platform.OS === 'ios';

let uniqueId;
if(isIOS){
  let iosId = await Application.getIosIdForVendorAsync();
  uniqueId = iosId
}else{
  uniqueId = Application.androidId
}

console.log(uniqueId)

자세히 보면 지금 설치한 Application 말고도 Platfom 이란게 있다.

상단에 임포트 한 부분을 보면 react-native에서 Platfom 이라는 도구도 꺼내올수 있다.

import { StyleSheet, Text, View, Image, ScrollView,TouchableOpacity,Alert,Share,Platform } from 'react-native';

Platfom에서 OS를 꺼내오고 있는데, 이 도구는 어떤 디바이스 운영체제에서 이 앱을 실행하고 있는지 알아내는 도구다.

이게 왜 필요하냐면, expo-application 공식문서를 보면 ios 에서 고유번호를 가져오는것과 안드로이드 갖고오는 방식이 다르기 때문에 분기처리를 해야 한다.

const isIOS = Platform.OS === 'ios';

상황 연산자를 사용해서 이 기기OS가 ios면 'true'가, ios가 아니면 'false'가 isIOS로 담기도록 선언했다.

if (isIOS) 가 true면 Application.getIosIdForVendorAsync() Application API를 통해 getIosIdForVendorAsync() ios용 ID값을 가져와서 id값을 uniqueId로 넣고, false면  uniqueId = Application.androidId 를 통해서 마찬가지로 uniqueId 변수 안에 고유한 사용자 번호를 얻을수 있다. ID값을 가져온 후에 uniqueId로 저장해야 하므로 await를 사용했다.

 

8. 데이터 쓰기

이제 사용자 고유 번호도 알고 있으니 찜하기 버튼을 눌렀을때 데이터가 저장하도록 만들어야 한다.

DetailPage에서 기존에 버튼을 눌렀을때 onPress={()=>popup()로 팝업이 뜨도록 되어 있던걸 수정해서 like 함수가 실행되도록 바꿔준다

<TouchableOpacity style={styles.button} onPress={()=>like()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
 
 
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)
        });
    },[])

    const like = async () => {
       
        // like 방 안에
        // 특정 사용자 방안에
        // 특정 찜 데이터 아이디 방안에
        // 특정 찜 데이터 몽땅 저장!
        // 찜 데이터 방 > 사용자 방 > 어떤 찜인지 아이디
        let userUniqueId;
        if(isIOS){
        let iosId = await Application.getIosIdForVendorAsync();
            userUniqueId = iosId
        }else{
            userUniqueId = await Application.androidId
        }

        console.log(userUniqueId)
           firebase_db.ref('/like/'+userUniqueId+'/'+ tip.idx).set(tip,function(error){
             console.log(error)
             Alert.alert("찜 완료!")
         });
    }

최상단에 임포트 하는것을 잊으면 안된다

import * as Application from 'expo-application';
const isIOS = Platform.OS === 'ios';
 
await를 사용할것이므로 async()를 써줘야 한다. Platform.OS를 통해서 userUniqueId를 가져온다
firebase_db.ref('/like/'+userUniqueId+'/'+ tip.idx).set(tip,function(error){
             console.log(error)
             Alert.alert("찜 완료!")
         });
 
이 부분이 값을 저장하는 부분이다.
 
'/like/'+userUniqueId+'/'+ tip.idx이 부분이 데이터가 저장될 방을 생성하는 부분이고,
.set() 부분 첫 번째 인자로는, 저장할 데이터(tip 상태)를 두 번째 인자로는 혹시 에러가 발생하면 나중에 처리 할 함수를 둔다.

-----------------------------

//핵심적인것은 단 몇줄의 코드지만 그걸 원활하게 사용하기 위해서 여러가지 코드를 사용해야해서 간단한게 없는것 같다.