기록하는 개발자

[React, Firebase] TwitterCloneCoding 2.0 New Tweet 작성 후 database에 저장하기 본문

Web/React

[React, Firebase] TwitterCloneCoding 2.0 New Tweet 작성 후 database에 저장하기

밍맹030 2021. 10. 12. 20:52
728x90

< firebase의 database 사용 >

 

cloue firestore 의 database는 NoSQL database 이다.
-규칙이 적고 유연하다는 장점이 있으나 이로 인해 sql의 자율성에 비해 제한사항이 있다는 단점 존재

특징
-collection 이라는 것을 가진다. 이는 컴퓨터의 폴더와 같은 역할
-document : 컴퓨터에 있는 문서와 비슷하며 필드, value, uid 등을 가진다.

 

< tweet 두 개 입력 후 화면 >

 

 

< fBase.js >

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
    apiKey: process.env.REACT_APP_API_KEY,
    authDomain: process.env.REACT_APP_API_AUTH_DOMAIN,
    projectId: process.env.REACT_APP_API_PROJECT_ID,
    storageBucket: process.env.REACT_APP_API_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_API_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_API_APP_ID,
};

initializeApp(firebaseConfig);

export const authService = getAuth();
export const dbService = getFirestore();

- db 사용을 위해 firebase/firestore를 import 해준다.

 

< App.js >

import React, {useEffect, useState} from 'react';
import AppRouter from "./Router";
import { authService } from "../fBase";
import {onAuthStateChanged} from '@firebase/auth';

function App() {
  const [init, setInit]=useState(false);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [userInfo, setUserInfo] = useState(null);
  useEffect(()=>{
      onAuthStateChanged(authService, (user) => {
        if(user!==null) {
          setIsLoggedIn(true);
          setUserInfo({user});
        }
        else setIsLoggedIn(false);
        setInit(true);
    });
  },[]);

  return (
    <>
      {init ? <AppRouter isLoggedIn={isLoggedIn} userInfo={userInfo}/> : "Initializing..."}
      <footer>&copy; twitter {new Date().getFullYear()}</footer>
    </>
  );
}

export default App;

1.

  const [userInfo, setUserInfo] = useState(null);

- 사용자 객체를 저장할 userInfo 변수를 null로 초기화한다.

 

2.

 useEffect(()=>{
      onAuthStateChanged(authService, (user) => {
        if(user!==null) {
          setIsLoggedIn(true);
          setUserInfo({user});
        }
        else setIsLoggedIn(false);
        setInit(true);
    });
  },[]);

- user 객체가 존재할 경우 userInfo 변수를 user 객체로 초기화 한다.

 

3.

 return (
    <>
      {init ? <AppRouter isLoggedIn={isLoggedIn} userInfo={userInfo}/> : "Initializing..."}
      <footer>&copy; twitter {new Date().getFullYear()}</footer>
    </>
  );

- AppRouter에 isLoggedIn과 함께 user 객체를 가진 변수 userInfo를 보낸다.

 

< Router.js >

import React from 'react';
import {Redirect, HashRouter as Router, Route, Switch} from "react-router-dom"
import Auth from "../routes/Auth";
import Home from "../routes/Home";
import Profile from "../routes/Profile";
import Navigation from "./Navigation"

const AppRouter = (isLoggedIn, {userInfo})=>{
    const flag=isLoggedIn[Object.keys(isLoggedIn)[0]];

    return( 
        <Router>
            {flag&&<Navigation/>}
            <Switch>
                {flag? (
                    <> 
                        <Route exact path="/"><Home userInfo={isLoggedIn.userInfo}/></Route>
                        <Route exact path="/profile"><Profile /></Route>
                        <Redirect from="*" to="/" />
                    </>
                ) : (
                    <>
                        <Route exact path="/"><Auth /></Route>
                        <Redirect from="*" to="/" />
                    </>
                )}
            </Switch>
        </Router>
    );
}
export default AppRouter;

 

1.

