[항해99] React 기본 강의

니나노개발생활·2021년 6월 28일
0

🏃🏻‍♀️bootcamp

목록 보기
17/18
post-thumbnail

JSX

  • 리액트에서는 딱 하나의 html 파일만 존재한다.
    (public 아래에 있는 index.html)
  • 따라서 뷰를 그리기 위에서는 JSX문법을 사용하여 App.js처럼 React 요소를 만들고 DOM에 렌더링 시켜 그린다.
  • 자바스크립트 안에서 html 태그같은 마크업을 넣어 뷰 작업을 편하게 함!
const start_half = <div>
    <h1>안녕하세요!</h1>
    <p>시작이 반이다!</p>
  </div>;

JSX 규칙

  • 태그는 꼭 닫아줘야 한다.

    그럼 input처럼 닫힘 태그가 필요없는 것들은?
    이것도 마찬가지로 이렇게!

<input type='text/>
  • 무조건 한 개의 엘리먼트를 반환하기
    한 개 이상의 엘리먼트를 반환하면 에러가 난다.
    아래는 에러 예시!
return (
    <p>안녕하세요! 리액트 반입니다 :)</p>

    <div className="App">
      <input type='text'/>
    </div>
  );
  • JSX에서 javascript를 가져 오려면 중괄호 이용
const cat_name= 'fall';

return (
      <div>
        hello {cat_name}!
      </div>
    );

값을 가져올 때 뿐만 아니라, map, 삼항연산자 등 자바스크립트 문법을 JSX 안에 쓸 때도 {}를 이용할 수 있다.

<div className="App">
      <p>안녕하세요! 리액트 반입니다 :)</p>
      {/* JSX 내에서 코드 주석은 이렇게 씁니다 :) */}
      {/* 삼항 연산자를 사용했어요 */}
      <p>{number > 10 ? number+'은 10보다 크다': number+'은 10보다 작다'}</p>
    </div>
  • class > className
<div className="App">

클래스 외 다른 명 id같은 아이들은 그대로 사용!

  • 인라인으로 style 주기
<p style={{color: 'orange', fontSize: '20px'}}>orange</p>

중괄호가 두 개인 이유는?

  • 딕셔너리도 자바스크립트이기 때문!

Component

  • 레고의 블럭이라고 생각하면 이해가 쉬움!
<!DOCTYPE html>
<html lang="en">
  <head>
  </head>
  <body>
    <header> 
        ...
    </header>
    <div class="container">
        <div id="image-banner">
            ...
        </div>
        <div id="contents-1">
            ...
        </div>
    </div>
    <footer>
        ...
    </footer>
  </body>
</html>

이런 html 파일이 있을 때 이 코드들을 조각내보면

  1. header
  2. container
    1. imagebanner
    2. contents1
  3. footer

이렇게 나눌 수 있다.

즉, 이 웹 사이트는,
크게 header, container, footer 세 개의 컴포넌트가 있고,
container 컴포넌트는,
imagebanner, contents1 컴포넌트로 이루어져 있는 것!

클래스형 컴포넌트

import React from 'react';
import logo from './logo.svg';
import './App.css';
// BucketList 컴포넌트를 import 해옵니다.
// import [컴포넌트 명] from [컴포넌트가 있는 파일경로];
import BucketList from './BucketList';

// 클래스형 컴포넌트는 이렇게 생겼습니다!
class App extends React.Component {

  constructor(props){
    super(props);
    // App 컴포넌트의 state를 정의해줍니다.
    this.state = {
      list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
    };
  }

  // 랜더 함수 안에 리액트 엘리먼트를 넣어줍니다!
  render() {
      return (
      <div className="App">
        <h1>내 버킷리스트</h1>
        {/* 컴포넌트를 넣어줍니다. */}
        <BucketList/>
      </div>
    );
  }
}

export default App;

함수형 컴포넌트

// 리액트 패키지를 불러옵니다.
import React from 'react'; 

// 함수형 컴포넌트는 이렇게 쓸 수도 있고
// function Bucketlist(props){
//     return (
//         <div>버킷 리스트</div>
//     );
// }

