data lab

react native - Expo 공부 3 - expo firebase 연동하기 - 실패

LAB 관리자 2024. 7. 8. 19:24
반응형

저번 글에서 일단 파이어베이스 연동안하고 클라이언트 측에서 코드만 변하는걸로 배포할거라했는데, 이게 진짜 내가 얼마나 무지한지 알려주는거다. 그런식으로 배포하면 사용자가 작성하고 앱끄면 다 날라가는데 앱이 무슨 의미가 있나 도대체? 웃기기만 하다.

 

expo랑 파이어베이스랑 어떻게 연동하는건지 좀 찾아보자

 nxp create-expo-app firebase-integration

이게 expo 프로젝트 생성이래. 그래? 이건 처음 할 때부터 이렇게 해야하는거같은데 흠~

https://kimnerd.tistory.com/193

 

[React Native] Expo & Firebase 연동하기 (RDS 사용법)

 

kimnerd.tistory.com

이사람 블로그 좋은거같다

 

1. firebase 설치 및 연동

npx expo intall firebase

yarn add firebase

얀은 버전 호환 이슈 없나?

 

2. firebaseConfig.js 파일 생성 및 적용

rds랑 app, 그리고 api키 같은거 써줘야되나봐. 음~

 

3. 사용하는 파이어베이스 기능 선언 및 실행

 

ref() 랑 set() 으로 post 하느걸 찾아보니, 데이터 구조를 JSON 형태로 직접 정의할 수 있는게 특징이래. 이놈의 JSON이 뭘까?

JSON이란

JavaScript Object Notation

구조 : 키-값 쌍의 집합

이거 약간 객체리터럴, 맵, 딕셔너리랑 비슷하네. 그냥 호환성이 좋은 자바스크립트 자료구조네

 

공부해야하는 것

 

1. props

2. 구조분해할당

3. useState 훅에 대해

 

https://velog.io/@minbrother/React-Native-10.-Firebase%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0

 

React-Native [10. Firebase로 데이터 관리하기]

Firebase 특징 구글에서 만든 서버리스(Serverless) 서비스 > Serverless 서버가 없다는 뜻이 아닌, 서버를 직접 만들 필요가 없다는 뜻! 필요한 서버 기능을 제공하는곳에서 서비스를 사용하기 때문에 매

velog.io

 

expo install firebase.음...


클로드가 알려주는 기존 expo 프로젝트랑 파이어베이스 연동하는 법

 

1. 필요한 패키지 설치(rds는 없는거같은데?)

npx expo install firebase @react-native-firebase/app @react-native-firebase/auth @react-native-firebase/firestore

 

fb RDS : npx expo install @react-native-firebase/database

2. 개발자 빌드 설치? 개발자 빌드가뭐야

npx expo install expo-dev-client

 

3.eas.json 설정

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    }
  }
}

 

4. 개발빌드 생성

eas build --profile development --platform android
//혹은
eas build --profile development --platform ios

 

5. firebase 프로젝트 생성하고 앱 등록하기

//파베 리얼타임 데이터베이스 Rules 탭에 아래같이 추가. 더 엄격한 보안규칙이 필요하대. 어뜨카니
{
  "rules": {
    ".read": true,
    ".write": true
  }
}

 

6. 구성파일 다운로드

 

  • Android: google-services.json을 /android/app/ 디렉토리에 저장
  • iOS: GoogleService-Info.plist를 Xcode 프로젝트에 추가
//7. Android 설정 (android/build.gradle):
buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:4.3.15'
  }
}

//8. Android 설정 (android/app/build.gradle):
apply plugin: 'com.google.gms.google-services'

 

 

9. ios 설정

pod 'Firebase', :modular_headers => true
pod 'FirebaseCore', :modular_headers => true
pod 'GoogleUtilities', :modular_headers => true

 

10. App.js 에서 Firebase 초기화

import { initializeApp } from 'firebase/app';
import database from '@react-native-firebase/database';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  // Firebase 콘솔에서 가져온 설정
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);

서버와 통신 기본

1. GET : 데이터 조회

2. POST : 새 데이터 생성

3. PUT : 기존 데이터 전체적으로 수정

4. PATCH : 기존 데이터 부분 수정

5. DELETE : 데이터 삭제

 

Js에서 위 요청을 보내는 주요 방법들

1. fetch API : 현대 브라우저 내장 API, Promise 기반

2. axios : 많이 쓰는 서드파티 라이브러리

3. XMLHttpRequest : 오래된 방식. ?

 

Firebase Realtime Database는

set() : put 과 유사

update() : patch

push() : post

remove() : delete

on() 또는 once() : get

 

 

SDK란 : Software Development Kit

 


