[Firebase] serverless 앱 구현하기

이지수·2021년 9월 10일
2

sparta-app

목록 보기
4/5
post-thumbnail

이번 포스트에서는 firebase를 사용하여 앱에서 데이터 읽기, 쓰기, 삭제를 적용하는 찜버튼을 구현해보겠습니다.

서버리스(serverless)??

서버리스란 이름 그대로 서버가 없다는건 아닙니다. 서버를 직접 만들 필요가 없다는 정도로 이해하면 됩니다!
서버를 직접 구현, 구성 할 필요없이 필요한 서버 기능을 제공하는 곳에서 서비스를 사용하기만 하면 됩니다.

Firebase??


파이어베이스는 구글에서 만든 서버리스 서비스입니다.
오늘 사용할 데이터베이스, 이미지 파일 서버는 물론 푸시 알람 기능, 로그인 인증 기능 등등 아주 다양한 기능들이 준비되어 있습니다.

파이어베이스에 가입한 후 프로젝트까지 생성해 주세요!

파이어베이스를 앱에 연결하기

프로젝트가 생성되었으면, 파이어베이스에 iOS를 개발하고 있는지 안드로이드를 개발하고 있는지 웹을 개발하고 있는지 알려줘야 개발 중인 앱에 파이어베이스를 앱 연결할 수 있는 연결 정보를 줍니다.

파이어베이스 프로젝트 내에서 앱 생성하기


현재는 Expo에서 안드로이드, iOS 앱 전용으로 개발하고 있지 않기 때문에 3번째 웹앱을 선택하여 앱을 생성합니다.

이 부분에서는 호스팅 설정은 체크하지 않고 진행합니다.
그러면 최종적으로 연결정보를 줍니다!

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

파이어베이스를 이용할 수 있게 도와주는 expo 도구를 설치합니다.

expo install firebase

이후에 다음 위치에 firebaseConfig.js 파일을 하나 생성합니다.

파일에는 다음 내용을 넣습니다.
여기서 주의해야될 점은 firebaseConfig 부분의 연결 정보는 개인의 것을 입력해야됩니다. 연결 정보는 프로젝트 설정에서 확인할 수 있습니다!!

//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: "apiKey",
  authDomain: "sparta-myhoneytip-jisoo.firebaseapp.com",
  projectId: "sparta-myhoneytip-jisoo",
  storageBucket: "sparta-myhoneytip.appspot.com",
  messagingSenderId: "messagingSenderId",
  appId: "appId",
  measurementId: "measurementId"
};

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

export const firebase_db = firebase.database()

리얼타임 데이터베이스??

JSON 형태로 저장/관리되는 데이터베이스 서비스로 플랫폼과 실시간 데이터를 주고 받는 것에 특화되어 있습니다. 이 서비스를 사용 할 땐, 파이어베이스에서 제공해주는 함수들을 이용하기만 하면 데이터 저장/수정/삭제가 가능합니다.

리얼타임 데이터베이스 생성

리얼타임 데이터베이스를 생성 후 규칙에서 모두 true로 바꿔주세요.

데이터 업로드

오른쪽에 있는 토글 버튼을 눌러 JSON 가져오기를 선택합니다.

저는 다음 형태의 json파일을 넣어주었습니다.

그러면 데이터들이 딕셔너리 구조로 저장이 되며, 숫자 0,1,2,3,4...은 리스트의 형태로 문제가 차곡차곡 쌓여 있는 모습을 볼 수 있습니다.

데이터 쓰기

찝 버튼을 눌렀을 때 파이어베이스로 데이터를 보내 저장하도록 하겠습니다.(const like 부분)
DetailPage.js

import React,{useState,useEffect} from 'react';
import { StyleSheet, Text, View, Image, ScrollView,TouchableOpacity,Alert,Share } from 'react-native';
import * as Linking from 'expo-linking';
import {firebase_db} from "../firebaseConfig"
import Constants from 'expo-constants';

export default function DetailPage({navigation,route}) {
    let user_idx = Constants.installationId
    console.log(user_idx)
    const [tip, setTip] = useState({
        //테스트 데이터
        "idx":9,
        "category":"재테크",
        "title":"렌탈 서비스 금액 비교해보기",
        "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Frental.png?alt=media&token=97a55844-f077-4aeb-8402-e0a27221570b",
        "desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
        "date":"2020.09.09"
    })
    
    useEffect(()=>{
        console.log(route)
        navigation.setOptions({
            title:route.params.title,
            headerStyle: {
                backgroundColor: '#000',
                shadowColor: "#000",
            },
            headerTintColor: "#fff",
        })
        //넘어온 데이터는 route.params에 들어 있습니다.
        // 파이어베이스에서 tip 데이터를 모두 가져옵니다.
        const { idx } = route.params;
        firebase_db.ref('/tip/'+idx).once('value').then((snapshot) => {
            let tip = snapshot.val();
            setTip(tip)
        });
    },[])

    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("찜 완료!")
        });
    }
    
    //공유하기 기능
    const share = () => {
        Share.share({
            message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
        });
    }
    
    // 외부링크와 공유
    const link = () => {
        Linking.openURL("https://spartacodingclub.kr")
    }
    return ( 
        <ScrollView style={styles.container}>
            <Image style={styles.image} source={{uri:tip.image}}/>
            <View style={styles.textContainer}>
                <Text style={styles.title}>{tip.title}</Text>
                <Text style={styles.desc}>{tip.desc}</Text>
                <View style={styles.buttonGroup}>
                    <TouchableOpacity style={styles.button} onPress={()=>like()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>share()}><Text style={styles.buttonText}>팁 공유하기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>link()}><Text style={styles.buttonText}>외부 링크</Text></TouchableOpacity>
                </View>
                
            </View>
            
        </ScrollView>
    
    )
}
//스타일...