// 이렇게 쓸 수도 있어요. =>가 들어간 함수를 화살표 함수라고 불러요.
// 저희는 앞으로 화살표 함수를 사용할거예요.
// 앗 () 안에 props! 부모 컴포넌트에게 받아온 데이터입니다.
// js 함수가 값을 받아오는 것과 똑같이 받아오네요.
const BucketList = (props) => {

    // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해줍니다.
    return (
        <div>
            버킷 리스트
        </div>
    );
}

// 우리가 만든 함수형 컴포넌트를 export 해줍니다.
// export 해주면 다른 컴포넌트에서 BucketList 컴포넌트를 불러다 쓸 수 있어요.
export default BucketList;

데이터 관리

State

  • 컴포넌트가 가지고 있는 데이터
  • 한 컴포넌트에서만 사용하는 정보를 주로 넣어놓고 생성, 수정하는 데이터
  • 생성도 수정도 오직 해당 컴포넌트 내에서만 이루어짐!


    이런 헤더 컴포넌트가 있을 때 이 안에 들어가있는 (로고 이미지 경로, 메뉴 이름 - 온라인, 오프라인, 기업교육, 내 강의실)

Props

  • 컴포넌트가 부모 컴포넌트로부터 받아온 데이터
  • 부모로부터 받은 데이터기 때문에 수정이 불가함!
<container>
	<imagebanner/>
	<contents1/>
</container>
  • 컴포넌트만 컴포넌트한테 필요한 이미지 경로를 가지고 있다고 가정(state로 가지고 있음)
    가 가지고 있는 이미지 경로를 에게 전달해주면, 이 이미지 경로가 컴포넌트의 props
  • App 컴포넌트에서 state를 넘겨주자
    컴포넌트에서 props를 넘겨줄 때
{/* <컴포넌트 명 [props 명]={넘겨줄 것(리스트, 문자열, 숫자, ...)}/> */}
<BucketList list={this.state.list}/>
  • 부모 컴포넌트가 보낸 props를 받아올 때
// 앗 () 안에 props! 부모 컴포넌트에게 받아온 데이터입니다.
// js 함수가 값을 받아오는 것과 똑같이 받아오네요.
const BucketList = ({list}) => {
    
    // Quiz 1: my_list에 ['a', 'b', 'c'] 대신 부모 컴포넌트가 넘겨준 값을 넣으려면 어떻게 해야할까요?
    const my_lists = ['a', 'b', 'c'];

    // 컴포넌트가 뿌려줄 ui 요소(리엑트 엘리먼트라고 불러요.)를 반환해줍니다.
    return (
        <div>
            {
                // js의 내장 함수 중 하나인 map입니다. 리스트의 갯수만큼 => 오른쪽 구문을 반복해요. 
                // 자세한 사용법은 아래 링크를 확인해주세요.
                // https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map
                my_lists.map((list, index) => {
                    // 콘솔을 확인해봅시다 :)
                    console.log(list);
                    return (<div key={index}>{list}</div>);
                })
            }
        </div>
    );
}

⭐️ 리액트 코딩 룰 1:
폴더는 소문자로 시작하는 카멜케이스를 사용
JS파일, 컴포넌트 이름은 대문자로 시작하는 카멜케이스를 사용

라이프 사이클

가상돔

☝🏻 DOM?

  • html 단위 하나하나를 객체로 생각하는 모델
  • 트리 구조
  • DOM 트리 중 하나가 수정될 때마다 모든 DOM에서 찾고 수정하고 한다면 필요없는 연산이 너무 많이 일어나서 등장한게 가상돔!
  • 메모리 상에서 돌아가는 가짜 DOM
  • 기존 DOM과 어떤 행동 후 새로 그린 DOM(가상 돔에 올라갔다고 표현) 피교해서 정말 바뀐 부분만 업데이트한다.
  • 처음 페이지에 진입했을 때, 데이터가 변했을 때

라이프 사이클 (=컴포넌트 생명주기)

  • 컴포넌트는 생성되고, 수정(업데이트)되고, 사라진다.

☝🏻

  • 생성 : 처음 컴포넌트를 불러오는 단계
  • 수정(업데이트) : 사용자의 행동(클릭, 입력)으로 데이터가 바뀌거나 부모 컴포넌트가 렌더링할 때
    -> props, state가 바뀔 때
    -> 부모 컴포넌트가 업데이트 되었을 때(=리렌더링)
    -> 강제로 업데이트 했을 경우(=forceUpdate())
  • 제거 : 페이지를 이동하거나 사용자의 행동(삭제 버튼)으로 컴포넌트가 화면에서 사라지는 단계

