Firebase firestore로 CRUD구현해보기(v9)

Suyeon Pi·2021년 10월 7일
2
post-thumbnail

노마드코더 트위터클론코딩강의를 보면서 v9 버전으로 코드를 변경하여 변경한 코드 일부만 작성하였습니다.

Firestore Database

Firebase에서 database로 Firestore Database와 Realtime Database를 제공한다. 둘다 실시간으로 데이터를 받아올 수 있지만 가장 큰 차이점은 데이터의 형태이다. Realtime database에서 데이터는 JSON형태로 받아왔다면 Firestore에서는 collection와 document의 형태로 이루어져있다. NoSQL이지만 SQL처럼 테이블 형태로 관리가 편하다는 장점이 있다.

더 자세한 차이점은 공식 홈페이지를 참고하자!

DB Class만들기

더 나은 확장성, 독립성을 위해 firstore Service에 관한 NweetRepository Class를 따로 만들어서 index.js에서 해당 class의 인스턴스를 만들어서 App에 주입하는 방식으로 하였다.

// index.js
import React from "react";
import ReactDOM from "react-dom";
import AuthService from "service/authService";
import NweetRepository from "service/nweetRepository";
import App from "./components/App";
import { firebaseApp } from "./service/firebase";

const authService = new AuthService(firebaseApp);
const nweetRepository = new NweetRepository();

ReactDOM.render(
  <React.StrictMode>
    <App authService={authService} nweetRepository={nweetRepository} />
  </React.StrictMode>,
  document.getElementById("root")
);
// nweetRepository.js
import {
  getFirestore,
  collection,
  addDoc,
  getDocs,
  onSnapshot,
  deleteDoc,
  doc,
  updateDoc,
} from "firebase/firestore";

class NweetRepository {
  constructor() {
    this.db = getFirestore();
  }

  addNweet(nweet, uid) { // 데이터 추가
    addDoc(collection(this.db, "nweets"), {
      text: nweet,
      createdAt: Date.now(),
      creatorId: uid,
    });
  }

  getNweets = async (onUpdate) => { // 데이터 받아오기
    const dbNweets = await getDocs(collection(this.db, "nweets"));
    dbNweets.forEach((doc) => {
      const nweetObj = {
        ...doc.data(),
        id: doc.id,
      };
      onUpdate((prev) => [...prev, nweetObj]);
    });
  };

  asyncNweets(onUpdate) {  // 실시간 데이터 동기화
    onSnapshot(collection(this.db, "nweets"), (snapshot) => {
      const nweetArray = snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));
      onUpdate(nweetArray);
    });
  }

  deleteNweet(id) {  // 데이터 삭제
    return deleteDoc(doc(this.db, "nweets", id));
  }

  updateNweet(id, newNweet) {  // 데이터 수정
    return updateDoc(doc(this.db, "nweets", id), {
      text: newNweet,
    });
  }
}

export default NweetRepository;

Create

// nweetRepository 클래스
  addNweet(nweet, uid) { // 데이터 추가
    addDoc(collection(this.db, "nweets"), {
      text: nweet,
      createdAt: Date.now(),
      creatorId: uid,
    });
  }
// 컴포넌트
  const [nweet, setNweet] = useState("");

  const onSubmit = async (event) => {
    event.preventDefault();
    await nweetRepository.addNweet(nweet, userObj.uid);
    setNweet("");
  };

  const onChange = (event) => {
    const { value } = event.target;
    setNweet(value);
  };
  • nweetRepository는 컴포넌트에 props으로 전달됨.
  • 컴포넌트 내의 form에서 submit이 되었을때 해당 value를 추가한다.
    (여기서 form의 value는 state로 설정한 nweet이다)
  • addNweet을 할때는 들어갈 text값과 로그인했을때 user의 uid가 들어간다.
  • firestore에서 nweets이라는 collection이 만들어진다. document는 임의의 id가 만들어진다. field에는 createdAt, creatorId, text 정보가 들어간다.

Read

1) 데이터 한번 가져오기

// Class
  getNweets = async (onUpdate) => {
    const dbNweets = await getDocs(collection(this.db, "nweets"));
    dbNweets.forEach((doc) => {
      const nweetObj = {
        ...doc.data(),
        id: doc.id,
      };
      onUpdate((prev) => [...prev, nweetObj]);
    });
  };
// Component
  const [nweets, setNweets] = useState([]);

  useEffect(() => {
    nweetRepository.getNweets(setNweets);
  }, [nweetRepository]);
  • 어플이 시작하자마자 nweets라는 collection을 받아온다.
  • getNweets()는 array를 리턴한다.
  • 리턴받은 document array를 돌면서 doc의 id와 데이터값으로 새로운 object를 만들어 Nweets array로 설정한다.

=> 하지만 getDocs()를 이용했을때 실시간으로 업데이트된 결과를 보기위해서는 새로고침을 해야한다.

2) 실시간 업데이트 수신대기

  asyncNweets(onUpdate) {
    onSnapshot(collection(this.db, "nweets"), (snapshot) => {
      const nweetArray = snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));
      onUpdate(nweetArray);
    });
  }
  useEffect(() => {
    nweetRepository.asyncNweets(setNweets);
  }, [nweetRepository]);
  • onSnapshot()이라는 메서드로 문서의 변경을 수신대기 할 수 있다. 처음호출될때 스냅샷을 찍어두었다가 데이터가 변경될때 마다 콜백이 호출되어 스냅샷이 업데이트된다.
  • onSnapshot() 메서드를 이용하면 해당 collection의 데이터가 변경될때마다 콜백함수가 실행되어 새로운 array를 받아온다.

Update

// Class
  updateNweet(id, newNweet) {
    return updateDoc(doc(this.db, "nweets", id), {
      text: newNweet,
    });
  }
// Component
  const onSubmit = async (e) => {
    e.preventDefault();
    await nweetRepository.updateNweet(nweetObj.id, newNweet);
    setEditiong(false);
  };
  • 수정할 nweet에서 form을 submit을 하면 updateNweet()을 부른다.
  • newNweet은 form에서 변경할 value값이다.
  • updateDoc()에서 doc이름을 변경할 해당 nweet의 id를 전달하여 해당 nweet을 받아와 수정할 수 있도록 한다. (nweet을 생성할때 id라는 key만들었음)
  • 두번째 인자로 객체로 업데이트할 내용을 전달한다

Delete

// Class
  deleteNweet(id) {
    return deleteDoc(doc(this.db, "nweets", id));
  }
// Component
  const onDeleteClick = async () => {
    const ok = window.confirm("Are you sure you want to delete this nweet?");
    if (ok) await nweetRepository.deleteNweet(nweetObj.id);
  };
  • 변경할 Nweet의 삭제 버튼을 누르면 onDeleteClick이 실행되도록함
  • window.confirm()을 이용해 삭제할 것인지 되물음 -> boolean리턴
  • boolean값이 true를 리턴하면 deleteNweet()실행
  • 해당 Nweet의 id값 (= doc.id) 를 전달하여 해당 doc를 삭제한다.

참고: 노마드코더 트위터 클론코딩
firebase doc
https://firebase.google.com/docs/firestore/rtdb-vs-firestore?hl=ko

profile
Stay hungry, Stay foolish!

0개의 댓글