Redux 시작하기
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 라이브러리를 사용해야한다.