Twitter Clone Coding
[Twitter Clone Coding] #3 NWEETING
jer0618
2022. 11. 5. 07:42
https://nomadcoders.co/nwitter
트위터 클론코딩 – 노마드 코더 Nomad Coders
React Firebase for Beginners
nomadcoders.co
*노마드코더 트위터 클론코딩 강의내용을 정리한 글입니다.
**강의에서는 Firebase v8 를 사용했지만 여기서는 Firebase v9을 사용하여 구현했습니다.
Form and Database Setup
Form 생성
- nweet을 작성하기 위한 form 생성
// Home.js
import React, { useState } from "react";
export default function Home() {
const [nweet, setNweet] = useState("");
const onSubmitHandler = (event) => {
event.preventDefault();
};
const onChangeHandler = (event) => {
setNweet(event.target.value);
};
return (
<div>
<form onSubmit={onSubmitHandler}>
<input type="text" value={nweet} onChange={onChangeHandler} placeholder="What's on your mind?" maxLength={120} />
<input type="submit" value="Nweet" />
</form>
</div>
);
}
Datebase 설정
- firebase 콘솔 ⇒ 빌드(develop) ⇒ Firestore Database(Cloud Firestore)
- database 생성 (test mode)
- fb.js 에 db관련 코드 추가
// fb.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
};
const fbApp = initializeApp(firebaseConfig);
export const fbAuth = getAuth(fbApp);
export const fbStore = getFirestore(fbApp); // 추가
Nweeting!
https://console.firebase.google.com/project/nwitter-9aeb9/firestore/data/~2F
- Cloud Firestore의 database는 NoSQL database
- Mongo DB와 유사-
용어
- Collection : 일종의 폴더
- Document : 일종의 문서
Admin Page
- 콘솔을 통해서 직접 데이터를 추가해줄 수 있음
onSubmit
https://firebase.google.com/docs/firestore/quickstart#web-version-9_2
// Home.js
// ...생략
const onSubmitHandler = async (event) => {
event.preventDefault();
try {
const nweetData = {
nweet,
createdAt: new Date(),
creatorId: userInfo.uid, // App.js에서 userInfo를 가져와서 할당
};
const res = await addDoc(collection(fbStore, "nweets"), nweetData);
console.log(res);
setNweet("");
} catch (error) {
console.log(error);
}
};
// ...생략
- addDoc을 사용하여 db에 데이터 추가
Getting the Nweets
// Home.js
// ...생략
const getNweets = async (querySnapshot) => {
try {
const tempNweets = [];
querySnapshot.docs.mapforEach((doc) => {
tempNweets.push({ id: doc.id, ...doc.data() });
});
// map구문 사용가능
// const tempNweets = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
console.log(tempNweets);
setNweets([...tempNweets]);
} catch (error) {
console.log(error);
}
};
// ...생략
- getDocs를 사용하여 데이터를 가져옴
- firebase에서 반환하는 데이터는 스냅샷의 형태 ⇒ 정보 조회를 위해 forEach와 data() 메소드 사용
TimeStamp
https://firebase.google.com/docs/reference/js/firestore_.timestamp
- firebase에서 시간은 나노초 단위로 기록
- JS에서 사용하기 위해서는 받아온 timestamp객체를 toDate()메소드를 사용해 JS Date객체로 변환 후 사용 가능
// Home.js
<div>
{nweets.map((item, idx) => (
<div key={`nweet-${idx}`}>
<h4>{item.nweet}</h4>
<span>{item.createdAt.toDate().toLocaleString()}</span> // timestamp객체 변환
</div>
))}
</div>
Realtime Nweets
- onSnapshot을 사용해서 이벤트리스너 등록 ⇒ 실시간 반영
useEffect(() => {
onSnapshot(collection(fbStore, "nweets"), (querySnapshot) => getNweets(querySnapshot));
}, []);
const getNweets = (querySnapshot) => {
try {
const tempNweets = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
console.log(tempNweets);
setNweets([...tempNweets]);
} catch (error) {
console.log(error);
}
};
Delete and Update
- Home.js에서 Nweet.js컴포넌트 분할 및 버튼 추가
- 본인여부 판단
import React from "react";
export default function Nweet({ nweet, isOwner }) {
return (
<div>
<h4>{nweet.nweet}</h4>
<span>{nweet.createdAt.toDate().toLocaleString()}</span>
{isOwner && (
<>
<button>Delete Nweet</button>
<button>Edit Nweet</button>
</>
)}
</div>
);
}
Delete
- deleteDoc함수 사용
const deleteNweet = async () => {
const ok = window.confirm("Are you sure you want to delete this Nweet?");
if (ok) await deleteDoc(doc(fbStore, "nweets", nweet.id));
};
Update(Edit)
- updateDoc함수를 사용
const updateNweet = async (event) => {
event.preventDefault();
updateDoc(doc(fbStore, "nweets", nweet.id), { nweet: newNweet });
toggleEditing();
};
전체코드
// Nweet.js
import { deleteDoc, doc, updateDoc } from "firebase/firestore";
import React, { useState } from "react";
import { fbStore } from "../fb";
export default function Nweet({ nweet, isOwner }) {
const [editing, setEditing] = useState(false);
const [newNweet, setNewNweet] = useState(nweet.nweet);
const deleteNweet = async () => {
const ok = window.confirm("Are you sure you want to delete this Nweet?");
if (ok) await deleteDoc(doc(fbStore, "nweets", nweet.id));
};
const toggleEditing = () => setEditing((prev) => !prev);
const updateNweet = async (event) => {
event.preventDefault();
updateDoc(doc(fbStore, "nweets", nweet.id), { nweet: newNweet });
toggleEditing();
};
const onChangeHandler = (event) => {
const value = event.target.value;
setNewNweet(value);
};
return (
<div>
{editing ? (
<>
<form>
<input type="text" value={newNweet} required onChange={onChangeHandler} />
<input type="submit" value="update" onClick={updateNweet} />
</form>
<button onClick={toggleEditing}>Cancel</button>
</>
) : (
<>
<h4>{nweet.nweet}</h4>
<span>{nweet.createdAt.toDate().toLocaleString()}</span>
{isOwner && (
<>
<button onClick={deleteNweet} name="delete">
Delete Nweet
</button>
<button onClick={toggleEditing} name="edit">
Edit Nweet
</button>
</>
)}
</>
)}
</div>
);
}
반응형