패스트캠퍼스 데브캠프 56~57일차 [Firebase, CRUD]

Su Min·2024년 8월 8일
0
post-thumbnail

토이프로젝트II 서버는 Firebase에서 제공하는 Firestore를 사용 !

토이프로젝트II는 짧은 기간인 3주동안 진행되어 서버까지 구축하지않고 firebase에서 제공되는 서버 firstore를 사용하기로 했다.

firestore 구조는 컬렉션 > 문서 > 필드 & 컬렉션 > 문서 > 필드 & 컬렉션 ... 이런 반복되는 구조로 되어있다. firebase/firestore에서 제공되는 다양한 메서드를 통해 CRUD 작업을 쉽게 할 수 있다.

🔗 Firebase 초기화

CRA는 process.env.REACT_APP_FIREBASE_API_KEY로 작성하지만 vite는 import.meta.env.VITE_FIREBASE_API_KEY로 작성한다. 이 부분에서 많이 헤맸었다....🥲
Firebase_Config.ts

import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

declare global {
  interface ImportMeta {
    readonly env: {
      readonly VITE_FIREBASE_DATABASE_URL: string;
      readonly VITE_FIREBASE_API_KEY: string;
      readonly VITE_FIREBASE_AUTH_DOMAIN: string;
      readonly VITE_FIREBASE_PROJECT_ID: string;
      readonly VITE_FIREBASE_STORAGE_BUCKET: string;
      readonly VITE_FIREBASE_MESSAGING_ID: string;
      readonly VITE_FIREBASE_APP_ID: string;
    };
  }
}

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
};

const firebase = initializeApp(firebaseConfig);
const db = getFirestore(firebase);

export { db };

SDK는 개인 정보로 공유되면 안되기때문에 env파일에 따로 작성해주고 gitignore에 추가로 작성한다.
.env

VITE_FIREBASE_API_KEY=******
VITE_FIREBASE_AUTH_DOMAIN=******
VITE_FIREBASE_PROJECT_ID=******
VITE_FIREBASE_STORAGE_BUCKET=******
VITE_FIREBASE_MESSAGING_ID=******
VITE_FIREBASE_APP_ID=******
VITE_FIREBASE_MEASUREMENT_ID=******

🔗 Create: addDoc( )

하위 컬렉션에 문서 생성

import { collection, addDoc, getDocs } from 'firebase/firestore';
import { db } from './Firebase_Config.tsx';

const createPayrollCorApp = async (name: string, month: number, option: string, correctionDetails: string) => {
  try {
    const membersSnapshot = await getDocs(collection(db, 'members'));
    for (const memberDoc of membersSnapshot.docs) {
      const collectionSnapshot = await getDocs(collection(db, `members/${memberDoc.id}/payrollCorApp`));
      for (const payDataDoc of collectionSnapshot.docs) {
        const payData = payDataDoc.data();
        if (payData.name === name) {
          await addDoc(collection(db, `members/${memberDoc.id}/payrollCorApp`), {
            name: payData.name,
            month,
            correctionState: 'standBy',
            correctionDetails,
            reasonForApplication: option,
          });
          break;
        }
      }
    }
  } catch (error) {
    console.log(error);
  }
};
export default createPayrollCorApp;

위의 로직은 members컬렉션 안에 parameter로 받은 유저의 정보와 같은 유저의 payrollCorApp 컬렉션의 필드를 addDoc()메서드를 사용하여 생성한다. addDoc은 문서 ID를 고유한 값으로 자동 생성되며 같은 데이터가 있으면 덮어씌워지는 것이 아닌 새로 생성한다.

🔗 Read: getDocs( )

최상위 컬렉션 읽기

import { collection, getDocs } from 'firebase/firestore';
import { db } from './Firebase_Config.tsx';

interface CollectionData {
  [key: string]: any;
}

const getUserData = async (collectionName: string) => {
  const docData: CollectionData[] = [];
  try {
    const querySnapshot = await getDocs(collection(db, collectionName));
    docData.push(querySnapshot.docs.map((doc) => doc.data()));
  } catch (error) {
    console.error(error);
  }
  return docData;
};

