JS' 공부흔적

[Firebase] Firebase, React를 사용한 간단한 프로그램 만들기 (3) - Firestore를 사용하여 데이터 다루기 본문

Firebase

[Firebase] Firebase, React를 사용한 간단한 프로그램 만들기 (3) - Firestore를 사용하여 데이터 다루기

이준수 2023. 1. 28. 19:49
 

[Firebase] Firebase, React를 사용한 간단한 프로그램 만들기 (2) - Firebase를 사용하여 이메일 로그인 구

[Firebase] Firebase, React를 사용한 간단한 프로그램 만들기 (1) - Firebase와 React 연동하기 앞으로 4편에 걸쳐서 파이어베이스와 리액트를 사용하여 아주아주 간단한 프로그램을 만들어보는 글을 작성

2junsu.tistory.com

이전 글에 이어서 이번 글에서는 Firebase에서 제공하는 DB인 Firestore를 사용하여 간단한 데이터를 저장하고 불러오고 삭제하는 것을 다룬다.

 

Firestore 기본 설정

우선, 콘솔에서 Firestore Database를 클릭하여 데이터베이스를 만든다.

 

 

프로덕션 모드에서 시작을 선택하고 다음을 누른다. 위치는 한국이므로 asia-northeast3 (Seoul)을 선택하여 만들기를 완료한다.

 

다 만들고 나면 에러를 방지하기 위해 규칙 탭으로 이동하여 아래와 같이 코드를 수정한다.

allow read, write: if true; // false를 true로 변경

이제 모든 준비가 끝났다. Firestore에 데이터를 저장하는 것부터 구현해보자!


데이터 추가 및 수정하기

우선, Firestore를 사용하기 위해서 firebase-config.js에 아래 코드를 추가한다.

// firebase-config.js

import { getFirestore } from "firebase/firestore";

...

export const db = getFirestore(app);

 

데이터를 추가하는 방법에는 addDoc과 setDoc을 사용하는 방법이 있는데, addDoc은 추가된 document의 id가 랜덤으로 정해진다. document id가 의미없다면 addDoc을 사용해도 무방하다. 여기에서는 setDoc을 사용하여 document의 id를 직접 정하는 방식으로 진행하겠다. 더 자세히 알고 싶다면 공식문서를 방문해보자.

 

회원가입을 할 때 우선적으로 document를 추가할 것이다. 그리고 로그인 후에 데이터를 관리하는 과정에서 데이터 수정에 대해 다룰 것이다. 따라서 Signup.jsx 파일로 이동하여 아래 코드를 추가한다.

// Signup.jsx

...
import { app, db } from "../firebase-config"; // db 추가
import { doc, setDoc } from "firebase/firestore"; // 추가
import { useNavigate } from "react-router-dom"; // 추가

