TIL_ver9 Firebase 시작하기 2 (firestore database이용해서 글쓰기)

이고운·2023년 4월 5일
0

firebase_게시판

목록 보기
2/5

이제 로그인을 하면 게시판에 글쓰기 기능을 넣을 것임.

1. firestore database 이용하여 데이터 관리하기

1) 글쓰기 기능 만들기 (CREATE)

(1) input 입력창과, submit 버튼을 만들기

input에 입력된 value값 state에 저장.

Home.js
const handleContents = (event) => {
    const {
      target: { value },
    } = event;
    setContent(value);
  };

(2) firestore database 이용하기 / import

콘솔 - 프로젝트 - create database -start in test mode - 지역선택 (지역에 따라 요금 다름)
cloud firestore의 database는 noSQL database임

collection / document가 있음
collection : 기본적으로 폴더와 같음.
document :문서와 같음.

  • mybase에 import 하기
import { getFirestore } from "firebase/firestore";
export const dbService = getFirestore();

(3) document 생성

  • submit할 때마다 document 생성

  • 메소드가 여러가지인데 일단 collection을 이용하기
    collection은 collection경로라는 걸 줘야함.
    (명시된 데이터를 담은 새로운 document을 collections에 추가하는 것임)

  • 자동으로 id를 생성해서 문서를 추가할 때는 addDoc을 사용
    공식문서 참고 :
    https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&authuser=0
    이때 addDoc안에 데이터를 넣을 수 있는데 어떠한 데이터도 가능
    나는 text, createAt를 넣음 (나중에 추가 및 수정)

Home.js
const handleSubmit = async (e) => {
try {
  const docRef = await addDoc(collection(dbService, "fweets"), {
       text: content,
       createAt: Date.now(),
    });
     setContent(docRef);
   } catch (error) {
     console.log(error);
   }

이렇게 하면 cloud firestore에 collection과 fweets document가 추가된 것을 볼 수 있음

2) 작성한 글 가져오기 (READ)

(1) get method 이용하여 작성한 데이터 가져오기

  • 글 배열이 들어가는 state만들기
  • mount가 되면 db collection 를 가져와서 보여줄 것임. (get method 이용)
Home.js
 const [contents, setContents] = useState([]);

