Main Course 주특기 4강 - React

박경준·2021년 6월 28일
0

main course - react

목록 보기
4/6

주특기 4강!

  1. keyframes를 설치하고 애니메이션 효과를 넣어본다.
  2. 서버와 서버리스에 대해 이해한다.
  3. realtime database를 어렴풋 알아본다.
  4. firebase를 이용한 BaaS 환경을 설정하자.
  5. React-firebase 사용법을 익힌다.

버킷리스트 앱 꾸미기

keyframes

const boxFade = keyframes`
  0% {
    opacity: 1;
    top: 20px;

  }
  50% {
    opacity: 0;
    top: 400px;
  }
  100% {
    opacity: 1;
    top: 20px;
  }
`;

Firebase - 서버리스

리액트에 Firebase 연동하기

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 데이터를 redux 스토어에 넣기

우리가 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;
  }
}
  • load 관련
// App.js
...
// 잠깐!! loadBucketFB를 import해오는 거 잊지말기!
// load()를 componentDidMount에서 부르면 되겠죠? :)
...
const mapDispatchToProps = (dispatch) => ({
  load: () => {
    dispatch(loadBucketFB());
  }
});

...
componentDidMount () {
  this.props.load(); // 컴포넌트 최초 렌더링 시 db 데이터를 받아옴
}
...
  • create 관련
// App.js
...
// 잠깐!! addBucketFB를 import해오는 거 잊지말기!
const mapDispatchToProps = (dispatch) => ({
  load: () => {
    dispatch(loadBucketFB());
  },
  create: (new_item) => {
    console.log(new_item);
    dispatch(addBucketFB(new_item));
  }
});
...
  • update 관련
// 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>
...
  • delete 관련
// Detail.js
...
<button onClick={() => {
  //   dispatch(); <- 괄호안에는 액션 생성 함수가 들어가야겠죠?
  // 예를 들면 이렇게요.
  dispatch(deleteBucketFB(bucket_index));
  props.history.goBack();
}}>삭제하기</button>
...
profile
빠굥

0개의 댓글