- keyframes를 설치하고 애니메이션 효과를 넣어본다.
- 서버와 서버리스에 대해 이해한다.
- realtime database를 어렴풋 알아본다.
- firebase를 이용한 BaaS 환경을 설정하자.
- React-firebase 사용법을 익힌다.
const boxFade = keyframes`
0% {
opacity: 1;
top: 20px;
}
50% {
opacity: 0;
top: 400px;
}
100% {
opacity: 1;
top: 20px;
}
`;
yarn add firebase
//firebase.js
import firebase from "firebase/app";
import "firebase/firestore";
const firebaseConfig = {
// firebase 설정과 관련된 개인 정보
};
// firebaseConfig 정보로 firebase 시작
firebase.initializeApp(firebaseConfig);
// firebase의 firestore 인스턴스를 변수에 저장
const firestore = firebase.firestore();
// 필요한 곳에서 사용할 수 있도록 내보내기
export { firestore };
// App.js
import { firestore } from "./firebase";
데이터 불러와보기
// App.js
componentDidMount() {
const bucket = firestore.collection("buckets");
// 하나만 확인하기
bucket
.doc("bucket_item")
.get()
.then((doc) => {
// .exists를 써서 데이터가 있는 지 없는 지 확인!
if(doc.exists){
// 데이터를 콘솔에 찍어보자!
console.log(doc.data());
}
});
//전체 확인하기
bucket
.get()
.then((docs) => { // 데이터 처리(get, add, update...) 해준 뒤에 비동기적으로 처리해줄 동작 넣기.
let bucket_data = [];
docs.forEach((doc) => {
// 도큐먼트 객체를 확인해보자!
console.log(doc);
// 도큐먼트 데이터 가져오기
console.log(doc.data());
// 도큐먼트 id 가져오기
console.log(doc.id);
if (doc.exists) {
bucket_data = [...bucket_data, { id: doc.id, ...doc.data() }];
}
});
console.log(bucket_data);
}
데이터 다루기
bucket.add({ text: "수영 배우기", compeleted: false });
bucket.doc("bucket_item").update({ compeleted: false });
bucket.doc("bucket_item").delete();
const bucket = firestore.collection("buckets"); // buckets라는 컬렉션은 없는 상태
bucket.doc("bucket_item").set({ text: "수영 배우기", compeleted: false }); // set을 이용할때는 doc 아이디를 결정해줘야 함
// bucket.add({ text: "수영 배우기", compeleted: false }); add를 이용할때는 doc 아이디 적어줄 필요 없음.
우리가 firestore에서 데이터를 가져올 때 비동기 통신을 한다고 했죠!
리덕스에서 비동기 통신을 할 때 필요한 미들웨어라는 친구 먼저 설치할 거예요.
미들웨어란...
리덕스 데이터를 수정할 때 [액션이 디스패치 되고 → 리듀서에서 처리] 하던 과정 기억하시죠?
미들웨어는 이 과정 사이에 미리 사전 작업을 할 수 있도록 하는 중간 다리 같은 거예요!
즉! [액션이 일어나고 → 미들웨어가 할 일 하기 → 리듀서에서 처리] 이 순서로 처리하게 됩니다!
yarn add redux-thunk
미들웨어 추가하기
// configStore.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import bucket from "./modules/bucket";
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
const middlewares = [thunk];
const enhancer = applyMiddleware(...middlewares);
const rootReducer = combineReducers({ bucket });
const store = createStore(rootReducer, enhancer);
export default store;
모듈과 App.js 수정해주기
파이어베이스랑 통신 → 필요하다면 리듀서 고치고 → 불러다 쓰기
// bucket.js
import {firestore} from "../../firebase"
const bucket_db = firestore.collection("bucket")
// Actions
const LOAD = "bucket/LOAD";
const CREATE = "bucket/CREATE";
const DELETE = "bucket/DELETE";
const UPDATE = "bucket/UPDATE";
const initialState = {
list: [
{text: '영화관 가기', completed: false},
{text: '매일 책읽기', completed: false},
{text: '수영 배우기', completed: false},
],
// list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};
// Action Creators
export const loadBucket = (bucket) => {
return { type: LOAD, bucket };
};
export const createBucket = (bucket) => {
return { type: CREATE, bucket };
};
export const deleteBucket = (bucket) => {
return { type: DELETE, bucket };
};
export const updateBucket = (bucket) => {
return { type: UPDATE, bucket };
};
export const loadBucketFB = () => {
return function (dispatch) {
bucket_db.get().then((docs) => {
let bucket_data = [];
docs.forEach((doc) => {
if (doc.exists) {
bucket_data = [...bucket_data, {id: doc.id, ...doc.data()}]
}
})
console.log(bucket_data);
dispatch(loadBucket(bucket_data));
})
}
}
export const addBucketFB = (bucket) => {
return function (dispatch) {
let bucket_data = {text: bucket, completed: false}
bucket_db.add(bucket_data).then(docRef => {
bucket_data = {...bucket_data, id: docRef.id};
dispatch(createBucket(bucket_data))
})
}
}
export const updateBucketFB = (bucket) => {
return function (dispatch, getState) {
// getState -> 이 모듈의 state를 가져와줌. 리덕스에 있는 state 데이터도 firebase load 함수를 통해 가져올수 있다는 뜻
const _bucket_data = getState().bucket.list[bucket];
let bucket_data = {..._bucket_data, completed: true};
if (!bucket_data.id) {
return;
}
bucket_db.doc(bucket_data.id).update(bucket_data).then(docRef => {
dispatch(updateBucket(bucket))
}).catch(error => { // 오류가 났다면 catch로 받아와서 에러를 보여준다.
console.log(error);
});
}
}
export const deleteBucketFB = (bucket) => {
return function (dispatch, getState) {
const _bucket_data = getState().bucket.list[bucket];
if (!_bucket_data.id) {
return;
}
bucket_db.doc(_bucket_data.id).delete().then(res => {
dispatch(deleteBucket(bucket));
}).catch(error => { // 오류가 났다면 catch로 받아와서 에러를 보여준다.
console.log(error);
});
}
}
// Reducer
export default function reducer(state = initialState, action) {
switch (action.type) {
// do reducer stuff
case "bucket/LOAD": {
if (action.bucket.length) {
return {...state, list: action.bucket};
}
return state;
}
case "bucket/CREATE":
const new_bucket_list = [...state.list, action.bucket];
return {...state, list: new_bucket_list};
case "bucket/DELETE":
const bucket_list = state.list.filter((l, idx) => {
return idx !== action.bucket
});
return {...state, list: bucket_list};
case "bucket/UPDATE": {
const bucket_list = state.list.map((l, idx) => {
if (idx === action.bucket){
return {...l, completed: true}
} else {
return l;
}
})
return {...state, list: bucket_list};
}
default:
return state;
}
}
// App.js
...
// 잠깐!! loadBucketFB를 import해오는 거 잊지말기!
// load()를 componentDidMount에서 부르면 되겠죠? :)
...
const mapDispatchToProps = (dispatch) => ({
load: () => {
dispatch(loadBucketFB());
}
});
...
componentDidMount () {
this.props.load(); // 컴포넌트 최초 렌더링 시 db 데이터를 받아옴
}
...
// App.js
...
// 잠깐!! addBucketFB를 import해오는 거 잊지말기!
const mapDispatchToProps = (dispatch) => ({
load: () => {
dispatch(loadBucketFB());
},
create: (new_item) => {
console.log(new_item);
dispatch(addBucketFB(new_item));
}
});
...
// Detail.js
...
import {updateBucketFB} from "./redux/modules/bucket";
import { useDispatch, useSelector } from "react-redux";
const Detail = (props) => {
const dispatch = useDispatch();
const bucket_list = useSelector((state) => state.bucket.list);
<button onClick={() => {
dispatch(updateBucketFB(bucket_index));
props.history.goBack();
}}>완료하기</button>
...
// Detail.js
...
<button onClick={() => {
// dispatch(); <- 괄호안에는 액션 생성 함수가 들어가야겠죠?
// 예를 들면 이렇게요.
dispatch(deleteBucketFB(bucket_index));
props.history.goBack();
}}>삭제하기</button>
...