3.Firestore Database

Updated:


1.Cloud FireStore

  • Cloud Firestore 의 database 는 NoSQL database 입니다. 그래서 규칙들도 많이 없고 상당히 유연하고 사용하기도 쉽습니다.

  • 규칙이 많이 없기 때문에 조금 제한되는 부분이 있을 수 있습니다.

특징

  • Collection : 기본적인 폴더와 같이 category 를 나눌수 있는 기능

  • Documentation : Collection 안에 있는 database 문서들을 말합니다.

  • firebase database 만들기

테스트 모드로 일단 database 생성합니다

image

  • 프로젝트 root 경로에 firebase.js 파일에서 firestore 를 import 해줘야 합니다
// in fbase.js on root your project
import "firebase/firestore";

// 프로젝트에서 dbService 라는 이름으로 export 해줌
export const dbService = firebase.firestore();

firebase.firestore docs

2.Collection Reference

  • collectionReference : collection 안의 document 를 추가,수정, 삭제 할수 있는 함수

firebase.firestore.CollectionReference

add (CREATE)

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

const Home = () => {
  // State
  const [chat, setChat] = useState("");

  // Submit Function
  const onSubmit = async (e) => {
    e.preventDefault();
    // "chat"이라는 collection 에 add 를 해서 data 를 넣을 수 있습니다. chat 은 useState 에서 저장된 값 (input 에서 넘어오는 값을 넣어 줄수 있습니다)
    await dbService.collection("chat").add({
      chat,
      createAt: Date.now(),
    });
    // db 에 등록한 후, 다시 초기화 함
    setChat("");
  };
  const onChange = (e) => {
    const {
      target: { value },
    } = e;
    setChat(value);
  };

  return (
    <div>
      <>
        <form onSubmit={onSubmit}>
          <input
            value={chat}
            onChange={onChange}
            type="text"
            placeholder="what's on your mind?"
            maxLength={120}
          />
          <input type="submit" value="chat" />
        </form>
      </>
    </div>
  );
};

get (READ)

  • 데이터를 가져오기 위해 get() 함수를 사용함

firebase.firestore.CollectionReference.get()

image

  • return 값으로 promise QuerySnapshot 을 가져오는데 데이터베이스에 있는 query docs 형태로 가져옴

firebase.firestore.QuerySnapshot

firebase.firestore.QueryDocumentSnapshot

data() 함수 형태로 data를 불러 올수 있음

const getChats = async () => {
  const dbChats = await dbService.collection("chats").get();
  dbChats.forEach((document) => console.log(document.data()));
};
  • DB 에 있는 data 를 가져와서 화면에 표시하는 방법
const [chats, setChats] = useState([]);

// read DB
const getChats = async () => {
  const dbChats = await dbService.collection("chats").get();
  dbChats.forEach((document) => {
    const chatsObj = {
      id: document.id,
      ...document.data(),
    };
    // 최근의 document와 이전의 document를 붙여서 setChats 으로 다시 할당
    setChats((prev) => [chatsObj, ...prev]);
  });
};
// mount 할때 dbService 에서 실행할 useEffect
useEffect(() => {
  getChats();
}, []);

return (
  <div>
    {chats.map((chat) => (
      <div key={chat.id}>
        <h4>{chat.chat}</h4>
      </div>
    ))}
  </div>
);
  • onSnapshot() 을 통해서 data를 가져오는 방식(위의 const getChats 를 대신 할 수 있음!!)
// read DB realtime

useEffect(() => {
  dbService
    .collection("chats")
    .orderBy("createAt", "desc")
    .onSnapshot((snapshot) => {
      const chatArray = snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));
      setChats(chatArray);
      console.log(chatArray);
    });
}, []);
  • query 를 이용하는게 아니라 snapshot 으로 사용했기 때문에 실시간으로 확인할 수 있게 됨

delete (DELETE)

  • 먼저 작성자가 인경우에만 delete edit 할 수 있게 설정하기
// in Home.js <Chat> 에 props data 로 userObj.uid 를 chat.creatorID 와 같은 경우가 isOwner{true} 를 Chat.js 에 넘김

<div>
  {chats.map((chat) => (
    <Chat
      key={chat.id}
      chatObj={chat}
      isOwner={chat.creatorID === userObj.uid}
    />
  ))}
</div>
// in Chat.js

import React from "react";

const Chat = ({ chatObj, isOwner }) => {
  return (
    <>
      <div>
        <h4>{chatObj.text}</h4>
        // isOwner 가 true 일때만 delete, edit 버튼이 보이게 수정
        {isOwner && (
          <>
            <button>Delete Chat</button>
            <button>Edit Chat</button>
          </>
        )}
      </div>
    </>
  );
};

export default Chat;

firebase.firestore.DocumentReference Delete

  • 삭제 : 메세지의 id 를 지정했기 때문에 id 를 찾아서 delete 해주면 됩니다
  // Delete function
  const onDeleteClick = async () => {
    const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
    if (check) {
      // delete ok
      await dbService.doc(`chats/${chatObj.id}`).delete();
    }
  }

  return (
    <>
      <div>
        <h4>{chatObj.text}</h4>
        {isOwner && (
          <>
            <button onClick={onDeleteClick}>Delete Chat</button>
            <button>Edit Chat</button>
          </>
        )}
      </div>
    </>
  );
};

edit (EDIT)

  • edit 은 기존 delete 에서 추가로 toggleEditing , onChange, onSubmit 등 만들어서 dbService 에 비동기로 update 해줘야 함
import React, { useState } from "react";
import { dbService } from "../fbase";

