[Redux] Redux-Toolkit 시작하기!
Redux-thunk나 Redux-saga로 서버, 글로벌 상태를 관리하며 개발을 해오다,, 프로젝트 규모가 커질수록 점점 막대해지는 서버 상태로 방대해지는 반복 코드로 인하여 서버와 글로벌의 상태 분리가 필요하다고 느꼈고, 서버 상태관리를 위하여 react-query를 도입했다!
그리하여, 글로벌 상태 관리는 더이상 thunk나 saga가 적합하지 않고, 게다가 redux를 사용하면서 느꼈던 불편함들이 많았기 때문에..
- 리덕스의 복잡한 스토어 설정
- 리덕스를 유용하게 사용하기 위해서 추가되어야 하는 많은 패키지들
- 리덕스 사용을 위해 요구되는 다량의 상용구(boilerplate) 코드
이러한 문제점을 보완하여 Redux에서는 Redux Toolkit을 만들어냈다!
이번 포스팅은 Redux Toolkit 공식문서와 Vercel에서 오픈한 Next.js + Typescript + Redux-Toolkit 소스코드를 참고하여 작성하였다.
Next.js + Typescript 설치
$ npx create-next-app 프로젝트명 --typescript
redux 패키지 설치
$ npm i @reduxjs/toolkit
$ npm i react-redux
$ npm i next-redux-wrapper
$ npm i @types/react-redux
$ npm i redux-logger # 필요한 경우에 설치
modules/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit';
import type { AppState, AppThunk } from './store';
export interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
// action + reducer 정의
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
},
},
});
// useSelector에 넣을 함수 내보내기
export const selectCounter = (state: AppState) => state.counter;
// actions 내보내기
export const { increment, decrement } = counterSlice.actions;
// reducer 내보내기
export default counterSlice.reducer;
- createSlice: action과 reducer를 한 번에 정의한다.
- 비동기적인 리듀서 함수를 정의하고자 할 땐 객체의 프로퍼티로 extraReducers 객체를 추가한다.
modules/index.ts
import { combineReducers } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
import counterReducer from './counterSlice';
const reducer = (state, action) => {
// SSR 작업 수행 시 필요한 코드
if (action.type === HYDRATE) {
return {
...state,
...action.payload,
};
}
return combineReducers({
counter: counterReducer,
})(state, action);
};
export default reducer;
- modules 내에서 정의한 리덕스 모듈들을 합쳐주는 역할을 한다.
if(action.type === HYDRATE)
는 SSR 작업 수행 시 HYDRATE라는 액션을 통해서 서버의 스토어와 클라이언트의 스토어를 합쳐주는 작업을 수행한다.Next.js
는 처음 렌더링 시SSR
을 하게 된다. 따라서store
를 호출할 때마다redux store
를 새로 생성하게 된다. 이 때 생성하는redux store
와 이후CSR
시 생성하는redux store
가 다르기 때문에 이 둘을 합쳐주는 로직이 필요하다.- 서버에서 생성한 store의 상태를
HYDRATE
라는 액션을 통해 client에 합쳐주는 작업을 한다.action.payload
에는 서버에서 생성한store
의 상태가 담겨있어 이 둘을 합쳐 새로운 client의redux store
의 상태를 만들게 된다.
- combineReducers: 리듀서 모듈들을 결합하는 역할을 한다.
modules/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import logger from 'redux-logger';
import reducer from './index';
const isDev = process.env.NODE_ENV === 'development';
const makeStore = () =>
configureStore({
reducer,
middleware: getDefaultMiddleware =>
isDev ? getDefaultMiddleware().concat(logger) : getDefaultMiddleware(),
devTools: isDev,
});
export const wrapper = createWrapper(makeStore, {
debug: isDev,
});
const store = makeStore();
// type 정의
export type AppState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
- configureStore: store를 생성한다.
- redux-toolkit은 devTools 등의 미들웨어들을 기본적으로 제공한다. 따라서 사용하고 싶은 미들웨어가 있다면 추가로 정의할 수 있고, 위의 코드에선 logger를 추가했다.
- creatWrapper: wrapper를 생성하여 스토어를 바인딩 한다.
pages/_app.tsx
import type { AppProps } from 'next/app';
import { wrapper } from '../modules/store';
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default wrapper.withRedux(MyApp);
- wrapper의 withRedux HOC로 App 컴포넌트를 감싸준다.
- next-redux-wrapper가 없다면 SSR로 리덕스 스토어에 접근할 수 없다.
- 각 페이지에서 getStaticProps, getServerSideProps 등의 함수 내에서 스토어 접근이 가능해진다.
동작 화면
혹시 내용에 오류가 있다면 피드백 부탁드립니다. 🙇🏻♀️
[참고]
Author And Source
이 문제에 관하여([Redux] Redux-Toolkit 시작하기!), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jjhstoday/Redux-Redux-Toolkit-시작하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)