자 이제 회피하지말고 Firebase 진입하자.

 

진입해서 프로젝트 하나 생성하고 ga 연결했다.

 

1. app.json에 android 앱 패키지 추가

앱의 안드로이드 패키지 이름을 등록하라고 해서 내 expo 프로젝트에서 app.json으로 간다음에 "android" : 돼있는데다가 

 "android": {
      "adaptiveIcon": {
        "foregroundImage": "----",
        "backgroundColor": "----"
      },
      "package": "com.내깃허브이름.프로젝트이름"
      },

이렇게 패키지 이름을 추가해줬다. 그리고 다시 파이어베이스 콘솔로 돌아가 안드로이드 패키지명을 입력했다.

ios 인 경우에는 ios 번들 ID (안드로이드 앱 패키지랑 비슷한듯) 입력하면된다.

2. google.services.json 다운 후 프로젝트 루트 디렉토리에 넣기

그냥 파베 콘솔에서 다운받아서 엑스포 프로젝트 루트 디렉토리에 넣었다. 따로 설명할게 없음

 

3. Firebase SDK 추가

참고로 SDK는 Software Development Kit 의 약자라고한다. (라이브러리, API, 문서, 코드샘플, 디버깅도구, 개발환경 등이 포함된대) 암튼 파이어베이스 SDK가 내가 아까 넣었던 구글 서비스 json에 액세스 하려면 Google service 플러그인이 필요하대. 그래서 Kotlin DSL을 선택할건지 Groovy를 선택할건지 묻길래 어지러워졌다. 클로드한테 물어봤더니 Expo 에서는 신경 안써도 된단다. 

  • expo-firebase-core 설치
  • 필요한 Firebase 패키지 설치 (예: expo-firebase-analytics)
  • app.json에 Firebase 구성 추가
  • 앱에서 Firebase 초기화 코드 작성

대신 이 네가지를 해야한단다.

npx expo install expo-firebase-core
npx expo install expo-firebase-core expo-firebase-analytics expo-firebase-database

 

그리고 app.json파일을 이렇게 바꿔줬다.

{
  "expo": {
    "name": "expo240705",
    "slug": "expo240705",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.jongwoo01.expo240705",
      "googleServicesFile": "./GoogleService-Info.plist"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.jongwoo01.expo240705",
      "googleServicesFile": "./google-services.json"
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "plugins": [
      "@react-native-firebase/app",
      "@react-native-firebase/database",
      "@react-native-firebase/analytics"
    ]
  }
}

ios 는 따로 안할건데 충돌안하겠지?

 

4. 앱에서 firebase 초기화 코드 작성

이건 아까 클로드가 하라고한대로 했으니까 됐겠지?

 

5. 파이어베이스 spark에서 blaze 요금제로 변경

하고, app hosting 이랑 Realtime database 쓰려고 일단 클릭은 했다.

근데 app hosting은 무슨 깃허브 어쩌고를 가져오라네

 

6. firebaseConfig.js

에 뭐가 들어가야되나 찾아보니까. 안드로이드 앱으로는 없고, 웹앱을 생성해야 api 키랑 이런게 생기는 갑네? 안드로이드 앱 호스팅을 선택했었는데 다시 웹앱만들기에 들어간다음에 나오는걸 내가 expo 프로젝트에 생성한 firebaseConfig.js 파일에다가 복사 붙여넣기 했다.

npm install firebase

npm install -g firebase-tools

firebase login

firebase init

firebase deploy

이렇게 하래 콘솔에서.

로그인은 이미 돼있대고. init 한다음에 RDS 랑 hosting 만 체크해서 넘어갔다. 그리고 만들어놨던 파이어베이스 프로젝트에 연결했다.

콘솔에서 RDS 세팅을 안했다고 cli에서 하란다.

 

asia-southeast1 로 했다

 

잔뜩 썼는데 다 날라갔다. 결론은 expo형태로 다시 똑같이 만들거다. 코드는 유지하고.

//app.json

{
  "expo": {
    "name": "expo240705",
    "slug": "expo240705",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.jongwoo01.expo240705",
      "googleServicesFile": "./GoogleService-Info.plist"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.jongwoo01.expo240705",
      "googleServicesFile": "./google-services.json"
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "plugins": [
      ["expo-firebase-core",
        {
          "androidPackageName": "com.jongwoo01.expo240705",
          "iosAppId": "1:1234567890:ios:XXXXXXXXXXXXXXX" // 여기에 실제 iOS App ID를 입력하세요
        }
      ],
      "expo-firebase-database",
      "expo-firebase-analytics"
    ],
    "extra": {
      "eas": {
        "projectId": "YOUR_EAS_PROJECT_ID" // EAS 프로젝트 ID를 여기에 입력하세요
      }
    }
  }
}