const getFweets = async()=>{
  const dbFweets = await db.collection("fweets").get()
  console.log(dbFweets);// dbFweets는 querySnapshat임
  //해당되는 메소드를 이용해 data를 가져옴. 그래서 일단 아래와 같이 콘솔찍기
  //dbFweets.forEach(document => console.log(document.data());
  
  //모든 이전 fweets에 대해 배열을 리턴, 이때 리턴하는 것들은 새로 작성한 트윗과
  //이전것들임 
  dbFweets.forEach(document => {
    setContents(prev => [document.data(), ...prev])

//async함수를 써야해서 별도의 함수로 저장  
useEffect(()=>{
  getFweets();
},[]);
  • 여기까지하고 console.log(contents)를 찍으면 배열 형식으로 나옴
  • 이 때 fweet의 객체를 만들어서 수정해 줄 것임.
Home.js
 const [contents, setContents] = useState([]);

const getFweets = async()=>{
  const dbFweets = await db.collection("fweets").get()
  dbFweets.forEach(document => {
    const fweetObject = {
       ...document.data(), // 모든 데이터를 가질 것이고
       id :document.id,//아이디도 가질 것임.
    setContents(prev => [fweetObject, ...prev])

//async함수를 써야해서 별도의 함수로 저장  
useEffect(()=>{
  getFweets();
},[]);

(2) map 이용하여 가져온 데이터 출력하기

Home.js
return(
  ...
 <div className="fweetWrap">
          {contents.map((fweet) => (
           <div key={fweet.id}>{fweet.fweet}</div>
          )}
        </div>
);

3) 작성한 글 실시간으로 가져오기

(1) 사용자 정보 가져오기

  • 사용자 정보가 있어야 해당 데이터를 수정/삭제하는 기능을 넣을 수 있음
  • 사용자 정보에 따라 페이지를 이동시킬 수도 있을 것임
App.js
const [userObj, setUserObj] = useState(null)


 useEffect(() => {
    auth.onAuthStateChanged((user) => {
      if (user) {
        setIsLogin(true);
        setUserObj(user);
      } else {
        setIsLogin(false);
      }
      setInit(true);
    });
  }, []);

auth가 바뀐다면 우리가 받을 user에 setUserObj를 넣을 것임
로그인이 되면 onAuthStateChanged가 호출됨. 그럼 로그인한 user를 받게 됨.
즉 우리는 이제 누가 로그인했는지 알게 됨.

  • userObj는 다른 곳에서도 사용 예정이니 props로 전달

(2) 작성한 글 받아오는 함수 수정

  • Home.js에 handleSubmit함수에 userId 넣어서 수정
Home.js
const handleSubmit = async (e) => {
try {
  const docRef = await addDoc(collection(dbService, "fweets"), {
       text: content,
       createAt: Date.now(),
       creatorId: userObj.uid // console.log(userObj)찍어서 uid찾음
    });
     setContent(docRef);
   } catch (error) {
     console.log(error);
   }

파이어베이스 콘솔 document에서 creatorId라는 새로운 field가 생김.
이제 누가 fweet을 생성했는지 알 수 있게됨.

(3) 실시간 데이터 가져오기

데이터베이스를 계속해서 실시간으로 가져오는 작업을 할 것임.
이때 사용하는 메소드는 onSnapshot임. 이건 리스너임.
Home.js의 getFweets함수와 useEffect 함수를 수정할 것임.
참고문서 : https://firebase.google.com/docs/firestore/query-data/listen?authuser=0&hl=ko#listen_to_multiple_documents_in_a_collection

//이전에 만들어둔 getFweets함수는 아예 지움

Home.js

 const [contents, setContents] = useState([]);

 useEffect(() => {
    const q = query(collection(dbService, "fweets"));
    onSnapshot(q, (snapshot) => {
      const fweetArray = snapshot.docs.map((doc) => ({ //모든 doc은 objects를 반환할 건데 그 내용은 아래와 같음.
        id: doc.id,
        ...doc.data(),
      }));
      setContents(fweetArray);
    });
  }, []);

snapshot은 우리가 가진 query와 같음. 하지만 이건 docs를 가지고 있음.
fweets는 우리가 페이지를 불러올 때 snapshot에서 나오는 것임.
여기서 full map을 만들건데 (fweets 배열) 반환한 배열을 setContents에 넣어줌.

2. 데이터 삭제하고 수정하기

참고문서 : https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ko

1) 데이터 수정을 위한 컴포넌트 만들기

2) UI 만들기 (버튼 등)

3) 작성자가 맞는지 확인하고 작성자가 맞으면 수정이나 삭제할 수 있는 버튼 생성

Home.js

<div>
  {contents.map((fweet) =>{
    <Fweet key={fweet.id} fweetObj ={fweetObj}, isOwner={fweet.creatorId === userObj.uid}
  })};
</div>


Fweet.js

{isOwner && 
  <>
  <button>Delete </button>
  <button>Edit </button>
  </>
}

4) 삭제 / 수정하는 함수 만들기

(1) 삭제 기능

Fweet.js

import { doc, deleteDoc, updateDoc } from "firebase/firestore";

const Fweet = ({ userObj, fweetObj, isOwner }) => {
  const FweetTextRef = doc(dbService, "fweets", `${fweetObj.id}`); //document의 id

  const handleDelete = async () => {
    const ok = window.confirm("Are you sure?");
    if (ok) {
      await deleteDoc(FweetTextRef);
      await deleteObject(ref(storageService, fweetObj.fileUrl));
    }
  };

(2) 수정 기능

  • edit여부를 결정하는 상태를 state에 저장함
  • toggle함수로 이전값을 return하는 함수를 만들어줌 (스위치)
  • edit state(나는 editing)이 true면 수정하는 div를 보여주고 아니면
    원래 내용 fweet div를 보여주게 조건부 렌더링
  • input창을 onChange하는 함수를 만들어줌 (아니면 수정하는 모습을 볼 수 없음)
  • input을 submit하는 edit update 버튼을 만들어줌.
Fweet.js

import { doc, deleteDoc, updateDoc } from "firebase/firestore";

const Fweet = ({ userObj, fweetObj, isOwner }) => {
  const [editing, setEditing] = useState(false); //edit모드 확인
  const [editedFweet, setEditedFweet] = useState(fweetObj.text); //input에 입력된 text 업데이트
  const FweetTextRef = doc(dbService, "fweets", `${fweetObj.id}`); //document의 id

  //토글 함수 : 이전값 반대로 return 해주는 함수를 넣음
  //true or false 여부에 따라 보여지는 div가 달라짐
  const handleEdit = () => {
    setEditing((prev) => !prev);
  };
  
  const submitEdit = async (e) => {
    e.preventDefault();
    await updateDoc(FweetTextRef, {
      text: editedFweet, //이전 양식이랑 같아야함.
    });
    setEditing(false); //edit모드 꺼주기
  };
  
  //input에 수정하는 value값 보이게 함.
  const editFweet = (event) => {
    const {
      target: { value },
    } = event;
    setEditedFweet(value);
  };
  
  return (
    <StyledFweet>
      <div>
        {editing ? (
          <div className="editFweet">
            <form onSubmit={submitEdit}>
              <input
                type="text"
                placeholder="Edit your Fweet"
                value={editedFweet}
                required
                onChange={editFweet}
                className="editTextInput"
              />
              <input type="submit" value="Update" className="editTextSubmit" />
            </form>
            <button onClick={handleEdit}>Cancel</button>
          </div>
        ) : (
          <div>
            <div className="fweetText">
              <div className="textWrap">
                <div className="userName">by {fweetObj.userName}</div>
                <div className="text">
                  <p>{fweetObj.text}</p>
                </div>
              </div>
              {isOwner && (
                <div className="buttonWrap">
                  <button onClick={handleDelete}>
                    <BsTrash3 />
                  </button>
                  <button onClick={handleEdit}>
                    <AiOutlineEdit />
                  </button>
                </div>
              )}
            </div>
          </div>
        )}
  

⭐️ 결과물

보면 작성자가 아닌 사람은 삭제버튼이랑 수정버튼이 안보인다.
그리고 삭제버튼 클릭시에 알림창이 뜨게 되어있고, 수정버튼 누르면 수정화면이 나옴.
이후에 수정한 내용으로 바꿔서 update버튼 누르면 수정 완료!

❗️어려웠던 점
ver9으로 firebase 이용하려니까 어려움이 많았다..
공식문서가 잘되어있긴하지만 그 공식문서 자료 찾는 것도 쉽지많은 않았음.
특히 실시간 데이터 가져오는 onSnapshot 메소드를 이용하는 게 제일 어려웠다.
공식문서 위치도 실시간업데이트 수신대기 이런이름에 컬렉션의 여러문서에 리슨이라고 적혀있는 부분이었음 ㅋㅋㅋㅋ게다가 query 메소드랑 같이 사용하는 거라 어려움이 있었다...
파이어베이스 공식문서 볼 때는 목차를 기준삼아 순차적으로 확인하는게 좋을 듯하다.
당연하지만 ~시작하기 이런 목차에서도 도움이 되는 내용들이 많았음 ㅎㅎㅎ.

profile
자 이제 시작이야~ 내 꿈을~ 내 꿈을 위한 여행~~🌈

0개의 댓글