알아두면 좋은 라이프 사이클 함수

☝🏻 라이프 사이클 함수는 클래스형 컴포넌트에서만 사용할 수 있다.
리액트 공식 매뉴얼에서 함수형 컴포넌트를 더 권장하기 때문에 라이프 사이클을 아는 건 중요하지만 함수형 컴포넌트를 더 많이 씀!
(리액트 16.8버전부터 등장한 React Hooks로 라이프 사이클 함수 대체 가능

// 클래스형 컴포넌트
class LifecycleEx extends React.Component {

// 생성자 함수
  constructor(props) {
    super(props);
    
    this.state = {
      cat_name: '가을',
    };
  }

  changeCatName = () => {
    // componentDidUpdate()를 보기 위해 쓰는 것
      this.setState({cat_name: '마요네즈'});
  }

  componentDidMount(){
  }

  componentDidUpdate(prevProps, prevState){
  }

  componentWillUnmount(){
  }

  render() {
    return (
      <div>
        <h1>제 고양이 이름은 {this.state.cat_name}입니다.</h1>
        <button onClick={this.changeCatName}>고양이 이름 바꾸기</button>
      </div>
    );
  }
}
  • constuctor() : 생성자 함수(컴포넌트 생성 시 가장 처음 호출)
  • render() : 컴포넌트의 모양을 정의
  • componentDidMount()
    : 마운트가 완료됨
    -> 첫번째 렌더링을 마친 후 딱 한 번 실행
    (리렌더링 하기 전에는 실행되지 않으며 이 안에서 ajax, 이벤트 등록, 함수 호출, DOM 관련 처리 등이 이루어짐)

☝🏻 마운트?
: 컴포넌트가 화면에 나타나는 것

  • componentDidUpdate(prevProps, prevState, snapshot)
    : 리렌더링을 완료한 후 실행

☝🏻 prevProps, prevState는 각각 업데이트 되기 전 props, state!
-> 이전 데이터와 비교할 일이 있을 때 사용

  • componentWillUnmount()
    : 컴포넌트가 DOM에서 제거 될 때 실행하는 함수
    만약 우리가 스크롤 위치를 추적 중이거나 어떤 이벤트 리스너를 등록했을 때 여기서 꼭꼭 해제를 해줘야함!

Ref

  • 만약 어떤 인풋박스에서 텍스트를 가지고 오고 싶을 때 리액트 요소에서 가지고 오는 방법

함수형

const input_text = React.useRef(null);
...
//현재 입력된 값(뷰에서)
input_text.current.value

State 관리 - 함수형

☝🏻순서

1) 컴포넌트 만들기
2) App에서 import
3) useState()로 state 등록
4) 뷰 만들기
5) 함수 만들기
6) 연결
7) 완성!

useState()로 state 등록

// count에는 state 값이, setCount는 count라는 state 값을 수정하는 함수가 될거예요.
  // useState(초기값): () 안에 초기값을 넣어줍니다.
  const [count, setCount] = React.useState(3);

함수 만들기

const addNemo = () => {
    // setCount를 통해 count에 저장된 값을 + 1 해줍니다.
    setCount(count + 1);
  };

const removeNemo = () => {
    // setCount를 통해 count에 저장된 값을 - 1 해줍니다.
    // 삼항 연산자
    setCount(count > 0 ? count - 1 : 0);
  };

연결

<div>
{/* 함수를 호출합니다. */}
<button onClick={addNemo}>하나 추가</button>
<button onClick={removeNemo}>하나 빼기</button>
</div>

Event Listener

  • 이벤트 리스너는 사용자가 어떤 행동을 하는지 아닌 지 지켜보다 알려주는 것!
  • 마우스 클릭, 터치, 마우스 오버, 키보드 누름 등
    더 많은 이벤트 알아보기

라우팅

SPA(Single Page Application)

