๐ก์ค๋ ํ ๊ฒ!
- firebase ์ฐ๋
1๏ธโฃ firebase ์ค์น
๐ firebase๋?
- NoSQL ๊ธฐ๋ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค
- RTSP ๋ฐฉ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ง์
- RTSP : Real Time Stream Protocol. ์ค์๊ฐ์ผ๋ก ๋ฐ์ดํฐ๋ค์ ์ ์ก
1. firebase ์์ํ๊ธฐ
2. ํ๋ก์ ํธ ๋ง๋ค๊ธฐ
3. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง๋ค๊ธฐ - Firestore Database
4. ์ปฌ๋์ ๋ง๋ค๊ธฐ
5. ํ๋ก์ ํธ ์ค์ / ํ๋ซํผ ์ ํ
- ์ฑ ๋ฑ๋ก ํ Firebase SDK ์ถ๊ฐ
6. firebase ์ค์น
- ํฐ๋ฏธ๋์์ server๋ก ์ด๋
npm install firebase
7. SDK์์ฑ (๋ณด์์ ์ํด .env ํ์ผ ์์ฑ ํ ๋ถ๋ฌ์ค๊ธฐ)
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import "dotenv/config";
import { getFirestore } from "firebase/firestore";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: process.env.fb_apiKey,
authDomain: process.env.fb_authDomain,
projectId: process.env.fb_projectId,
storageBucket: process.env.fb_storageBucket,
messagingSenderId: process.env.fb_messagingSenderId,
appId: process.env.fb_appId,
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export default app;
export const db = getFirestore(app);
2๏ธโฃ Product ์ฐ๋
1. Query
const productResolver: Resolver = {
Query: {
products: async (parent, { cursor = "", showDeleted = false }) => {
const products = collection(db, "products");
const queryOptions = []; // orderby Error with startAfter, where
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[] = [];
console.log(snapshot);
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,
};
},
},
}
โ๏ธ collection : firebase์์ ๋ง๋ ์ปฌ๋์ ์ค products ์ปฌ๋์ ์ ๊ฐ์ ธ์ด
โ๏ธ doc(), getDoc()
- doc() : ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐธ์กฐํ๋ฉฐ, ์ปฌ๋์
์ด๋ฆ, ๋ฌธ์ ID๋ฅผ ์ธ์๋ก ์ฌ์ฉ
- ๋ฐ๋ผ์, cursor(ํ์ด์ง ๋ ์ํ ID)์ ์ ๋ฌ
- getDoc() : doc() ๋ฉ์๋์์ ์ธ๊ธํ ์ปฌ๋์
๊ธฐ๋ฐ ์ฐธ์กฐ์์ ํน์ ๋ฌธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
- promise๋ฅผ ๋ฐํํ๊ณ ๊ทธ ์์ await ํค์๋ ์ถ๊ฐ
โ๏ธ startAfter : ์์์ ์ ์ ์ธํ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ
โ๏ธ ๋ง์ฝ ์ญ์ ๋ ์ํ(!showDeleted)์ด๋ผ๋ฉด ๋ฐฐ์ด์์ where๋ฌธ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ฐ์ดํฐ๋ฅผ unshift.
- where("createdAt", "!=", null) : createdAt์ด ์๋ ๋ฐ์ดํฐ
โ๏ธ query : ๋ฌธ์ ID๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์ถฉ์กฑํ๋ ๋ชจ๋ ๋ฌธ์ ๊ฒ์ (์ค๋ฆ์ฐจ์)
- orderBy()๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ์ ์ ๋ ฌ ์์๋ฅผ ์ง์ ํ ์ ์์
- limit()์ ์ฌ์ฉํ์ฌ ๊ฒ์๋๋ ๋ฌธ์์ ์๋ฅผ ์ ํํ ์ ์์
โ๏ธ snapshot.forEach : ๋ฐ์ดํฐ ๊ฐ๊ฐ์ id์ ๊ฐ์ data ๋ฐฐ์ด์ ์ต์ข ์ ์ผ๋ก ์ ์ฅ
2. Mutation
1) addProduct
- ํด๋ผ์ด์ธํธ์์ ๋ณด๋ด์จ ๋ฐ์ดํฐ ์ ๋ณด๋ค์ newProduct ๊ฐ์ฒด์ ์ ์ฅ.
- addDoc : Create๊ธฐ๋ฅ์ addDoc ๋ฉ์๋๋ฅผ ์ฌ์ฉ.
- addDoc("ํค๋ฅผ ํตํด ๊ฐ์ ธ์จ db", "payload")
addProduct: async (parent, { imageUrl, price, title, description }) => {
const newProduct = {
imageUrl,
price,
title,
description,
createdAt: serverTimestamp(),
};
// products ์ปฌ๋์
์ newProduct ๋ฐ์ดํฐ ์ถ๊ฐ
const result = await addDoc(collection(db, "products"), newProduct);
const snapshot = await getDoc(result);
return {
...snapshot.data(),
id: snapshot.id,
};
},
2) updateProduct
- doc : ์ ๋ฐ์ดํธํ ๋ฐ์ดํฐ๋ฅผ doc ๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ ธ์ด
- ๋ง์ฝ ์ ๋ฐ์ดํธํ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ฉด ์๋ฌ ๋ฐํ
- updateDoc: ๋๋ฒ์งธ ๊ฐ์ผ๋ก ๊ธฐ์กด์ ์๋ key๊ฐ์ ๋ฃ๊ณ value๋ฅผ ๋ฃ์ผ๋ฉด ๊ธฐ์กด ๊ฐ์ด ๋์ฒด๋๊ณ , ์๋ key์ value๋ฅผ ๋ฃ์ผ๋ฉด key, value๊ฐ ์ถ๊ฐ.
- data: ํด๋ผ์ด์ธํธ์์ ๋ณด๋ธ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ ์ ๋ณด.
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,
};
},
3) deleteProduct
- ์ญ์ ๋ ์ํ์ด๋ผ๋ ํ์๋ง ํด์ฃผ๊ธธ ์ํ์ผ๋ฏ๋ก, updateDoc์ ํตํด createdAt ๊ฐ๋ง ์ญ์ ์์ผ์ ์ผ๋ฐ ์ํ๊ณผ ์ญ์ ์ํ์ ๊ตฌ๋ถ
deleteProduct: async (parent, { id }) => {
// ์ค์ db์์ delete๋ฅผ ํ๋ ๋์ , createdAt์ ์ง์์ค๋ค.
const productRef = doc(db, "products", id);
if (!productRef) throw new Error("์ํ์ด ์์ต๋๋ค.");
await updateDoc(productRef, { createdAt: null });
return id;
},
3๏ธโฃ Cart ์ฐ๋
1. query
const cartResolver: Resolver = {
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;
},
},
}
2. Mutation
1) addCart
- ์ฅ๋ฐ๊ตฌ๋์ ์ถ๊ฐํ ์ํ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธฐ
- cart ์ปฌ๋์ ์ ์ถ๊ฐํ ์ํ๋ฐ์ดํฐ์ ๊ฐ์ด ๊ฐ์ ๊ฒ์ด ์๋์ง ํ๋ณ
- ๋ง์ฝ ๊ฐ์ ์ํ์ด ์๋ค๋ฉด?
- amount: increment(1): ์๋ 1์ฉ ์ฆ๊ฐ
- ๋ง์ฝ ๊ฐ์ ์ํ์ด ์๋ค๋ฉด?
- ์๋ 1๋ก ์ ์ฅ, ํด๋น ๋ฐ์ดํฐ ์ ์ฅ.
addCart: async (parent, { productId }) => {
if (!productId) throw Error("์ํid๊ฐ ์๋ค!");
const productRef = doc(db, "products", productId);
const cartCollection = collection(db, "cart");
const exist = (
await getDocs(query(cartCollection, 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,
};
},
2) updateCart
- ํด๋ผ์ด์ธํธ์์ ๋ณ๊ฒฝ๋ amount๊ฐ์ ์ ์ฅ
updateCart: async (parent, { cartId, amount }) => {
if (amount < 1) throw Error("1 ์ดํ๋ก ๋ฐ๊ฟ ์ ์์ต๋๋ค.");
const cartRef = doc(db, "cart", cartId);
if (!cartRef) throw Error("์ฅ๋ฐ๊ตฌ๋ ์ ๋ณด๊ฐ ์๋ค!");
await updateDoc(cartRef, {
amount,
});
const cartSnapshot = await getDoc(cartRef);
return {
...cartSnapshot.data(),
id: cartSnapshot.id,
};
},
3) deleteCart
- deleteDoc์ ์ปฌ๋์ ๊ณผ ํด๋น ๋ฐ์ดํฐ ID๋ฅผ ๋ฃ๊ธฐ
deleteCart: async (parent, { cartId }) => {
const cartRef = doc(db, "cart", cartId);
if (!cartRef) throw new Error("์ฅ๋ฐ๊ตฌ๋ ์ ๋ณด๊ฐ ์์ต๋๋ค");
await deleteDoc(cartRef);
return cartId;
},
4) executePay
executePay: async (parent, { ids }) => {
// createdAt์ด ๋น์ด์์ง ์์ ids๋ค์ ๋ํด์ ๊ฒฐ์ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋์๋ค๊ณ ๊ฐ์ ํ๊ณ
// cart์์ ์ด๋ค์ ids๋ฅผ ์ง์์ค๋ค.
const deleted = [];
for await (const id of ids) {
const cartRef = doc(db, "cart", id);
const cartSnapshot = await getDoc(cartRef);
const cartData = cartSnapshot.data();
const productRef = cartData?.product;
if (!productRef) throw Error("์ํ์ ๋ณด๊ฐ ์๋ค.");
const product = (await getDoc(productRef)).data() as Product;
if (product.createdAt) {
await deleteDoc(cartRef);
deleted.push(id);
}
}
return deleted;
},
๋!! ๋ค์์ ๋ฐฐํฌ๋ค,, ๊ทผ๋ฐ ๊ฐ์์์ ๋ค๋ฃฌ heroku๋ ๋ฌด๋ฃ๊ฐ ๋๋ฌ๋ค๋๋ฐ.. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฐพ์๋ด์ผํ ๊ฒ ๊ฐ๋ค
๋๊ทผ๋๊ทผ..