리액트 기초 과제인 퀴즈퀴즈 작업중 Firestore를 사용하여 DB관리를 하려고 한다.
로컬에서 더미 데이터로 1차 테스트는 완료된 상태인데, Firebase를 처음 사용해봤고 강의를 한번 보고 기억하고, 이해하기가 어려우니 다시 한번 강의를 복습하면서 나에게 필요한 내용을 흐름과 사용법에 맞추어 정리해보고자 한다.
yarn add firebase
파이어베이스 초기화를 위한 코드 작성을 하기 위해서 스크립트 파일을 만들어 준다.
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// 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
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "발급된 api 키",
authDomain: "quizquiz-986fe.firebaseapp.com",
projectId: "quizquiz-986fe",
storageBucket: "quizquiz-986fe.appspot.com",
messagingSenderId: "718714704774",
appId: "1:718714704774:web:cd0ba3652bacc7acddd207",
measurementId: "G-46NWHPDB9R"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
내가 만든 firebase.js는 파이어베이스 객체를 만들기 위한 작업으로 나중에 Cloud Firestore를 import해서 사용할 수 있는 환경을 만들어 준 것이다.
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics"; // analytics를 사용하지 않는다면 삭제
import { getFirestore } from "firebase/firestore";
...
export const db = getFirestore() // App.js에서 사용 할 DB 객체 만들기
이렇게 Firebase를 연동한 후 Firestore 설정까지 끝내고나면 우리가 export한 db객체를 사용해 내 Cloud DB에 접근이 가능하다.
import { db } from './firebase'
import { collection, getDoc, getDocs } from 'firebase/firestore'
데이터에 접근하는 방식이 비동기 통신이기 때문에 async await를 활용하여 데이터를 온전히 받아오는게 가능하다.
useEffect( async () => {
const query = await getDocs(collection(db, 'question'))
query.forEach(doc => {
console.log(doc.id, doc.data())
})
그렇다면 우리는 redux에서 관리하고 있는 상태값을 비동기로 값을 받아오거나 변경하는 등의 작업을 할때 액션 생성 함수를 작성 할 수 있게 해주는 미들웨어를 사용해야한다.
redux-thunk는 위에서 설명한 바와 같이 액션 생성 함수를 작성 할 수 있게하여 액션이 발생하기 전에 조건을 주거나 어떠한 행동을 처리할 수있게 한다.
먼저 redux-thunk를 사용하기 위해서는 패키지를 설치해야한다.
yarn add reudx-thunk
패키지를 설치 하였다면 store를 생성하는 부분에서 옵셔널한 기능을 엮어 주어야 한다.
// configStore.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import user from './modules/user'
import question from './modules/question'
import rank from './modules/rank'
const middlewares = [thunk]
const inhancer = applyMiddleware(...middlewares)
const rootReducer = combineReducers({user, question, rank})
const store = createStore(rootReducer, inhancer)
export default store
위 코드를 보면 applyMiddleware를 import하고 redux-thunk를 import해왔다.
createStore() 할때 reducer들과 옵셔널한 기능을 추가로 넣어줄수 있는데 이때 미들웨어의 묶을음 inhancer로 만들때 applyMiddleware를 사용해서 넣어 줄수 있다.
export function loadCardFB() {
return async (dispatch) => {
const cardListData = await getDocs(collection(db, 'card'))
const dbArr = []
// 여기서 forEach는 Array 내장 메서드가 아니다.
// firebase 객체에서 제공하는 메서드 임
cardListData.forEach(card => {
const cardObj = {
id: card.id,
...card.data()
}
dbArr.push(cardObj)
})
dispatch(loadCard(dbArr))
}
}
// 데이터 추가하기
const docRef = await addDoc(collection(db, '컬렉션명'), 추가할 데이터)
firestore에 추가되면서 자동으로 생성된 아이디를 참조하기 위해서 getDoc()을 사용한다.
const _doc = await getDoc(docRef)
_doc.id // 생성된 고유 id
마지막으로 액션 생성 함수를 살펴보면 다음과 같이 작성 할 수 있다.
export function addCardFB(newCard) {
return async (dispatch) => {
const docRef = await addDoc(collection(db, 'card'), newCard)
const _doc = await getDoc(docRef)
const card = {
id: _doc.id,
...newCard
}
dispatch(addCard(card))
}
}
차이점을 확인해보고 싶어서 두 객체를 콘솔에 찍어보았다.
우선 두 함수가 리턴하는 객체의 차이는 _document의 유무이다.
getDoc()을 사용해서 추가한 데이터의 고유 id값을 꺼내오기 위해서는 실제 데이터베이스에 값을 불러올 자료가 필요하기 때문에 _document 속성이 존재한다.
하지만 doc()는 데이터베이스에 있는 필드를 삭제하기만 하면 되기때문에 아이디 값으로 조회에 delete만 해버리게 할 수 있는것으로 보인다.
나는 아래와 같이 데이터를 삭제하는 액션 함수를 정의 했다.
export function deleteCardFB(cardId) {
return async (dispatch) => {
const docRef = doc(db, 'card', cardId)
console.log('[doc]', docRef)
await deleteDoc(docRef)
dispatch(deleteCard(cardId))
}
}
export function modifyCardFB(modifiedCard) {
return async (dispatch, getState) => {
const docRef = doc(db, 'card', modifiedCard.id)
await updateDoc(docRef, modifiedCard.card)
const _cardList = getState().card.list
const index = _cardList.findIndex(card => card.id === modifiedCard.id)
const modifiedData = {
index,
...modifiedCard
}
dispatch(modifyCard(modifiedData))
}
}