: html이 1개 뿐인 어플리케이션 > 딱 한번만 정적 자원을 받아온다.
-> 페이지를 이동할 때마다 서버에서 주는 html로 화면을 바꾸다보면 상태 유지가 어렵고, 바뀌지 않은 부분까지 새로 불러와 사용성에서 매우 비효율적이다.
-> SPA는 처음에 모든 컴포넌트를 받아와 사용자가 안들어가 볼 페이지까지 전부 가지고 오기 때문에 아주아주 많은 컴포넌트가 있다면 첫 로딩 속도가 조금 느리다 (스피너주기!)

라우팅

: 라우팅을 통해 브라우저 주소창대로 다른 페이지를 보여줌
-> 라이브러리 사용
react-router-dom 공식문서

사용

  • index.js에 BrowserRouter 적용
import { BrowserRouter } from "react-router-dom";
...
// BrowserRouter은 웹 브라우저가 가지고 있는 주소 관련 정보를 props로 넘겨주기 때문에 현재 내가 어느 주소를 보고 있는지 쉽게 알려준다.
<BrowserRouter>
    <App />
</BrowserRouter>,

넘겨줄 props가 없을 때

<Route path="주소[/home 처럼 /와 주소를 적어요]" component={[보여줄 컴포넌트]}/>

넘겨줄 props가 있을 때

<Route path="주소[/home 처럼 /와 주소를 적어요]" render={(props) => (<BucketList list={this.state.list} />)} />

☝🏻 exact : '/'기호가 '/주소1' '/주소2'에 포함되어있어 다른 주소를 넘어가도 기본 '/'페이지가 뜬다. 이 때 exact를 메인 홈페이지에 적어주면 해결!

파라미터 주는 방법

<Route path="/cat/:cat_name" component={Cat}/>

링크로 이동시키기

: a태그와 비슷한 역할로 페이지 전환을 도와준다.

<Link to="주소">[텍스트]</Link>

history

//해당 주소로 이동
props.history.push('/주소');
//뒤로가기
props.history.goBack();

잘못된 주소 처리하기

1) NotFound.js파일 만들고 빈 컴포넌트
2) App.js에서 불러오기
3) 라우팅 임포트에 Switch추가
4) Route에 주소없이 연결!

...
	<Switch>
            <Route
              path="/"
              exact
              render={(props) => (
                <BucketList
                  list={this.state.list}
                  history={this.props.history}
                />
              )}
            />
            <Route path="/detail" component={Detail} />
            <Route component={NotFound} />
          </Switch>
...

리덕스

: 상태관리 라이브러리
리덕스 공식문서

  • 데이터를 한 곳에 몰아넣고, 여기저기에서 꺼내볼 수 있게 해준다.

기본 용어

State

: 저장하고 있는 상태값(=데이터)로 딕셔너리 형태({[key]:value})로 보관

Action

: 상태에 변화가 필요할 때(=데이터를 변경) 발생

// 액션은 객체, type은 이름- 임의의 문자열
{type: 'CHANGE_STATE', data: {...}}

ActionCreator

: 액션 생성 함수

//이름 그대로 함수
const changeState = (new_data) => {
// 액션을 리턴
	return {
		type: 'CHANGE_STATE',
		data: new_data
	}
}

Reducer

: 리덕스에 저장된 상태(=데이터)를 변경하는 함수

☝🏻 액션 생성 함수를 부른다 > 액션을 만든다 > 리듀서가 현재 상태(데이터)와 액션 객체를 받는다 > 새로운 데이터를 만든다 > 리턴한다

// 임의로 정한 기본 상태값(나중에 firebase에서 데이터를 불러오기 전까지 요게 표출됨)
const initialState = {
	name: '가을'
}

function reducer(state = initialState, action) {
	switch(action.type){
		// action의 타입마다 케이스문을 걸어주면, 
		// 액션에 따라서 새로운 값을 돌려줌!
		case CHANGE_STATE: 
			return {name: '가을이'};

		default: 
			return false;
	}	
}

Store

  • 리덕스를 적용하기 위해 만드는 것
  • 스토어에는 리듀서, 현재 애플리케이션 상태, 리덕스에서 값을 가져오고 액션을 호출하기 위한 내장 함수가 포함되어 있다.
  • json처럼 생김
  • 내장함수는 공식문서 참고

dispatch

: 액션을 발생시키는 스토어의 내장 함수

