e도서관/Redux

Redux 시작하기

Heoky 2022. 1. 9. 01:44

react에 리덕스를 도입하기 위해 node module package를 install

$npm install react-redux

추후 브라우저의 확장프로그램인 redux devtool를 이용하기 위해 install (상태 추적에 용이)

$npm install redux-devtools-extension

next.js에 redux를 사용하기 위해서 install

$npm install next-redux-wrapper

next.js에서 redux를 사용하기 위해 해야될 일이 있다. 


1. front/store/configureStore.js 생성

// configureStore.js
import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, compose, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

import reducer from '../reducers';

// 1. store를 생성하는 함수를 정의
const configureStore = () => {
  const middlewares = []; // 미들웨어들을 넣으면 된다.
  const enhancer =
    process.env.NODE_ENV === 'production'
      ? compose(applyMiddleware(...middlewares))
      : composeWithDevTools(applyMiddleware(...middlewares));
  const store = createStore(reducer, enhancer);
  return store;
};

// 2. next-redux-wrapper에서 제공하는 createWrapper정의
export const wrapper = createWrapper(configureStore, { debug: true }); // process.env.NODE_ENV === 'development',

store를 생성해 reducer를 실행해준다. enhancer는 배포모드와 개발모드 일때 실행할 작업을 나눈 것

next-redux-wrapper는 유저가 페이지를 요청할때마다 리덕스 스토어를 생성해야 하기 때문에 configureStore함수를 정의해서
넘기는 것이다.


2. reducer 만들기

front/reducer/index.js 생성

import { combineReducers } from 'redux'; // 여러 리듀서들을 하나로 합쳐준다.

import user from './user';

const rootReducer = combineReducers({
  user, // 여기에 다른 리듀서들을 더 적으면 된다!
});

export default rootReducer; // _app.js에서 reducer로 사용된다!

combineReducer를 통해 분리한 component를 합쳐서 내보내준다. (front/reducer/기능별 컴포넌트 생성, 예시: user.js)


3. reducer 구현하기

// 초기 state를 설정
export const initialState = {
  isLogedin: false,
};

// action을 정의
export const LOGIN_REQUEST = 'LOGIN_REQUEST';

// action 함수 정의
export const loginAction = (data) => {
  console.log('loginAction data: ', data);
  return {
    type: LOGIN_REQUEST,
    data,
  };
};

// reducer 구현 (일어날 행위를 구현)
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case LOGIN_REQUEST:
      return {
        ...state, // 상태를 직접 변경 X, 불변성을 지키기 위함 (얕은 복사)
        isLogedin: true,
        user: action.data,
      };
    default:
      return state;
  }
};

export default reducer;

4. pages/_app.js 생성

import React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';

import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'

import { wrapper } from '../store/configureStore';

const KStyle = ({ Component }) => ( // 각 페이지의 component
  <>
    <Head>
      <meta charSet="utf-8" />
      <title>K-Style</title>
    </Head>
    <Component />
  </>
);

KStyle.propTypes = {
  Component: PropTypes.func.isRequired,
};

export default wrapper.withRedux(KStyle);

js 실행 시 _app.js가 먼저 실행될 것이고 wrapper.withRedux()를 통해 감싸준 컴포넌트가 리덕스 스토어에 접근이 가능해진다.
주의: _app.js에서 custom getinitialProps 정의 금지. 확장 순간 next.js의 페이지 최적화 기능을 무효화 시킨다.


5. Next.js에서 생성한 redux store와 Client에서 생성한 redux store는 다르기 때문에 이 둘을 합쳐야 한다.

reducers/index.js

import { combineReducers } from 'redux'; // 여러 리듀서들을 하나로 합쳐준다.
import { HYDRATE } from 'next-redux-wrapper';

import user from './user';

const rootReducer = combineReducers({ // HYDRATE 추가
  index: (state = {}, action) => {
    switch (action.type) {
      case HYDRATE:
        console.log('HYDRATE', action);
        return {
          ...state,
          ...action.payload,
        };
      default:
        return state;
    }
  },
  user, // 여기에 다른 리듀서들을 더 적으면 된다!
});

export default rootReducer; // _app.js에서 reducer로 사용된다!

Next.js에서 생성한 redux store와 Client에서 생성한 redux store는 다르기 때문에 서버에서 생성한 스토어의 상태를 HYDRATE라는 액션을 통해서 클라이언트에 합쳐주는 작업이 필요하다.

action.payload에는 서버에서 생성한 스토어의 상태가 담겨있다. 이 둘을 합쳐 새로운 클라이언트의 리덕스 스토어의 상태를 만든다.

만약에 순수 next.js만 사용하고 next-redux-wrapper를 사용하지 않는다면 우리는 getInitialProps등에서 리덕스 스토어에 접근할 수 없다. 그런 경우엔 axios나 fetch등의 api 라이브러리를 사용해야한다.