필요한 라이브러리
reduxjs/toolkit
: Redux를 사용할 때 store 생성, 액션 타입 지정, 리듀서 함수 등 작성해야하는 코드양을 줄이고 불필요한 보일러플레이팅을 지양하기 위해서 사용.
react-redux
: useSelector
, useCallback
로 각 컴포넌트에서 store 접근하기 위해서 사용
next-redux-wrapper
: Next.js에서 각 페이지 컴포넌트의 getInitialProps
, getServerSideProps
, getStaticProps
로 store에 접근하기 위해서 사용
redux-logger
: 브라우저 환경에서 store 변화를 직관적으로 보기 위해서 사용
$ yarn add @reduxjs/toolkit react-redux next-redux-wrapper
$ yarn add redux-logger -D
/* src/store/index.ts */
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import logger from 'redux-logger';
import reducer from './modules';
const makeStore = (context) =>
configureStore({
reducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
devTools: process.env.NODE_ENV !== 'production',
});
export const wrapper = createWrapper(makeStore, {
debug: process.env.NODE_ENV !== 'production',
});
configureStore()
: createStore
와 동일한 기능에 DevTools/Middleware
추가, 생성한 reducer까지 하나의 인자로 받음
/* src/store/modules/admin.ts */
import { createSlice } from '@reduxjs/toolkit';
import { IAdminInfo } from '../../../global-interfaces';
const initialState = {
info: {
authorization: '',
nick: '',
roleCd: '',
statCd: '',
userEmail: '',
userId: '',
userLevel: '',
},
};
const adminSlice = createSlice({
name: 'admin',
initialState,
reducers: {
logout: (state) => {
state.info = undefined;
},
login: (state, action) => {
state.info = action.payload;
},
},
});
export const { login, logout } = adminSlice.actions;
export default adminSlice.reducer;
createSlice()
: reducer이름과 함수가 포함된 초기 상태와 lookup테이블을 받아 액션 생성자 함수, 액션 유형 문자열 및 리듀서 함수를 자동으로 생성
/* src/store/modules/index.ts*/
import { combineReducers } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
import admin from './admin';
const reducer = (state, action) => {
if (action.type === HYDRATE) {
return {
...state,
...action.payload,
};
}
return combineReducers({
admin,
})(state, action);
};
export default reducer;
hydrate
: Next.js 서버에서 생성한 redux store
와 client에서 생성한 redux store
가 다르기 때문에, 이렇게 서버에서 생성한 store의 상태를 (HYDRATE라는 액션으로) client에 합쳐준다.
/* src/pages/_app.tsx */
import '../styles/globals.scss';
import Head from 'next/head';
import Router from 'next/router';
import React from 'react';
import { wrapper } from "../store";
function MyApp({ Component, pageProps }) {
const Layout = Component.layout || (({ children }) => <>{children}</>);
return (
<React.Fragment>
<Head>
<meta name='viewport'
content='width=device-width, initial-scale=1, shrink-to-fit=no'
/>
<title>Admin Page</title>
</Head>
<Layout> <Component {...pageProps} /></Layout>
</React.Fragment >)
;
}
export default wrapper.withRedux(MyApp);