// 간단히 표현하자면 이런 식으로 우리가 발생시키고자 하는 액션을 파라미터로 넘겨서 사용합니다.
dispatch(action); 

특징

  • store는 1개만 쓴다 > 단일 스토어 규칙 (한 프로젝트에서 하나의 스토어)
  • stor의 state(데이터)는 오직 action으로만 변경할 수 있다.

☝🏻 데이터가 마구잡이로 변하지 않도록 불변성을 유지하기 위함
리덕스에 저장된 데이터 = 상태 = state >> 읽기 전용
이건 js의 문법 특징이랑도 비슷한 개념인듯?

  • 어떤 요청이 와도 리듀서는 같은 동작을 해야한다.

☝🏻 리듀서는 순수한 함수여야 한다.
? 순수한 함수?

  • 파라미터 외의 값에 의존하지 않아야 한다.
  • 이전 상태는 수정하지 않고 변화를 준 새로운 객체를 return한다.
  • 파라미터가 같으면 항상 같은 값을 반환한다.
  • 리듀서는 이전 상태와 액션을 파라미터로 받는다.

상태관리

필요한 이유

  • 자식 컴포넌트는 부모 컴포넌트의 state를 마음대로 조작할 수 없다.(데이터는 단방향으로 흐르기 때문에)
    리덕스는 여러 컴포넌트가 동일한 상태를 보고 있을 때 굉장히 유용하다.
    또, 데이터를 관리하는 로직을 컴포넌트에서 빼면 컴포넌트는 뷰만 관리할 수 있고 코드가 깔끔해지니 유지보수에도 좋다.

    1) 리덕스 Store를 컴포넌트에 연결한다.
    2) 컴포넌트에서 상태 변화가 필요할 때 Action을 부른다.
    3) Reducer를 통해서 새로운 상태 값을 만든다.
    4) 새 상태값을 Store에 저장한다.
    5) 컴포넌트는 새로운 상태값을 받아온다(props를 통한 것이기 때문에 새로 랜더링 됨)

덕스구조

  • 모양새대로 action, actionCreator, reducer를 분리해서 작성
    (액션은 액션끼리, 액션생성함수는 액션생성함수끼리, 리듀서는 리듀서끼리)
  • 모양새로 묶는 대신 기능으로 묶어서 작성
    덕스 구조 참고 깃허브

Action

const LOAD = 'bucket/LOAD';
const CREATE = 'bucket/CREATE';

initialState

: 초기 상태값(기본 값)

const initialState = {
  list: ["영화관 가기", "매일 책읽기", "수영 배우기"],
};

Action Creactor

export const loadBucket = (bucket) => {
    return { type: LOAD, bucket };
}

export const createBucket = (bucket) => {
    return {type: CREATE, bucket};
}

Reducer

  • load할 땐, 가지고 있던 기본값을 그대로 뿌려주면 되고 create할 땐, 새로 받아온 값을 가지고 있던 값에 더해서 리턴해주면 됨!
export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    // do reducer stuff
    case "bucket/LOAD":
      return state;

    case "bucket/CREATE":
      const new_bucket_list = [...state.list, action.bucket];
      return { list: new_bucket_list };

    default:
      return state;
  }
}

Store(configStore.js)

//configStore.js
import { createStore, combineReducers } from "redux";
import bucket from './modules/bucket';
import { createBrowserHistory } from "history";

// 브라우저 히스토리를 만들어줍니다.
export const history = createBrowserHistory();
// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ bucket });

// 스토어를 만듭니다.
const store = createStore(rootReducer);

export default store;

연결

Store 연결하기

//index.js
...
// 리덕스를 주입해줄 프로바이더를 불러옵니다!
import { Provider } from "react-redux";
// 연결할 스토어도 가지고 와요.
import store from "./redux/configStore";
ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById("root")
);

reportWebVitals();

함수형 컴포넌트에서 리덕스 데이터 사용하기

...
// redux hook을 불러옵니다.
import {useDispatch, useSelector} from 'react-redux';
...
// 버킷리스트를 리덕스 훅으로 가져오기
const bucket_list = useSelector(state => state.bucket.list);
...
//list를 나열한 map이 있다면 여기에 bucket_list로 적용
<ListStyle>
      {bucket_list.map((list, index) => {
        return (
          <ItemStyle
            className="list_item"
            key={index}
            onClick={() => {
              props.history.push("/detail");
            }}
          >
            {list}
          </ItemStyle>
        );
      })}
    </ListStyle>

