본문 바로가기
Web Service/React

[to-do-list] #3. CRUD

by junnykim 2022. 1. 6.

https://nomadcoders.co/nwitter/

 

트위터 클론코딩 – 노마드 코더 Nomad Coders

React Firebase for Beginners

nomadcoders.co

~3.6까지 들었는데, 이 부분은 firebase랑 연동해서 하는 작업이 많음~

CRUD = Create Read Update Delete

 

App.js

import React, { useEffect, useState } from 'react';
import AppRouter from 'components/Router';
import {authService} from 'fbase';

function App() {
  const [init, setInit]=useState(false);
  //const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [userObj, setUserObj] = useState(null);
  useEffect(() => {
    authService.onAuthStateChanged((user) => {
      if(user){
        //setIsLoggedIn(true);
        setUserObj(user);
      /*} else {
        setIsLoggedIn(false);*/
      }
      setInit(true);
    });
  }, [])

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

export default App;

user가 쓴 게시물을 user가 삭제할 수 있도록 userObj를 추가했다.

 
const [userObj, setUserObj] = useState(null);

추가되면서 바뀐 부분

useEffect(() => {
    authService.onAuthStateChanged((user) => {
      if(user){
        //setIsLoggedIn(true);
        setUserObj(user);
      } /*else {
        setIsLoggedIn(false);
      }*/
      setInit(true);
    }

authService가 바뀐다면, user에 setUserObj를 넣는다. 로그인 되면 onAuthStateChanged가 호출되고, 로그인 한 user를 받게 된다. 

어플리케이션이 시작하고 준비되면 setInit(true)로 시작한다. user를 얻어야 로그인이 되는데, 어떤 user도 받지 못하면 로그인할 수 없다. 만약 user를 얻게 되면 setUserObj를 사용한다. userObj가 없으면 로그인되지 않는다.

<AppRouter isLoggedIn={isLoggedIn} userObj={userObj} />
-> <AppRouter isLoggedIn={Boolean(userObj)} userObj={userObj} />

이 경우에는 원한다면 isloggedin을 지워도 된다. 대신 isloggedin대신에 userObj를 넣어주면 된다.

AppRouter이동 시, userObj의 값을 true,false로 바꾸어 보낸다.

 

Router.js

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

const AppRouter = ({ isLoggedIn, userObj }) => {
    return(
        <Router>
            {isLoggedIn && <Navigation />}
            <Switch>
                {isLoggedIn ? (
                <>
                    <Route exact path="/"> 
                        <Home userObj={userObj} />
                    </Route>
                    <Route exact path="/profile">
                        <Profile />
                    </Route>

                </> 
                ) : (
                    <>
                    <Route exact path="/">
                      <Auth />
                    </Route>
                  </>
                )};
            </Switch>
        </Router>
    )
};

export default AppRouter;

App.js의 설명에서 언급했지만, userObj를 넣어줌으로써 user인 경우에만 이동할 수 있도록 한다. 

 

Home.js

import { dbService } from "fbase";
import React, { useState, useEffect } from "react";
import Nweet from "components/Nweet";

const Home = ({ userObj }) => {
    const [nweet, setNweet] = useState("");
    const [nweets, setNweets] = useState([]);
    /*const getNweets = async () => {
        const dbNweets = await dbService.collection("nweets").get();
        dbNweets.forEach((document) => {
        const nweetObject = {
            ...document.data(),
            id: document.id,
        };
        setNweets((prev) => [nweetObject, ...prev]);
    });
  };*/
  useEffect(() => {
    /*getNweets();*/
    dbService.collection("nweets").onSnapshot((snapshot) => {
        const nweetArray = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setNweets(nweetArray);
      });
  }, []);
    const onSubmit = async (event) => {
        event.preventDefault();
        await dbService.collection("nweets").add({
          /*nweet,*/
          text: nweet,
          createdAt: Date.now(),
          creatorId: userObj.uid,
        });
        setNweet("");
      };
      const onChange = (event) => {
        const {
          target: { value },
        } = event;
        setNweet(value);
      };

      return (
        <div>
          <form onSubmit={onSubmit}>
            <input
              value={nweet}
              onChange={onChange}
              type="text"
              placeholder="What's on your mind?"
              maxLength={120}
            />
            <input type="submit" value="Upload" />
          </form>
          <div>
            {nweets.map((nweet) => (
                /*<div key={nweet.id}>
                <h4>{nweet.text}</h4>
              </div>*/
                 <Nweet
                    key={nweet.id}
                    nweetObj={nweet}
                    isOwner={nweet.creatorId === userObj.uid}
               />
            ))}
        </div>
        </div>
      );
    };
    export default Home;

라우터에 userObj를 넣어줌으로써 Home이 userObj prop를 갖게 되었다!

Home에서는 업로드할 수 있게 한다. 이번 장에서 가장 많이 변화한 부분 인 것 같다. (이 때 업로드는 그냥 편하게 nweet이라 하겠음.)

이 때 업로드는 firestore가 database가 되어 글 쓴 것들이 올라간다.

 

Snapshot

lister로 Snapshot을 사용한다.  Snapshot은 무언가가 일어나면(= 기본적으로 데이터베이스에 무슨 일이 있을 때), dbService.collection의 onSnpshot을 사용했다.

새로운 Snapshot을 받을 때 알리고 그 후에 state에 배열을 집어 넣는다.  

 

snapshot 전에는 주석처리에서 볼 수 있겠지만 getNweets를 사용했다.

getNweets

collection의 nweet에서 가지고 오도록 dbService.collection의 get을 사용했다. 

컴포넌트가 마운트 될 때,  get을 통해서 모든 것을 가지고 왔다.

dbNweets.forEach는 nweet의 객체를 만드는 것이다.  모든 document.data를 가지고, id도 주었음!

setNweets((prev)=> [nweetObject, ..prev]); 모든 dbNweets안에 있는 모든 document.data에 대해서 모든 이전 nweets에 대해 새로 작성한 트윗과, 그 이전의 것들을 가지고 온다!

 

onSubmit

dbService.collection의 add를 쓰면 추가할 수 있다. 이 때 그냥 추가하는 것이 아니라 여러가지 속성을 준다. collection(=database)의 이름은 nweets이고 내가 작성한 text는 nweet(=업로드)가 되는 것!

전송할 때 userObj를 활용한 id와 시간을 함께 전송한다. (onChange는 언급하지 않겠음)

 

 

Nweet.js

import React, { useState } from "react";
import { dbService } from "fbase";

const Nweet = ({ nweetObj, isOwner }) => {
  const [editing, setEditing] = useState(false);
  const [newNweet, setNewNweet] = useState(nweetObj.text);
  const onDeleteClick = async () => {
    const ok = window.confirm("Are you sure you want to delete this nweet?");
    if (ok) {
      await dbService.doc(`nweets/${nweetObj.id}`).delete();
    }
  };
  const toggleEditing = () => setEditing((prev) => !prev);
  const onSubmit = async (event) => {
    event.preventDefault();
    await dbService.doc(`nweets/${nweetObj.id}`).update({
      text: newNweet,
    });
    setEditing(false);
  };
  const onChange = (event) => {
    const {
      target: { value },
    } = event;
    setNewNweet(value);
  };
  return (
    <div>
      {editing ? (
        <>
          <form onSubmit={onSubmit}>
            <input
              type="text"
              placeholder="Edit your List"
              value={newNweet}
              required
              onChange={onChange}
            />
            <input type="submit" value="Update List" />
          </form>
          <button onClick={toggleEditing}>Cancel</button>
        </>
      ) : (
        <>
          <h4>{nweetObj.text}</h4>
          {isOwner && (
            <>
            
              <button onClick={onDeleteClick}>Delete list</button>
              <button onClick={toggleEditing}>Edit list</button>
            </>
          )}
        </>
      )}
    </div>
  );
};

export default Nweet;

컴포넌트는 두가지의 state를 가진다.

 

editing은 말 그대로 수정하고 있는지, 아닌지를 뜻한다.

newNweet는 input의 값을 수정할 수 있다. 이 경우엔 newNweet을 수정하는 것이다.

form을 submit할 때 dbservice가 collection을 한다. 어떤 collection에 데이터를 저장할지 지정하는 건 아주 중요!

댓글