데이터 읽기

이제 찜한 목록을 보여주도록 하겠습니다.
여기서 주의해야될 점은 데이터가 없으면 에러가 나기 때문에 처리를 해줘야 합니다. 저는 로딩페이지를 따로 만들어 데이터가 없을 경우 로딩 화면을 띄우는 것으로 처리했습니다.

LikePage.js

import React, {useEffect, useState} from 'react';
import { ScrollView, Text, StyleSheet} from 'react-native';
import LikeCard from '../components/LikeCard';
import Loading from '../components/Loading'
import Card from '../components/Card';
import Constants from 'expo-constants';
import { firebase_db } from '../firebaseConfig';

export default function LikePage({navigation, route}) {
    const [tip, setTip] = useState([])
    const [ready,setReady] = useState(true)

    useEffect(()=>{
        navigation.setOptions({
            title:"꿀팁 찜"
        })
        //식별자
        const user_id = Constants.installationId;
      
        // 서버리스를 이용하여 데이터베이스를 조회하기 위해, 파이어베이스 측에서 정해놓은 API 사용방법입니다.
        firebase_db.ref('/like/'+user_id).once('value').then((snapshot) => {
            console.log("파이어베이스에서 데이터 가져왔습니다!!")
          // 조회한 데이터는 snapshot 부분에 담겨서 {} 내부에서 사용할 수 있는데, 
          // 그 중 실제 우리에게 필요한 데이터는 snapshot.val()로 가져와 변수에 담아 사용할 수 있습니다.
            let tip = snapshot.val();
            // 데이터가 없을 경우 에러처리
            if(tip.length > 0) {
                setTip(tip)
                setReady(false)
            }
  })
    })
  
    // 삼항연산자
    return ready ? <Loading/> : (
        <ScrollView style={StyleSheet.container}>
            {
                tip.map((content, i)=>{
                    return(<LikeCard key={i} content={content} navigation={navigation}/>)
                })
            }
        </ScrollView>
    )
}

const styles = StyleSheet.create({
    container:{
        backgroundColor:"#fff"
    }

useState??

저번 포스트에서는 useEffect는 화면이 그려진 다음 가장 먼저 실행되는 함수였습니다. 그럼 useState는 무엇일까요?

컴포넌트마다 데이터를 보유하고 관리 할 수 있습니다. 데이터라고 불러도 되지만, 리액트에서는 컴포넌트에서 보유/관리 되는 데이터를 상태라 부릅니다.
리액트에서 상태(state)는 리액트 라이브러리에서 제공해주는 useState로 생성하고 setState 함수로 정/변경 할 수 있습니다.
useState 함수 사용법은 다음과 같습니다.

const [현재 상태 값 변수, 상태 값 갱신 함수] = userState(상태 초기값);

데이터 삭제


찜버튼을 누르면 데이터베이스에 저장이되고 데이터를 불러와 화면에 출력해주는 것 까지는 완성이 되었습니다! 이제는 찜 해제를 누르면 꿀팁 찝 페이지에서 꿀팁이 없어지는 것을 구현해보겠습니다.

LikeCard.js

import React from 'react';
import {View, Image, Text, StyleSheet,TouchableOpacity, Alert} from 'react-native'
import {firebase_db} from "../firebaseConfig"
import Constants from 'expo-constants';


//MainPage로 부터 navigation 속성을 전달받아 Card 컴포넌트 안에서 사용
export default function LikeCard({content,navigation}){

    const detail = () => {
        navigation.navigate('DetailPage',{idx:content.idx})
    }
    
    // 꿀팁 삭제
    const remove = () => {
        const user_id = Constants.installationId;
        firebase_db.ref('/like/'+user_id+'/'+content.idx).remove().then(function(){
            Alert.alert("삭제 완료");
            navigation.navigate('LikePage')
        })
    }
    return(
        //카드 자체가 버튼역할로써 누르게되면 상세페이지로 넘어가게끔 TouchableOpacity를 사용
        <View style={styles.card}>
            <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 style={styles.buttonGroup}>
                    <TouchableOpacity style={styles.button} onPress={()=>detail()}><Text style={styles.buttonText}>자세히보기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>remove()}><Text style={styles.buttonText}>찜 해제</Text></TouchableOpacity>
              
                </View>
            </View>
        </View>
    )
}

삭제기능은 데이터베이스에서 가져올 데이터 뒤에 .remove()만 추가해주시면 됩니다!

이렇게 앱에서 데이터 읽기, 쓰기, 삭제를 구현해봤습니다!!🤗

0개의 댓글