이제 로그인을 하면 게시판에 글쓰기 기능을 넣을 것임.
input에 입력된 value값 state에 저장.
Home.js
const handleContents = (event) => {
const {
target: { value },
} = event;
setContent(value);
};
콘솔 - 프로젝트 - create database -start in test mode - 지역선택 (지역에 따라 요금 다름)
cloud firestore의 database는 noSQL database임
collection / document가 있음
collection : 기본적으로 폴더와 같음.
document :문서와 같음.
import { getFirestore } from "firebase/firestore";
export const dbService = getFirestore();
submit할 때마다 document 생성
메소드가 여러가지인데 일단 collection
을 이용하기
collection
은 collection경로라는 걸 줘야함.
(명시된 데이터를 담은 새로운 document을 collections에 추가하는 것임)
자동으로 id를 생성해서 문서를 추가할 때는 addDoc
을 사용
공식문서 참고 :
https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ko&authuser=0
이때 addDoc안에 데이터를 넣을 수 있는데 어떠한 데이터도 가능
나는 text, createAt를 넣음 (나중에 추가 및 수정)
Home.js
const handleSubmit = async (e) => {
try {
const docRef = await addDoc(collection(dbService, "fweets"), {
text: content,
createAt: Date.now(),
});
setContent(docRef);
} catch (error) {
console.log(error);
}
이렇게 하면 cloud firestore에 collection과 fweets document가 추가된 것을 볼 수 있음
Home.js
const [contents, setContents] = useState([]);
const getFweets = async()=>{
const dbFweets = await db.collection("fweets").get()
console.log(dbFweets);// dbFweets는 querySnapshat임
//해당되는 메소드를 이용해 data를 가져옴. 그래서 일단 아래와 같이 콘솔찍기
//dbFweets.forEach(document => console.log(document.data());
//모든 이전 fweets에 대해 배열을 리턴, 이때 리턴하는 것들은 새로 작성한 트윗과
//이전것들임
dbFweets.forEach(document => {
setContents(prev => [document.data(), ...prev])
//async함수를 써야해서 별도의 함수로 저장
useEffect(()=>{
getFweets();
},[]);
Home.js
const [contents, setContents] = useState([]);
const getFweets = async()=>{
const dbFweets = await db.collection("fweets").get()
dbFweets.forEach(document => {
const fweetObject = {
...document.data(), // 모든 데이터를 가질 것이고
id :document.id,//아이디도 가질 것임.
setContents(prev => [fweetObject, ...prev])
//async함수를 써야해서 별도의 함수로 저장
useEffect(()=>{
getFweets();
},[]);
Home.js
return(
...
<div className="fweetWrap">
{contents.map((fweet) => (
<div key={fweet.id}>{fweet.fweet}</div>
)}
</div>
);
App.js
const [userObj, setUserObj] = useState(null)
useEffect(() => {
auth.onAuthStateChanged((user) => {
if (user) {
setIsLogin(true);
setUserObj(user);
} else {
setIsLogin(false);
}
setInit(true);
});
}, []);
auth가 바뀐다면 우리가 받을 user에 setUserObj를 넣을 것임
로그인이 되면 onAuthStateChanged
가 호출됨. 그럼 로그인한 user를 받게 됨.
즉 우리는 이제 누가 로그인했는지 알게 됨.
handleSubmit
함수에 userId 넣어서 수정Home.js
const handleSubmit = async (e) => {
try {
const docRef = await addDoc(collection(dbService, "fweets"), {
text: content,
createAt: Date.now(),
creatorId: userObj.uid // console.log(userObj)찍어서 uid찾음
});
setContent(docRef);
} catch (error) {
console.log(error);
}
파이어베이스 콘솔 document에서 creatorId라는 새로운 field가 생김.
이제 누가 fweet을 생성했는지 알 수 있게됨.
데이터베이스를 계속해서 실시간으로 가져오는 작업을 할 것임.
이때 사용하는 메소드는 onSnapshot
임. 이건 리스너임.
Home.js의 getFweets함수와 useEffect 함수를 수정할 것임.
참고문서 : https://firebase.google.com/docs/firestore/query-data/listen?authuser=0&hl=ko#listen_to_multiple_documents_in_a_collection
//이전에 만들어둔 getFweets함수는 아예 지움
Home.js
const [contents, setContents] = useState([]);
useEffect(() => {
const q = query(collection(dbService, "fweets"));
onSnapshot(q, (snapshot) => {
const fweetArray = snapshot.docs.map((doc) => ({ //모든 doc은 objects를 반환할 건데 그 내용은 아래와 같음.
id: doc.id,
...doc.data(),
}));
setContents(fweetArray);
});
}, []);
snapshot은 우리가 가진 query와 같음. 하지만 이건 docs를 가지고 있음.
fweets는 우리가 페이지를 불러올 때 snapshot에서 나오는 것임.
여기서 full map을 만들건데 (fweets 배열) 반환한 배열을 setContents에 넣어줌.
참고문서 : https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ko
Home.js
<div>
{contents.map((fweet) =>{
<Fweet key={fweet.id} fweetObj ={fweetObj}, isOwner={fweet.creatorId === userObj.uid}
})};
</div>
Fweet.js
{isOwner &&
<>
<button>Delete </button>
<button>Edit </button>
</>
}
Fweet.js
import { doc, deleteDoc, updateDoc } from "firebase/firestore";
const Fweet = ({ userObj, fweetObj, isOwner }) => {
const FweetTextRef = doc(dbService, "fweets", `${fweetObj.id}`); //document의 id
const handleDelete = async () => {
const ok = window.confirm("Are you sure?");
if (ok) {
await deleteDoc(FweetTextRef);
await deleteObject(ref(storageService, fweetObj.fileUrl));
}
};
Fweet.js
import { doc, deleteDoc, updateDoc } from "firebase/firestore";
const Fweet = ({ userObj, fweetObj, isOwner }) => {
const [editing, setEditing] = useState(false); //edit모드 확인
const [editedFweet, setEditedFweet] = useState(fweetObj.text); //input에 입력된 text 업데이트
const FweetTextRef = doc(dbService, "fweets", `${fweetObj.id}`); //document의 id
//토글 함수 : 이전값 반대로 return 해주는 함수를 넣음
//true or false 여부에 따라 보여지는 div가 달라짐
const handleEdit = () => {
setEditing((prev) => !prev);
};
const submitEdit = async (e) => {
e.preventDefault();
await updateDoc(FweetTextRef, {
text: editedFweet, //이전 양식이랑 같아야함.
});
setEditing(false); //edit모드 꺼주기
};
//input에 수정하는 value값 보이게 함.
const editFweet = (event) => {
const {
target: { value },
} = event;
setEditedFweet(value);
};
return (
<StyledFweet>
<div>
{editing ? (
<div className="editFweet">
<form onSubmit={submitEdit}>
<input
type="text"
placeholder="Edit your Fweet"
value={editedFweet}
required
onChange={editFweet}
className="editTextInput"
/>
<input type="submit" value="Update" className="editTextSubmit" />
</form>
<button onClick={handleEdit}>Cancel</button>
</div>
) : (
<div>
<div className="fweetText">
<div className="textWrap">
<div className="userName">by {fweetObj.userName}</div>
<div className="text">
<p>{fweetObj.text}</p>
</div>
</div>
{isOwner && (
<div className="buttonWrap">
<button onClick={handleDelete}>
<BsTrash3 />
</button>
<button onClick={handleEdit}>
<AiOutlineEdit />
</button>
</div>
)}
</div>
</div>
)}
⭐️ 결과물
보면 작성자가 아닌 사람은 삭제버튼이랑 수정버튼이 안보인다.
그리고 삭제버튼 클릭시에 알림창이 뜨게 되어있고, 수정버튼 누르면 수정화면이 나옴.
이후에 수정한 내용으로 바꿔서 update버튼 누르면 수정 완료!
❗️어려웠던 점
ver9으로 firebase 이용하려니까 어려움이 많았다..
공식문서가 잘되어있긴하지만 그 공식문서 자료 찾는 것도 쉽지많은 않았음.
특히 실시간 데이터 가져오는 onSnapshot 메소드를 이용하는 게 제일 어려웠다.
공식문서 위치도 실시간업데이트 수신대기 이런이름에 컬렉션의 여러문서에 리슨이라고 적혀있는 부분이었음 ㅋㅋㅋㅋ게다가 query 메소드랑 같이 사용하는 거라 어려움이 있었다...
파이어베이스 공식문서 볼 때는 목차를 기준삼아 순차적으로 확인하는게 좋을 듯하다.
당연하지만 ~시작하기 이런 목차에서도 도움이 되는 내용들이 많았음 ㅎㅎㅎ.