app.js

import React, { useState, useEffect } from "react";
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  FlatList,
  StyleSheet,
  SafeAreaView,
  KeyboardAvoidingView,
  Platform,
} from "react-native";
import { initializeApp } from "firebase/app";
import * as database from 'expo-firebase-database';

// Firebase 설정
const firebaseConfig = {
  // Firebase 콘솔에서 가져온 설정을 여기에 넣으세요
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  databaseURL: "YOUR_DATABASE_URL",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID",
};

// Firebase 초기화
const app = initializeApp(firebaseConfig);

const Post = ({ item }) => (
  <View style={styles.post}>
    <View style={styles.postHeader}>
      <Text style={styles.postNumber}>#{item.id}</Text>
    </View>
    <View style={styles.postContent}>
      <Text style={styles.storeName}>{item.storeName}</Text>
      <Text>{item.content}</Text>
    </View>
    <View style={styles.postFooter}>
      <Text style={styles.timestamp}>
        {new Date(item.timestamp).toLocaleString()}
      </Text>
    </View>
  </View>
);

const App = () => {
  const [posts, setPosts] = useState([]);
  const [storeName, setStoreName] = useState("");
  const [content, setContent] = useState("");

  useEffect(() => {
    const onValueChange = database()
      .ref("/posts")
      .orderByChild("timestamp")
      .on("value", (snapshot) => {
        const data = snapshot.val();
        const postList = data
          ? Object.keys(data).map((key) => ({
              id: key,
              ...data[key],
            }))
          : [];
        setPosts(postList.reverse());
      });

    return () => database().ref("/posts").off("value", onValueChange);
  }, []);

  const addPost = async () => {
    if (storeName && content) {
      try {
        const newPostRef = database().ref("/posts").push();
        await newPostRef.set({
          storeName,
          content,
          timestamp: database.ServerValue.TIMESTAMP,
        });
        setStoreName("");
        setContent("");
      } catch (error) {
        console.error("Error adding post: ", error);
      }
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.headerText}>식당 대신 전해드립니다</Text>
      </View>
      <FlatList
        data={posts}
        renderItem={({ item }) => <Post item={item} />}
        keyExtractor={(item) => item.id}
        style={styles.postList}
      />
      <KeyboardAvoidingView
        behavior={Platform.OS === "ios" ? "padding" : undefined}
        keyboardVerticalOffset={Platform.OS === "ios" ? 64 : 0}
        style={styles.form}
      >
        <TextInput
          style={styles.input}
          placeholder="가게 이름"
          value={storeName}
          onChangeText={setStoreName}
        />
        <TextInput
          style={[styles.input, styles.contentInput]}
          placeholder="제보내용을 적어주세요"
          value={content}
          onChangeText={setContent}
          multiline
        />
        <TouchableOpacity style={styles.button} onPress={addPost}>
          <Text style={styles.buttonText}>글 작성</Text>
        </TouchableOpacity>
      </KeyboardAvoidingView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f0f0f0",
  },
  header: {
    backgroundColor: "#4a4a4a",
    padding: 20,
    alignItems: "center",
  },
  headerText: {
    color: "white",
    fontSize: 20,
    fontWeight: "bold",
  },
  postList: {
    padding: 20,
  },
  post: {
    backgroundColor: "white",
    borderRadius: 8,
    padding: 15,
    marginBottom: 15,
    shadowColor: "#000",
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.2,
    shadowRadius: 1,
    elevation: 2,
  },
  postHeader: {
    flexDirection: "row",
    justifyContent: "flex-end",
    marginBottom: 10,
  },
  postNumber: {
    fontSize: 14,
    color: "#777",
  },
  postContent: {
    marginBottom: 10,
  },
  storeName: {
    fontWeight: "bold",
    marginBottom: 5,
  },
  postFooter: {
    alignItems: "flex-end",
  },
  timestamp: {
    fontSize: 12,
    color: "#999",
  },
  form: {
    padding: 20,
    backgroundColor: "#f9f9f9",
    borderTopWidth: 1,
    borderTopColor: "#eee",
  },
  input: {
    backgroundColor: "white",
    borderRadius: 4,
    borderWidth: 1,
    borderColor: "#ddd",
    padding: 10,
    marginBottom: 10,
  },
  contentInput: {
    height: 100,
    textAlignVertical: "top",
  },
  button: {
    backgroundColor: "#4a4a4a",
    borderRadius: 4,
    padding: 15,
    alignItems: "center",
  },
  buttonText: {
    color: "white",
    fontWeight: "bold",
  },
});

export default App;
반응형