const AppRouter = (isLoggedIn, {userInfo})=>{

- App.js 로부터 넘겨바든 isLoggedIn과 userInfo 객체

 

2.

<Switch>
    {flag? (
        <> 
            <Route exact path="/"><Home userInfo={isLoggedIn.userInfo}/></Route>
            <Route exact path="/profile"><Profile /></Route>
            <Redirect from="*" to="/" />
        </>
    ) : (
        <>
            <Route exact path="/"><Auth /></Route>
            <Redirect from="*" to="/" />
        </>
    )}
</Switch>

- login이 true인 경우의 라우팅에서 Home component에 userInfo를 전달한다.

 

 

< Home.js >

import { dbService } from "fBase";
import React, { useEffect, useState } from "react";
import { collection, addDoc, query, onSnapshot, orderBy, serverTimestamp } from '@firebase/firestore'
import Tweet from "../components/Tweet";

const Home = ({userInfo}) => {
    const [tweet, setTweet] = useState('');
    const [tweets, setTweets] = useState([]);

    useEffect(() => {
        const q = query(collection(dbService, 'tweets'), orderBy('createdAt', 'desc'))
        const unsubscribe = onSnapshot(q, (querySnapshot) => {
            const nextTweets = querySnapshot.docs.map((document) => {
                return {
                    id: document.id,
                    ...document.data(),
                }
            })
            setTweets(nextTweets);
        })
        return () => {
            unsubscribe();
        }
    }, [])

    const OnSubmit = async (e) => {
        e.preventDefault()
        const docRef = await addDoc(collection(dbService, 'tweets'), {
            text : tweet,
            createdAt: serverTimestamp(),
            creatorId : userInfo[Object.keys(userInfo)[0]].uid,
        })
        console.log('Document written with ID: ', docRef.id);
        setTweet('');
    }

    const OnChange = (e) => {
        const { target: { value } } = e
        setTweet(value);
    }

    return (
        <div>
            <form onSubmit={OnSubmit}>
                <input type="text" placeholder="What's on your mind?" maxLength={120} onChange={OnChange} value={tweet} />
                <input type="submit" value="Nweet" />
            </form>
            <div>
                {tweets.map((tweet) => (
                    <Tweet key={tweet.id} 
                    tweetObj={tweet} 
                    isOwner={tweet.creatorId===userInfo[Object.keys(userInfo)[0]].uid}
                    />
                ))}
            </div>
        </div>
    )
}
export default Home;

 

1.

const Home = ({userInfo}) => {
    const [tweet, setTweet] = useState('');
    const [tweets, setTweets] = useState([]);

- Routes.js로부터 userInfo 객체를 받는다.

 

 tweet useState  :  새로운 값(tweet value)을 입력하여 Database에 넣는 useState 

 tweets useState  : Database에서 값을 가져오기 위한 useState

 

-  tweets 는 빈 배열로 초기화한다. 이에 database에서 값들을 불러올 때 database collection의 Document를 하나씩 가져와서 배열안에 넣어 저장하고 화면으로 불러온다.

- database에서 document를 가져오기 위해서는 collection에서 get method를 사용한다.

 

2.

 const OnSubmit = async (e) => {
        e.preventDefault()
        const docRef = await addDoc(collection(dbService, 'tweets'), {
            text : tweet,
            createdAt: serverTimestamp(),
            creatorId : userInfo[Object.keys(userInfo)[0]].uid,
        })
        setTweet('');
    }

- tweet을 작성하고 submit 을 하면 해당 tweet의 내용(text), 작성된 시간(createdAt), 작성자의 id(creatorId)가 database에 저장된다.

- 새 tweet을 database에 저장한 후 tweet form을 비워준다.

 

3.

    const OnChange = (e) => {
        const { target: { value } } = e
        setTweet(value);
    }

- value가 form에 입력되면 해당 event를 감지하여 tweet state 를 value로 초기화한다.

 

 

4. 실시간으로 데이터를 데이터베이스에서 가져오기

공식 문서 link : Collection   QuerySnapshot  onSnapshot

  useEffect(() => {
        const q = query(collection(dbService, 'tweets'), orderBy('createdAt', 'desc'))
        const unsubscribe = onSnapshot(q, (querySnapshot) => {
            const nextTweets = querySnapshot.docs.map((document) => {
                return {
                    id: document.id,
                    ...document.data(),
                }
            })
            setTweets(nextTweets);
        })
        return () => {
            unsubscribe();
        }
    }, [])

 q  : createdAt 항목에 따라 내림차순으로 dbService(firebase db)로부터 tweet를 가져와 query로 저장한다.

 unsubscrib  : onSnapshot을 통해 db에 무슨 일이 있을 때 알림을 받는다.

 nextTweets  : querySnapshot 객체를 map을 통해 순회하며 id와 data를 


- 새로운 snapshot을 받을 때 id와 doc.data를 가지는 nextTweets라는 배열을 만든다.

- 후에 tweets배열을 nextTweets 배열로 초기화한다.
- return 내부에서는 tweets배열을 이용하여 Tweet component를 만든다.

 

 

5.

    return (
        <div>
            <form onSubmit={OnSubmit}>
                <input type="text" placeholder="What's on your mind?" maxLength={120} onChange={OnChange} value={tweet} />
                <input type="submit" value="Nweet" />
            </form>
            <div>
                {tweets.map((tweet) => (
                    <Tweet key={tweet.id} 
                    tweetObj={tweet} 
                    isOwner={tweet.creatorId===userInfo[Object.keys(userInfo)[0]].uid}
                    />
                ))}
            </div>
        </div>
    )
}

- tweets 배열을 map으로 순회하며 Tweet component를 생성한다.

  → 각 tweet 마다 id, tweet(text 내용), userId를 가지며 Tweet component에서는 각 tweet에 대한 수정과 삭제가 가 다루어진다.

728x90