배민문방구 클론 리액트 프로젝트입니다.
firestore Database 를 이용하였고, Items의 객체는 위와 같은 모양입니다.
firebase에서 데이터를 가져오려면
import { getFirestore, query, collection, getDocs } from "firebase/firestore";
import { dbService } from "../firebase";
const q = query(collection(dbService, "items")); // collection 이름이 담겨있음
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => doc.data());
이런식으로 가져와야 합니다. 그런데 이걸 상태에 어떻게 저장할 수 있을까에 대한 고민을 많이 했습니다.
만약, useState라는 훅을 이용하면
let [items, setItems] = useState([]);
const fetchItem = ( ) => {
// 서버에서 items 배열을 가져오는 작업
const q = query(collection(dbService, "items"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => setItems([doc.data(), ...items]));
}
useEffect(()=>{
fetchItem();
},[])
이런 식으로, 상품을 볼 수 있는 페이지의 component 가 mount 되기 전 Item 을 가져오면 됩니다.
여기서도 고민인게, 서버와 통신하는 코드가 컴포넌트 안에 있는 것이 맞는가에 대한 문제입니다. 이건 따로 fetchItem() 을 다른 파일로 빼고 import() 하여 사용해주면 될 것 같습니다 .. !
비동기 처리도 너무 까다롭고 내가 원하는대로 dispatch 가 되지 않는다 ...
그 때 발견한 것이 redux-thunk 였다.
굳이 , firebase가 아니더라도 json 형식으로 데이터를 axios 로 받아온다거나.. 그런 비동기처리가 필요할 때 유용하게 사용할 것 같다.
redux-saga 도 존재하지만, 난 then과 catch 처리가 그렇게 불편하다고 생각되지 않고 리덕스 청크만으로도 충분할 것 같다고 판단했다.
npm install redux-thunk
npm intall react-redux-firebase
(firebase를 쓰지 않는다면.. 가벼이 pass해도 되는 작업이다.)
firebase.js 파일
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
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_ID,
appId: process.env.REACT_APP_API_ID,
};
const app = initializeApp(firebaseConfig);
const authService = getAuth();
const dbService = getFirestore();
export { app, authService, dbService };
auth 서비스를 이용하지 않을 것이라면,
import { getAuth } from "firebase/auth";
const authService = getAuth();
를 없애도 된다.
보안 상의 이유로 firebaseConfig 는 .env 파일에 따로 저장해뒀습니다. :)
자신 프로젝트의 firebaseConfig 는 안다는 전제하에 넘기겠습니다 ..... !
itemsAction.js
import { getFirestore, query, collection, getDocs } from "firebase/firestore";
//import { dbService } from "../firebase";
const FILTER_ITEMS = "FILTER_ITEMS";
const categoryFilterItemsData = (category) => async (dispatch) => {
try {
//const q = query(collection(dbService, "items"));
//const querySnapshot = await getDocs(q);
//let Item = [];
//querySnapshot.forEach((doc) => {
//});
//console.log(Item);
// <---------- axios로 받던 아무튼 비동기 처리 ------>
dispatch({ type: FILTER_ITEMS, payload: Item });
} catch (e) {
console.log(e);
}
};
export default categoryFilterItemsData;
dispatch({type:액션이름 , payload: 비동기처리로 받아온 데이터})
해주면 된다. 만약, 액션이 두개라면 ? 화살표 함수를 두 개 만들면 끝 !
ItemReducer.js
const initialState = [];
const ItemsReducer = (state = initialState, action) => {
switch (action.type) {
case "FILTER_ITEMS":
const items = action.payload;
console.log(items);
return items;
default:
return state;
}
};
export default ItemsReducer;
Items의 action 을 처리해줄 Reducer 파일을 생성해준다.
액션은 case 별로 구별해주고, default(기본 상태)는 state를 리턴해준다.
action.type 을 이용하여 action 의 타입을 구별해줍니다.
rootReducer.js
import { combineReducers } from "redux";
import ItemsReducer from "./ItemReducer";
const rootReducer = combineReducers({ ItemsReducer });
export default rootReducer;
store 에 저장될 rootReducer를 만들어준다.
Reducer는 2개 이상일 확률이 있으니 combineReducer로 묶어줬다.
만약, 한개라면 그냥
const rootReducer = ItemsReducer;
로 한 개만 보내줘도 무방하다
store.js
import { createStore, applyMiddleware } from "redux";
import rootReducer from "./rootReducer";
import thunk from "redux-thunk";
import { getFirebase } from "react-redux-firebase";
const middlewares = [thunk.withExtraArgument(getFirebase)];
const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
마지막으로 store를 묶어준다.
만약 firebase를 이용하지 않는다면
const middlewares = [thunk]; const store = createStore(rootReducer, applyMiddleware(middlewares));
를 사용하면 됩니다.
dispatch(categoryFilterItemsData(param.id));
이런식으로 사용해주면 됩니당!
dispatch({type: 모시기}) 를 해주던 건 categoryFilterItemData에서 해주니까 그런거겠쥬?
리덕스와 파이어베이스 연결하는 방법은 한국어로 된 포스팅이 많이 없어서
혼자서 영어 유투브들을 보며 엉ㅇ엉엉 울면서 찾은 방법입니다.
서버 통신하는 것이 상태관리와 구분되지 않아서 100 % 마음에 드는 방법은 아니긴 합니다
더 나은 방법이 있다면 알려주시면 감사하겠습니다..... 🙊