노마드코더 트위터클론코딩강의를 보면서 v9 버전으로 코드를 변경하여 변경한 코드 일부만 작성하였습니다.
Firebase에서 database로 Firestore Database와 Realtime Database를 제공한다. 둘다 실시간으로 데이터를 받아올 수 있지만 가장 큰 차이점은 데이터의 형태이다. Realtime database에서 데이터는 JSON형태로 받아왔다면 Firestore에서는 collection와 document의 형태로 이루어져있다. NoSQL이지만 SQL처럼 테이블 형태로 관리가 편하다는 장점이 있다.
더 자세한 차이점은 공식 홈페이지를 참고하자!
더 나은 확장성, 독립성을 위해 firstore Service에 관한 NweetRepository Class를 따로 만들어서 index.js에서 해당 class의 인스턴스를 만들어서 App에 주입하는 방식으로 하였다.
// index.js
import React from "react";
import ReactDOM from "react-dom";
import AuthService from "service/authService";
import NweetRepository from "service/nweetRepository";
import App from "./components/App";
import { firebaseApp } from "./service/firebase";
const authService = new AuthService(firebaseApp);
const nweetRepository = new NweetRepository();
ReactDOM.render(
<React.StrictMode>
<App authService={authService} nweetRepository={nweetRepository} />
</React.StrictMode>,
document.getElementById("root")
);
// nweetRepository.js
import {
getFirestore,
collection,
addDoc,
getDocs,
onSnapshot,
deleteDoc,
doc,
updateDoc,
} from "firebase/firestore";
class NweetRepository {
constructor() {
this.db = getFirestore();
}
addNweet(nweet, uid) { // 데이터 추가
addDoc(collection(this.db, "nweets"), {
text: nweet,
createdAt: Date.now(),
creatorId: uid,
});
}
getNweets = async (onUpdate) => { // 데이터 받아오기
const dbNweets = await getDocs(collection(this.db, "nweets"));
dbNweets.forEach((doc) => {
const nweetObj = {
...doc.data(),
id: doc.id,
};
onUpdate((prev) => [...prev, nweetObj]);
});
};
asyncNweets(onUpdate) { // 실시간 데이터 동기화
onSnapshot(collection(this.db, "nweets"), (snapshot) => {
const nweetArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
onUpdate(nweetArray);
});
}
deleteNweet(id) { // 데이터 삭제
return deleteDoc(doc(this.db, "nweets", id));
}
updateNweet(id, newNweet) { // 데이터 수정
return updateDoc(doc(this.db, "nweets", id), {
text: newNweet,
});
}
}
export default NweetRepository;
// nweetRepository 클래스
addNweet(nweet, uid) { // 데이터 추가
addDoc(collection(this.db, "nweets"), {
text: nweet,
createdAt: Date.now(),
creatorId: uid,
});
}
// 컴포넌트
const [nweet, setNweet] = useState("");
const onSubmit = async (event) => {
event.preventDefault();
await nweetRepository.addNweet(nweet, userObj.uid);
setNweet("");
};
const onChange = (event) => {
const { value } = event.target;
setNweet(value);
};
1) 데이터 한번 가져오기
// Class
getNweets = async (onUpdate) => {
const dbNweets = await getDocs(collection(this.db, "nweets"));
dbNweets.forEach((doc) => {
const nweetObj = {
...doc.data(),
id: doc.id,
};
onUpdate((prev) => [...prev, nweetObj]);
});
};
// Component
const [nweets, setNweets] = useState([]);
useEffect(() => {
nweetRepository.getNweets(setNweets);
}, [nweetRepository]);
=> 하지만 getDocs()를 이용했을때 실시간으로 업데이트된 결과를 보기위해서는 새로고침을 해야한다.
2) 실시간 업데이트 수신대기
asyncNweets(onUpdate) {
onSnapshot(collection(this.db, "nweets"), (snapshot) => {
const nweetArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
onUpdate(nweetArray);
});
}
useEffect(() => {
nweetRepository.asyncNweets(setNweets);
}, [nweetRepository]);
// Class
updateNweet(id, newNweet) {
return updateDoc(doc(this.db, "nweets", id), {
text: newNweet,
});
}
// Component
const onSubmit = async (e) => {
e.preventDefault();
await nweetRepository.updateNweet(nweetObj.id, newNweet);
setEditiong(false);
};
// Class
deleteNweet(id) {
return deleteDoc(doc(this.db, "nweets", id));
}
// Component
const onDeleteClick = async () => {
const ok = window.confirm("Are you sure you want to delete this nweet?");
if (ok) await nweetRepository.deleteNweet(nweetObj.id);
};
참고: 노마드코더 트위터 클론코딩
firebase doc
https://firebase.google.com/docs/firestore/rtdb-vs-firestore?hl=ko