const Chat = ({ chatObj, isOwner }) => {
  // State
  const [editing, setEditing] = useState(false);
  const [newChat, setNewChat] = useState(chatObj.text);

  // Delete function
  const onDeleteClick = async () => {
    const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
    if (check) {
      // delete ok
      await dbService.doc(`chats/${chatObj.id}`).delete();
    }
  };

  // Edit function
  const toggleEditing = () => setEditing((prev) => !prev);
  const onSubmit = async (e) => {
    e.preventDefault();
    await dbService.doc(`chats/${chatObj.id}`).update({
      text: newChat,
    });
    setEditing(false);
  };
  const onChange = (e) => {
    const {
      target: { value },
    } = e;
    setNewChat(value);
  };

  return (
    <>
      <div>
        {editing ? (
          <>
            <form onSubmit={onSubmit}>
              <input
                type="text"
                placeholder="Edit your Chat"
                value={newChat}
                required
                onChange={onChange}
              />
              <input type="submit" value="Update Chat" />
              <button onClick={toggleEditing}>Cancel</button>
            </form>
          </>
        ) : (
          <>
            <h4>{chatObj.text}</h4>
            {isOwner && (
              <>
                <button onClick={onDeleteClick}>Delete Chat</button>
                <button onClick={toggleEditing}>Edit Chat</button>
              </>
            )}
          </>
        )}
      </div>
    </>
  );
};

export default Chat;

Sum-up

  • Home.js 에서 useEffect() 안에 listener 로 firebase 의 onSnapshot 을 사용하고 있습니다.

  • onSnapshot 은 기본적으로 데이터 베이스에 변경사항이 발생될 경우 알림을 받아 오는 기능을 합니다. 여기예시로 받아오는것은 .collection("chats") 을 지정해 놓고 사용합니다.

  • 그 대이터를 받아서 chatArray 라는 배열에 데이터를 저장하고 그것을 setChats 을 통해서 state 배열에 집어 넣습니다.

// read DB realtime
useEffect(() => {
  dbService
    .collection("chats")
    .orderBy("createAt", "desc")
    .onSnapshot((snapshot) => {
      const chatArray = snapshot.docs.map((doc) => ({
        // 모든 받아오늘 data 의 형태는 id 와 doc.data() 형태로 되어 있습니다.
        id: doc.id,
        ...doc.data(),
      }));
      setChats(chatArray);
    });
}, []);
  • 화면에는 chat component 를 불러와서 chat data 를 형태로 화면에 rendering 해 줌니다. 이 떄, 2개의 props 를 chat component 에 전달하게 되는데 chatObj 와 isOwner를 chats 에서 받은 데이터를 입력해서 Chat component 에 넘겨 줍니다.

  • 넘겨주는 chatObj 는 author, text, createdAt 형태로 되어 있습니다. isOwner는 boolean data 로 상황에 따라서 true, false props 를 넘겨 줍니다. (delete 와 edit 을 해주기 위해 chat 을 만든 사람과 === userObj.uid 이면 true , 아니면 false 가 됨)

<div>
  {chats.map((chat) => (
    <Chat
      key={chat.id}
      chatObj={chat}
      isOwner={chat.creatorID === userObj.uid}
    />
  ))}
</div>
  • useObj.uid 는 Home 이 props 로 받는 data 로써 Router component 를 통해 받으며, 또 Router 는 useObj 를 App.js 에서 props 로 받아 옵니다.

  • App.js 에서는 로그인, 로그아웃이 일어날때 사용자의 정보를 저장하는 곳으로 project 에서 중요한 State 는 App.js 에 보관 합니다. (hooks 를 사용할 경우)

  • onAuthStateChanged() 는 app 이 초기화 될때마다 발생됩니다. Firebase 에서 로그인을 한지 안한지 알 수 있게 해줍니다.

// in App.js
  // authService.currentUser 를 통해 로그인 되었는지 안되었는지 확인 할 수 있음 (로그인 안되어 있으면 null 이 return )
  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}/> : "Initializing...."}
  • Chat.js component 에서는 2개의 State 를 가지고 있는데 editing 은 boolean data 로써 기본적으로 chat을 수정하고 잇는지 아닌지를 가리키는 값이고 기본값은 false임, newChat 은 input 의 값을 수정하기 위해 만든 state 로 기본값은 입력되어 있는 chatObj.text 값임
const Chat = ({ chatObj, isOwner }) => {
  // State
  const [editing, setEditing] = useState(false);
  const [newChat, setNewChat] = useState(chatObj.text);

  // Delete function
  const onDeleteClick = async () => {
    const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
    if (check) {
      // delete ok
      await dbService.doc(`chats/${chatObj.id}`).delete();
    }
  };

  // Edit function
  const toggleEditing = () => setEditing((prev) => !prev);
  const onSubmit = async (e) => {
    e.preventDefault();
    await dbService.doc(`chats/${chatObj.id}`).update({
      text: newChat,
    });
    setEditing(false);
  };
  const onChange = (e) => {
    const {
      target: { value },
    } = e;
    setNewChat(value);
  };

  return (
    <>
      <div>
        {editing ? (
          <>
            <form onSubmit={onSubmit}>
              <input
                type="text"
                placeholder="Edit your Chat"
                value={newChat}
                required
                onChange={onChange}
              />
              <input type="submit" value="Update Chat" />
              <button onClick={toggleEditing}>Cancel</button>
            </form>
          </>
        ) : (
          <>
            <h4>{chatObj.text}</h4>
            {isOwner && (
              <>
                <button onClick={onDeleteClick}>Delete Chat</button>
                <button onClick={toggleEditing}>Edit Chat</button>
              </>
            )}
          </>
        )}
      </div>
    </>
  );
};

🔶 🔷 📌 🔑

Reference

Categories:

Updated:

Leave a comment