☝🏻 상세 페이지를 확인하기 위해서 URL파라미터를 적용할 수 있다.

//App.js
<Route
  path="/detail/:index"
  render={(props) => <Detail match={props.match} history={props.history} />}
/>
//해당 js
<ItemStyle
   className="list_item"
   key={index}
   onClick={() => {
   // 배열의 몇번째 항목을 눌렀는 지, url 파라미터로 넘겨줍니다.
   props.history.push("/detail/"+index);
   }}>
   {list}
</ItemStyle>

액션 생성 함수 사용하기

...
// redux hook을 불러옵니다.
import { useDispatch, useSelector } from "react-redux";
// 내가 만든 액션 생성 함수를 불러옵니다.
import {deleteBucket} from "./redux/modules/bucket";

const Detail = (props) => {
    //dispatch > 액션 발생시키는 내장함수
    const dispatch = useDispatch();
...
<button onClick={() => {
        //   dispatch(); <- 괄호안에는 액션 생성 함수
        // 예를 들면 이렇게요.
        dispatch(deleteBucket(bucket_index));
        props.history.goBack();
      }}>삭제하기</button>
  

서버와 서버리스

웹의 동작방식

  • 웹은 요청과 응답으로 굴러간다.
    -> 클라이언트가 서버에게 요청, 서버가 클라이언트에게 응답

서버리스

  • 서버가 없다가 아닌 서버를 신경쓸 필요가 없다는 뜻!
  • 이미 누군가가 구축해둔 서버의 일부를 빌려서 쓸 수 있다.

Firestore

  • Firebase에 포함된 서비스 중 하나로 유연하고 확장 가능한 NoSQL 클라우드 데이터베이스
  • realtime-database를 제공하는 서비스
  • 실시간으로 데이터가 추가되고 삭제되는 것을 클라이언트에게 알려줌
  • react-firebase라는 패키지를 통해 편리하게 연결이 가능하다.

구조

  • Collection: 문서(다큐먼트)의 집합
  • Document: JSON 형식으로 데이터를 저장할 수 있음

리액트에 연동하기

☝🏻 firebase, redux-thunk(미들웨어) 패키지 설치

  • 미들웨어?
    : 리덕스 데이터를 수정할 때 액션이 디스패치 되고 > 리듀서에서 처리하는 과정 사이에 미리 사전 작업을 할 수 있도록 하는 중간 다리같은 역할
    즉, 액션이 일어나고 > 미들웨어가 할 일 하고 > 리듀서에서 처리
  • redux-thunk?
    : 객체 대신 함수를 생성하는 액션 생성함수를 작성할 수 있게 해준다.
    -> 왜 필요할까?
    : 리덕스는 기본적으로 액션 객체를 디스패치 한다. 즉, 함수를 생성하면 특정 액션이 발생하기 전에 조건을 주거나, 어떤 행동을 사전에 처리할 수 있다.

1) firebase.js 파일 만들기

//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 };

2) 필요한 곳에서 firestore 가져오기

import { firestore } from "./firebase";

3) 파이어 베이스랑 통신하는 함수 만들기

const bucket_db = firestore.collection("bucket");

// 파이어베이스랑 통신하는 부분
export const loadBucketFB = () => {
  return function (dispatch) {
    
    bucket_db.get().then((docs) => {
      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);
      // 이제 액션 생성 함수한테 우리가 가져온 데이터를 넘겨줘요! 그러면 끝!
      dispatch(loadBucket(bucket_data));
    });
  };
};

4) 필요하다면 리듀서 수정

case "bucket/LOAD": {
      if(action.bucket.length >0){
        return { list: action.bucket };
      }

      return state;
    }

5) 불러서 쓰기!

⭐️ configStore.js에 미들웨어 추가하기

import { createStore, combineReducers, applyMiddleware, compose } 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;
profile
깃헙으로 이사중..

1개의 댓글

comment-user-thumbnail
2021년 6월 28일

세상에.

답글 달기