getUserdata( )는 collectionName을 인자로 받아 getDocs메서드를 사용하여 firestore database에 있는 collectionName의 데이터를 읽을 수 있다.

하위 컬렉션 읽기

const getCollectionData = async () => {
  const allDocData: CollectionData[] = [];
  try {
    const membersSnapshot = await getDocs(collection(db, 'members'));

    for (const memberDoc of membersSnapshot.docs) {
      const userDocId = memberDoc.id;
      const collectionSnapshot = await getDocs(collection(db, `members/${userDocId}/payrollDetails`));
      collectionSnapshot.docs.forEach((obj) => {
        allDocData.push(obj.data());
      });
    }
  } catch (error) {
    console.log(error);
  }
  return allDocData;
};

상위 컬렉션을 읽고 컬렉션의 문서 ID를 조회하여 하위 컬렉션을 읽는다.

🔗 Update: setDoc( )

하위 컬렉션 필드 수정

import { collection, doc, setDoc, getDocs } from 'firebase/firestore';
import { db } from './Firebase_Config.tsx';

const updateCorrectionState = async (name: string, month: number, state: string, correctionDetails: string) => {
  try {
    const membersSnapshot = await getDocs(collection(db, 'members'));
    for (const memberDoc of membersSnapshot.docs) {
      const collectionSnapshot = await getDocs(collection(db, `members/${memberDoc.id}/payrollCorApp`));
      for (const payDataDoc of collectionSnapshot.docs) {
        const payDataId = payDataDoc.id;
        const payData = payDataDoc.data();
        if (payData.name === name && payData.month === month && payData.correctionDetails === correctionDetails) {
          await setDoc(doc(db, `members/${memberDoc.id}/payrollCorApp`, payDataId), {
            name: payData.name,
            month: payData.month,
            correctionState: state,
            correctionDetails: payData.correctionDetails,
            reasonForApplication: payData.reasonForApplication,
          });
          break;
        }
      }
    }
  } catch (error) {
    console.log(error);
  }
};

export default updateCorrectionState;

읽기와 동일하게 getDocs메서드를 사용하여 컬렉션의 문서를 조회하고 해당 문서 중 조건에 맞는 문서를 찾아 setDoc메서드를 사용하여 데이터를 업데이트한다. 추가로 doc 메서드를 사용하여 컬렉션 내의 문서 ID를 참조한다. 여기서 동일한 값의 필드는 조회된 값과 동일하게 넣어주고 바꿀 필드만 값을 넣어주어야 기존 필드를 유지하면서 수정할 수 있다.
addDoc처럼 setDoc도 필드를 새로 생성하지만 다른점은 이미 존재하는 데이터를 덮어쓴다는 점이다. addDoc과 setDoc의 차이점을 알고 적절히 사용해야 한다.

🔗 Delete: deleteDoc( )

하위 컬렉션 문서 삭제

import { collection, deleteDoc, doc, getDocs } from 'firebase/firestore';
import { db } from './Firebase_Config.tsx';

const deleteSalaryCorrectionAPI = async (name: string, month: number, correctionDetails: string) => {
  try {
    const membersSnapshot = await getDocs(collection(db, 'members'));
    for (const memberDoc of membersSnapshot.docs) {
      const collectionSnapshot = await getDocs(collection(db, `members/${memberDoc.id}/payrollCorApp`));
      for (const payDataDoc of collectionSnapshot.docs) {
        const payData = payDataDoc.data();
        if (payData.name === name && payData.correctionDetails === correctionDetails) {
          await deleteDoc(doc(db, `members/${memberDoc.id}/payrollCorApp`, payDataDoc.id));
          break;
        }
      }
    }
  } catch (error) {
    console.log(error);
  }
};
export default deleteSalaryCorrectionAPI;

Update방식처럼 doc메서드를 통해 문서의 ID를 참조하고 deleteDoc메서드를 사용하여 해당 ID를 가진 문서와 그 필드를 삭제한다.

profile
성장하는 과정에서 성취감을 통해 희열을 느낍니다⚡️

0개의 댓글

관련 채용 정보