[ React ] firebase와 연동하기

CJY00N·2023년 7월 22일
0

react

목록 보기
10/10
post-thumbnail

firebase의 firestore를 db로 사용할 것이다.

파이어베이스 연동

server디렉토리에서 파이어베이스 dependency를 설치한다.

yarn add firebase

▼ server/firebase.js
아래 내용이 포함되도록 파이어베이스 설정을 한다.

파이어베이스 쿼리 작성

상품 쿼리 작성

▼ server/resolvers/product.ts

  • createAt 기준으로 내림차순 정렬
  • startAt 옵션은 쿼리 결과의 시작 지점을 지정한다.
  • unshift() : 배열 앞에 새로운 값을 추가하고 배열의 전체 개수를 리턴함 /
    shift() : 배열의 맨 앞 값을 삭제하고 삭제한 값을 리턴함
  • where 옵션은 쿼리를 필터링하는 데 사용된다. (특정 조건을 만족하는 데이터만 가져오거나 쿼리함)
  • query에 limit를 주어 15개씩만 가져오도록 한다.
  • getDocs는 콜렉션의 모든 문서를 가져오는데 사용되며 비동기로 동작한다.(promise를 반환)
  • getDoc은 문서의 위치를 가리키는 레퍼런스로, 해당 (개별)문서의 데이터를 읽거나 쓰는 데 사용됨
Query: {
    products: async (
      parent,
      { cursor = "", showDeleted = false } /*{ db }*/
    ) => {
      const products = collection(db, "products");
      const queryOptions: any[] = [orderBy("createdAt", "desc")];
      if (cursor) {
        const snapshot = await getDoc(doc(db, "products", cursor));
        queryOptions.push(startAfter(snapshot));
      }
      if (!showDeleted) {
        queryOptions.unshift(where("createdAt", "!=", null));
      }
      const q = query(products, ...queryOptions, limit(PAGE_SIZE));

      const snapshot = await getDocs(q);
      const data: DocumentData[] = [];
      snapshot.forEach((doc) => data.push({ id: doc.id, ...doc.data() }));
      return data;
    },
    product: async (parent, { id }) => {
      const snapshot = await getDoc(doc(db, "products", id));
      return {
        ...snapshot.data(),
        id: snapshot.id,
      };
    },
  },

장바구니 쿼리 작성

▼ server/src/cart.ts

  • getDocs로 cart db를 모두 가져온 다음 forEach로 data에 하나씩 push해준다.
  • CartItem인 product는 id값으로 찾아 리턴한다.
  Query: {
    cart: async (parent, args) => {
      const cart = collection(db, "cart");
      const cartsnap = await getDocs(cart);
      const data: DocumentData[] = [];
      cartsnap.forEach((doc) => {
        const d = doc.data();
        data.push({ id: doc.id, ...d });
      });
      return data;
      // return db.cart;
    },
  },
CartItem: {
    product: async (cartItem, args) => {
      const product = await getDoc(cartItem.product);
      const data = product.data() as any;
      return {
        ...data,
        id: product.id,
      };
      // return db.products.find((product) => product.id === cartItem.id);
    },
  },

상품 mutation 작성

  • addProduct는 addDoc을 사용한다.
  • updateProduct는 updateDoc을 사용한다.
  • deleteProduct는 updateDoc을 사용해 createdAt 값을 null로 수정한다.

▼ server/src/resolvers/product.ts

addProduct: async (parent, { imageUrl, price, title, description }) => {
      const newProduct = {
        price,
        imageUrl,
        title,
        description,
        createdAt: serverTimestamp(),
      };
      const result = await addDoc(collection(db, "products"), newProduct);
      const snapshot = await getDoc(result);
      return {
        ...snapshot.data(),
        id: snapshot.id,
      };
}
updateProduct: async (parent, { id, ...data }) => {
      const productRef = doc(db, "products", id);
      if (!productRef) throw new Error("존재하지 않는 상품입니다.");
      await updateDoc(productRef, data);
      const snap = await getDoc(productRef);
      return {
        ...snap.data(),
        id: snap.id,
      };
}
deleteProduct: async (parent, { id }) => {
      const productRef = doc(db, "products", id);
      if (!productRef) throw new Error("존재하지 않는 상풉입니다.");
      await updateDoc(productRef, { createdAt: null });
      return id;
}

