3.Firestore Database
Updated:
1.Cloud FireStore
-
Cloud Firestore 의 database 는 NoSQL database 입니다. 그래서 규칙들도 많이 없고 상당히 유연하고 사용하기도 쉽습니다.
-
규칙이 많이 없기 때문에 조금 제한되는 부분이 있을 수 있습니다.
특징
-
Collection : 기본적인 폴더와 같이 category 를 나눌수 있는 기능
-
Documentation : Collection 안에 있는 database 문서들을 말합니다.
-
firebase database 만들기
테스트 모드로 일단 database 생성합니다
- 프로젝트 root 경로에 firebase.js 파일에서 firestore 를 import 해줘야 합니다
// in fbase.js on root your project
import "firebase/firestore";
// 프로젝트에서 dbService 라는 이름으로 export 해줌
export const dbService = firebase.firestore();
2.Collection Reference
- collectionReference : collection 안의 document 를 추가,수정, 삭제 할수 있는 함수
add (CREATE)
import React, { useState } from "react";
import { dbService } from "../fbase";
const Home = () => {
// State
const [chat, setChat] = useState("");
// Submit Function
const onSubmit = async (e) => {
e.preventDefault();
// "chat"이라는 collection 에 add 를 해서 data 를 넣을 수 있습니다. chat 은 useState 에서 저장된 값 (input 에서 넘어오는 값을 넣어 줄수 있습니다)
await dbService.collection("chat").add({
chat,
createAt: Date.now(),
});
// db 에 등록한 후, 다시 초기화 함
setChat("");
};
const onChange = (e) => {
const {
target: { value },
} = e;
setChat(value);
};
return (
<div>
<>
<form onSubmit={onSubmit}>
<input
value={chat}
onChange={onChange}
type="text"
placeholder="what's on your mind?"
maxLength={120}
/>
<input type="submit" value="chat" />
</form>
</>
</div>
);
};
get (READ)
- 데이터를 가져오기 위해 get() 함수를 사용함
- return 값으로 promise QuerySnapshot 을 가져오는데 데이터베이스에 있는 query docs 형태로 가져옴
data() 함수 형태로 data를 불러 올수 있음
const getChats = async () => {
const dbChats = await dbService.collection("chats").get();
dbChats.forEach((document) => console.log(document.data()));
};
- DB 에 있는 data 를 가져와서 화면에 표시하는 방법
const [chats, setChats] = useState([]);
// read DB
const getChats = async () => {
const dbChats = await dbService.collection("chats").get();
dbChats.forEach((document) => {
const chatsObj = {
id: document.id,
...document.data(),
};
// 최근의 document와 이전의 document를 붙여서 setChats 으로 다시 할당
setChats((prev) => [chatsObj, ...prev]);
});
};
// mount 할때 dbService 에서 실행할 useEffect
useEffect(() => {
getChats();
}, []);
return (
<div>
{chats.map((chat) => (
<div key={chat.id}>
<h4>{chat.chat}</h4>
</div>
))}
</div>
);
onSnapshot()
을 통해서 data를 가져오는 방식(위의const getChats
를 대신 할 수 있음!!)
// read DB realtime
useEffect(() => {
dbService
.collection("chats")
.orderBy("createAt", "desc")
.onSnapshot((snapshot) => {
const chatArray = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setChats(chatArray);
console.log(chatArray);
});
}, []);
- query 를 이용하는게 아니라 snapshot 으로 사용했기 때문에 실시간으로 확인할 수 있게 됨
delete (DELETE)
- 먼저 작성자가 인경우에만 delete edit 할 수 있게 설정하기
// in Home.js <Chat> 에 props data 로 userObj.uid 를 chat.creatorID 와 같은 경우가 isOwner{true} 를 Chat.js 에 넘김
<div>
{chats.map((chat) => (
<Chat
key={chat.id}
chatObj={chat}
isOwner={chat.creatorID === userObj.uid}
/>
))}
</div>
// in Chat.js
import React from "react";
const Chat = ({ chatObj, isOwner }) => {
return (
<>
<div>
<h4>{chatObj.text}</h4>
// isOwner 가 true 일때만 delete, edit 버튼이 보이게 수정
{isOwner && (
<>
<button>Delete Chat</button>
<button>Edit Chat</button>
</>
)}
</div>
</>
);
};
export default Chat;
- 삭제 : 메세지의 id 를 지정했기 때문에 id 를 찾아서 delete 해주면 됩니다
// Delete function
const onDeleteClick = async () => {
const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
if (check) {
// delete ok
await dbService.doc(`chats/${chatObj.id}`).delete();
}
}
return (
<>
<div>
<h4>{chatObj.text}</h4>
{isOwner && (
<>
<button onClick={onDeleteClick}>Delete Chat</button>
<button>Edit Chat</button>
</>
)}
</div>
</>
);
};
edit (EDIT)
- edit 은 기존 delete 에서 추가로
toggleEditing
,onChange
,onSubmit
등 만들어서 dbService 에 비동기로 update 해줘야 함
import React, { useState } from "react";
import { dbService } from "../fbase";
const Chat = ({ chatObj, isOwner }) => {
// State
const [editing, setEditing] = useState(false);
const [newChat, setNewChat] = useState(chatObj.text);
// Delete function
const onDeleteClick = async () => {
const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
if (check) {
// delete ok
await dbService.doc(`chats/${chatObj.id}`).delete();
}
};
// Edit function
const toggleEditing = () => setEditing((prev) => !prev);
const onSubmit = async (e) => {
e.preventDefault();
await dbService.doc(`chats/${chatObj.id}`).update({
text: newChat,
});
setEditing(false);
};
const onChange = (e) => {
const {
target: { value },
} = e;
setNewChat(value);
};
return (
<>
<div>
{editing ? (
<>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Edit your Chat"
value={newChat}
required
onChange={onChange}
/>
<input type="submit" value="Update Chat" />
<button onClick={toggleEditing}>Cancel</button>
</form>
</>
) : (
<>
<h4>{chatObj.text}</h4>
{isOwner && (
<>
<button onClick={onDeleteClick}>Delete Chat</button>
<button onClick={toggleEditing}>Edit Chat</button>
</>
)}
</>
)}
</div>
</>
);
};
export default Chat;
Sum-up
-
Home.js
에서useEffect()
안에 listener 로 firebase 의onSnapshot
을 사용하고 있습니다. -
onSnapshot 은 기본적으로 데이터 베이스에 변경사항이 발생될 경우 알림을 받아 오는 기능을 합니다. 여기예시로 받아오는것은
.collection("chats")
을 지정해 놓고 사용합니다. -
그 대이터를 받아서 chatArray 라는 배열에 데이터를 저장하고 그것을 setChats 을 통해서 state 배열에 집어 넣습니다.
// read DB realtime
useEffect(() => {
dbService
.collection("chats")
.orderBy("createAt", "desc")
.onSnapshot((snapshot) => {
const chatArray = snapshot.docs.map((doc) => ({
// 모든 받아오늘 data 의 형태는 id 와 doc.data() 형태로 되어 있습니다.
id: doc.id,
...doc.data(),
}));
setChats(chatArray);
});
}, []);
-
화면에는 chat component 를 불러와서 chat data 를 형태로 화면에 rendering 해 줌니다. 이 떄, 2개의 props 를 chat component 에 전달하게 되는데 chatObj 와 isOwner를
chats
에서 받은 데이터를 입력해서 Chat component 에 넘겨 줍니다. -
넘겨주는 chatObj 는 author, text, createdAt 형태로 되어 있습니다. isOwner는 boolean data 로 상황에 따라서 true, false props 를 넘겨 줍니다. (delete 와 edit 을 해주기 위해 chat 을 만든 사람과 === userObj.uid 이면 true , 아니면 false 가 됨)
<div>
{chats.map((chat) => (
<Chat
key={chat.id}
chatObj={chat}
isOwner={chat.creatorID === userObj.uid}
/>
))}
</div>
-
useObj.uid
는 Home 이 props 로 받는 data 로써 Router component 를 통해 받으며, 또 Router 는 useObj 를App.js
에서 props 로 받아 옵니다. -
App.js
에서는 로그인, 로그아웃이 일어날때 사용자의 정보를 저장하는 곳으로 project 에서 중요한 State 는 App.js 에 보관 합니다. (hooks 를 사용할 경우) -
onAuthStateChanged()
는 app 이 초기화 될때마다 발생됩니다. Firebase 에서 로그인을 한지 안한지 알 수 있게 해줍니다.
// in App.js
// authService.currentUser 를 통해 로그인 되었는지 안되었는지 확인 할 수 있음 (로그인 안되어 있으면 null 이 return )
const [init, setInit] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userObj, setUserObj] = useState(null);
useEffect(() => {
authService.onAuthStateChanged((user) => {
if (user) {
setIsLoggedIn(true);
setUserObj(user);
} else {
setIsLoggedIn(false);
}
setInit(true)
});
}, [])
return (
<>
{init ? <AppRouter isLoggedIn={isLoggedIn} userObj={userObj}/> : "Initializing...."}
- Chat.js component 에서는 2개의 State 를 가지고 있는데
editing
은 boolean data 로써 기본적으로 chat을 수정하고 잇는지 아닌지를 가리키는 값이고 기본값은 false임,newChat
은 input 의 값을 수정하기 위해 만든 state 로 기본값은 입력되어 있는 chatObj.text 값임
const Chat = ({ chatObj, isOwner }) => {
// State
const [editing, setEditing] = useState(false);
const [newChat, setNewChat] = useState(chatObj.text);
// Delete function
const onDeleteClick = async () => {
const check = window.confirm("정말로 메세지를 삭제하시겠습니까?");
if (check) {
// delete ok
await dbService.doc(`chats/${chatObj.id}`).delete();
}
};
// Edit function
const toggleEditing = () => setEditing((prev) => !prev);
const onSubmit = async (e) => {
e.preventDefault();
await dbService.doc(`chats/${chatObj.id}`).update({
text: newChat,
});
setEditing(false);
};
const onChange = (e) => {
const {
target: { value },
} = e;
setNewChat(value);
};
return (
<>
<div>
{editing ? (
<>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Edit your Chat"
value={newChat}
required
onChange={onChange}
/>
<input type="submit" value="Update Chat" />
<button onClick={toggleEditing}>Cancel</button>
</form>
</>
) : (
<>
<h4>{chatObj.text}</h4>
{isOwner && (
<>
<button onClick={onDeleteClick}>Delete Chat</button>
<button onClick={toggleEditing}>Edit Chat</button>
</>
)}
</>
)}
</div>
</>
);
};
🔶 🔷 📌 🔑
Reference
- normad corder firebase - https://nomadcoders.co/nwitter/lobby
Leave a comment