const Signup = () => {
  const navigate = useNavigate(); // 추가
  
  ...
  
  localStorage.setItem(
        "userInfo",
        JSON.stringify({
          accessToken: await userCredential.user.getIdToken(),
          uid: userCredential.user.uid,
        })
      );

	  // 추가
      await setDoc(doc(db, "data", userCredential.user.uid), {
        textList: [],
      });

      navigate("/"); // 추가
      window.location.reload();

회원가입을 하게 되면 자동으로 로그인까지 되므로 navigate를 사용했다.

 

이번엔 setDoc() 부분을 보자.

await setDoc(doc(db, "data", userCredential.user.uid), {
        textList: [],
      });

위의 코드는 document를 만드는, 즉 데이터를 추가하는 코드이다.

data라는 이름의 collection(흔히 말하는 테이블)에다가 document id를 userCredential.user.uid로 하는 document를 만들겠다는 뜻이다. 그 뒤에 객체 형식으로 document에 들어갈 데이터를 작성해주면 된다.

 

이 코드를 실행하고 Firestore 데이터 탭을 확인하면 잘 추가된 것을 확인할 수 있다.

 

728x90

 

이제 Main 페이지를 꾸며보자.

아주 간단하게 텍스트를 입력받고 추가 버튼을 누르면 DB에 추가되도록, 그리고 추가한 데이터를 불러오도록 구현해보겠다.

 

예상대로 동작한다면, 입력받은 텍스트 값이 위의 textList 배열에 하나씩 늘어갈 것이다. 이를 우리는 데이터를 "추가"한다고 말하지만, Firestore에서는 이를 "수정"한다고 표현한다. 데이터를 "추가"한다는 것은 document(문서) 하나하나를 collection에 추가한다는 말이고, 우리는 그렇게 만들어진 document 내에 있는 필드, 즉 textList를 다루는 것이므로 이는 엄밀히 말하면 데이터를 "수정"하는 것이다.

 

아무튼 데이터를 수정하는 것에는 setDoc과 updateDoc이 있다.

setDoc은 document 전체를 주어진 데이터로 덮어쓴다. 따라서 데이터가 없다면, 그냥 데이터를 추가해버린다.

updateDoc은 document의 특정 필드만 업데이트 할 수 있다. 특정 필드의 값이 없다면 필드 값을 추가해버린다.

 

여기에서는 updateDoc을 사용하겠다. 위에서 setDoc을 사용하여 데이터를 추가했던 것과 사용 방법은 동일하다.

그럼 이제 텍스트를 입력받고 추가 버튼을 누르면 DB에 추가되도록 코드를 작성해보자!

import { doc, getDoc, updateDoc } from "firebase/firestore"; // getDoc은 밑에서 데이터 불러오기에 쓰임
import React, { useEffect, useState } from "react";
import { db } from "../firebase-config";

const Main = () => {
  const uid = JSON.parse(localStorage.getItem("userInfo") || "{}").uid;
  const [text, setText] = useState("");
  const [textList, setTextList] = useState([]);

  // DB에서 데이터를 불러오는 함수
  const getTextList = async () => {
   ...
  };

  useEffect(() => {
    getTextList();
  }, []);

  // textList 최신화 후 DB에서 데이터를 다시 불러옴
  const addText = async () => {
    let list = [...textList, text];
    await updateDoc(doc(db, "data", uid), {
      textList: list,
    });
    getTextList();
  };

  return (
    <div>
      <input
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <button onClick={addText}>추가</button>
    </div>
  );
};

export default Main;

텍스트를 입력 후에 추가 버튼을 누르고, Firestore를 확인해보면 데이터가 잘 수정된 것을 확인할 수 있다.

 

 


데이터 불러오기

잘 추가된 데이터를 불러와서 화면에 뿌려보자!

위에서 채우지 않은 getTextList 함수를 채워보자. 데이터를 불러오기 위해서는 getDoc() 또는 getDocs()를 사용한다. 메소드 이름에서도 알 수 있듯이 여러 개의 데이터를 불러오고 싶으면 getDocs()를 사용할 수도 있지만, 여기에서는 자신의 textList만 불러오면 되므로 getDoc()을 사용하겠다.

const getTextList = async () => {
  try {
    const docRef = await getDoc(doc(db, "data", uid));
    setTextList(docRef.data().textList);
  } catch (error) {
    console.error(error);
  }
};

getDoc()을 사용하여 데이터를 불러오고, 그 중에서 textList를 추출하여 local state에 담아준다. 그리고 textList 내의 값을 적당히 뿌려보자.

...

return (
    <div>
      <input
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <button onClick={addText}>추가</button>
      
      {/* 추가 */}
      <ul>
        {textList.map((d, i) => (
          <li key={i}>
            <div>
                <span>{d}</span>
                <button>X</button>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );

export default Main;

잘 작동한다면 아래와 같은 결과를 얻을 수 있다.

 

 

SMALL

데이터 삭제하기

이번엔 데이터를 삭제해보자. document 전체를 삭제하고 싶으면 아래와 같이 사용하면 된다.

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

await deleteDoc(doc(db, "data", uid));

 

그리고 특정 필드 자체를 삭제하고 싶으면 아래와 같이 deleteField()를 사용한다.

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

// Remove the 'textList' field from the document
await updateDoc(doc(db, 'data', uid), {
    textList: deleteField()
});

 

그러나 우리는 textList의 데이터 하나하나를 따로 삭제할 것이다. 사실 이때는 filter() 메소드를 사용하여 로컬 단에서 데이터를 걸러준 후, updateDoc()을 사용하면 된다.

아래와 같이 deleteText 함수를 정의하고, X 버튼을 누르면 동작하는 코드를 작성하자.

...

  const deleteText = async (i) => {
    let list = [...textList].filter((_, idx) => i !== idx);
    await updateDoc(doc(db, "data", uid), {
      textList: list,
    });
    getTextList();
  };
  
...

          <li key={i}>
            <div>
              <span>{d}</span>
              <button
                onClick={() => {
                  deleteText(i);
                }}
              >
                X
              </button>
            </div>
          </li>

 

잘 동작한다면 아래와 같은 결과를 얻을 수 있다.

 

 

지금까지 3개의 글을 통해서 파이어베이스를 사용하여 회원가입, 로그인, DB 통신 기능을 사용해봤고, 아주아주 간단한 프로젝트를 만들어봤다. 다음 글에서는 이 프로젝트를 모든 사람들이 사용할 수 있도록 실제로 배포해보는 내용에 대해 다루겠다.

 

 

 

728x90
반응형