장바구니 mutation 작성

  • addCart는 addDoc을 사용한다.
  • updateCart는 updateDoc을 사용한다.
  • deleteCart는 deleteDoc을 사용한다.

▼ server/src/resolvers/cart.ts

addCart: async (parent, { productId }) => {
      if (!productId) throw Error("상품 productId가 없습니다.");
      const productRef = doc(db, "products", productId);
      const cartCollection = collection(db, "cart");
      const exist = (
        await getDocs(
          query(collection(db, "cart"), where("product", "==", productRef)))).docs[0];

      let cartRef;
      if (exist) {
        cartRef = doc(db, "cart", exist.id);
        await updateDoc(cartRef, { amount: increment(1) });
      } else {
        cartRef = await addDoc(cartCollection, {
          amount: 1,
          product: productRef,
        });
      }
      const cartSnapshot = await getDoc(cartRef);

      return {
        ...cartSnapshot.data(),
        product: productRef,
        id: cartSnapshot.id,
      };
}
updateCart: async (parent, { cartId, amount }) => {
      if (amount < 1) throw new Error("수량은 1이상이어야 합니다.");
      const cartRef = doc(db, "cart", cartId);
      if (!cartRef) throw new Error("장바구니 정보가 없습니다");
      await updateDoc(cartRef, { amount });
      const cartSnapshot = await getDoc(cartRef);

      return {
        ...cartSnapshot.data(),
        id: cartSnapshot.id,
      };
}
deleteCart: async (parent, { cartId }) => {
      const cartRef = doc(db, "cart", cartId);
      if (!cartRef) throw new Error("장바구니 정보가 없습니다");
      await deleteDoc(cartRef);
      return cartId;
}

결제처리 mutation

  • ids로 전달받은 id들을 for문으로 돌면서 cartData를 얻고, 만약 createAt이 있으면(결제가 가능하면) cartRef에서 삭제한다.
executePay: async (parent, { ids }) => {
      const deleted = [];
      for await (const id of ids) {
        const cartRef = doc(db, "cart", id);
        const cartSnapshop = await getDoc(cartRef);
        const cartData = cartSnapshop.data();
        const productRef = cartData?.product;
        if (!productRef) throw new Error("상품 정보가 없습니다.");
        const product = (await getDoc(productRef)).data() as Product;
        if (product.createdAt) {
          await deleteDoc(cartRef);
          deleted.push(id);
        }
      }
      return deleted;
}

클라이언트에 DB변경 적용하기

삭제복구 기능 추가

  • 삭제한 상품을 다시 목록으로 복귀하기 위해서 기존의 수정버튼을 누르면 다시 복구하도록 할 것이다.
  • 따라서 updateProduct를 할 때 createdAt을 다시 지정해주어 복구하도록 한다.

▼ server/src/resolvers/product.ts

    updateProduct: async (parent, { id, ...data }) => {
      const productRef = doc(db, "products", id);
      if (!productRef) throw new Error("존재하지 않는 상품입니다.");
      await updateDoc(productRef, { ...data, createdAt: serverTimestamp() });
      const snap = await getDoc(productRef);
      return {
        ...snap.data(),
        id: snap.id,
      };
    },

삭제된 상품에 위 사진과 같이 수정 및 복구 버튼이 뜨고 수정완료를 누르면 원래대로 복구된다.

profile
COMPUTER SCIENCE ENGINEERING / Web Front End

1개의 댓글

comment-user-thumbnail
2023년 7월 22일

좋은 글 감사합니다.

답글 달기