-
리액트 프로젝트를 진행하며 상태관리시에 프로젝트 규모가 작으면 Context API를 이용하고
그 외에 경우에는 Redux 또는 Mobx를 많이 사용하는것 같습니다.
리덕스는 액션 타입, 액션 실행함수, 리듀서 이렇게 만들어주고 관리하는 특성이 있어서 코드량이 많아진다는 단점이 있습니다.
그렇지만 Redux DevTools 등을 이용하여 상태변화를 하나하나 다 체크하고 확인할 수 있기 때문에 안정성은 높다는 평가가 많아요.
저는 리덕스의 Redux Toolkit이 리덕스의 코드를 확 줄여줘서 정말 편하다고 하길래 찾아봤습니다.
직접 테스트를 조금 해보니까 좀 많이 편한거 같아서 앞으로 자주 이용할 생각입니다.
리덕스 등 다른 필수 패키지가 다 설치되어있다는 가정하에 리덕스 툴킷을 설치해줍니다.
yarn add @reduxjs/toolkit // Redux Toolkit 설치
저는 리덕스 관련 파일들을 store 디렉토리를 만들어 관리합니다.
store 디렉토리입니다. (해당 프로젝트는 next.js 와 타입스크립트를 사용하며 진행하고 있습니다.)
store 디렉토리와 화면을 그려주는 components 디렉토리.
그 컴포넌트들을 리덕스와 연결하며 감싸주는 컴포넌트들을 보관하는 container 디렉토리.
이런식으로 관리하는 방식에서 redux-toolkit을 사용하면서 감싸주는 컴포넌트들의 디렉토리인 container를 아예 없애버렸습니다.
redux-toolkit에서는 reducer들마다 컴포넌트에 사용될 커스텀 훅(Custom Hook)들을 따로 만들어서 container 디렉토리 없이 사용했습니다.
테스트 코드는 간단하게 로그인과 로그아웃만 isLoggedIn과 userData로 관리해 주었습니다.
// store/modules/user.ts import { createSlice, PayloadAction } from '@reduxjs/toolkit'; // 초기 상태 타입 export type UserState = { isLoggedIn: boolean; userData: any; }; // 액션 Payload 타입 export type LoginPayload = { userId: string; password: string; }; // 초기 상태 const initialState: UserState = { isLoggedIn: false, userData: null, }; // 리듀서 슬라이스 const userSlice = createSlice({ name: 'user', initialState, reducers: { loginAction(state: UserState, action: PayloadAction<LoginPayload>) { state.isLoggedIn = true; state.userData = action.payload; }, logoutAction(state: UserState) { state.isLoggedIn = false; state.userData = null; }, }, }); // 리듀서 & 액션 리턴 const { reducer, actions } = userSlice; export const { loginAction, logoutAction } = actions; export default reducer;
createSlice로 기존의 리덕스에서 액션 타입, 액션 생성함수, 리듀서를 다 따로 만들어주던걸 한 번에 만들어줄 수 있습니다.
userSlice를 저런식으로 만들어주면 userSlice.reducer, userSlice.actions 이런식으로 리듀서와 액션타입을 사용할 수 있습니다.
userData의 타입은 테스트 코드에서는 아직 정해진게 없기 때문에 any로 해주었습니다.
이제 내보내준 리듀서와 액션타입들을 container가 따로 없으니 userHooks라는 Custom Hook을 만들어줍니다.
// store/modules/userHook.ts import { useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { RootState } from '.'; import { loginAction, LoginPayload, logoutAction } from './user'; // 커스텀 훅 export default function useUser() { const { isLoggedIn } = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const login = useCallback((data: any) => { dispatch(loginAction(data)); }, []); const logout = useCallback(() => { dispatch(logoutAction()); }, []); return { isLoggedIn, login, logout }; }
커스텀 훅을 만들어줬습니다.
useSelector, useDispatch를 따로 userHook 파일 안에 한꺼번에 만들어주고 login, logout 함수도 만들어줬습니다.
이제 내보내서 적용시키는 단계입니다.
// store/modules/index.ts import { combineReducers } from 'redux'; import user from './user'; // 루트 리듀서 const rootReducer = combineReducers({ user }); export default rootReducer; // 루트 리듀서의 반환값를 유추해줍니다 // 추후 이 타입을 컨테이너 컴포넌트에서 불러와서 사용해야 하므로 내보내줍니다. export type RootState = ReturnType<typeof rootReducer>;
index.ts 입니다. 이곳에서 combineReducers 안에 만들어준 user를 넣어줍니다.
rootReducer를 내보내주고 RootState 타입은 ReturnType을 이용하여 rootReducer의 state타입을 넣어줍니다.
이렇게 만들어주면 추후에 combineReducers에 다른 리듀서를 추가만 해주시면 알아서 RootState의 타입에도 추가됩니다.
// store/configureStore.ts import { configureStore } from '@reduxjs/toolkit'; import { createWrapper } from 'next-redux-wrapper'; import rootReducer from 'store/modules'; const store = () => { const store = configureStore({ reducer: rootReducer }); return store; }; const wrapper = createWrapper(store, { // 이 부분이 true면 디버그때 자세한 설명이 나옵니다. (개발할때는 true로) debug: process.env.NODE_ENV === 'development', }); export default wrapper;
configureStore 파일에서는 store를 cofigureStore()를 이용하여 reducer를 불러와줬는데
"기존의 createStore랑 뭐가다르냐?" 하실 수 있습니다.
configureStore는 toolkit에서 리덕스 createStore를 베이스로하여 새롭게 만들어준 기능입니다.
createStore를 사용하면 devTools를 이용할때는 composeWithDevTools를 넣어줘야하고,
미들웨어를 사용할때도 applyMiddleware도 넣어줘야 합니다.
configureStore를 이용한다면
const store = () => { const store = configureStore({ reducer: rootReducer, middleware: [...getDefaultMiddleware()] }); return store; };
이렇게 간단하게 리듀서와 미들웨어를 쉽게 넣어줄 수 있습니다.
cofigureStore는 Redux DevTools와 리덕스 미들웨어를 포함하고 있어서 devtools는 따로 넣어주지 않아도 알아서 들어가고
미들웨어는 따로 무언가를 불러올 필요없이 저렇게 뒤에 추가만 해주시면 됩니다.
export default wrapper.withRedux(App);
_app.tsx입니다. 저는 next.js를 사용하고 있어서 이렇게 적용시켜 줬습니다.
(일반 리액트 프로젝트라면 index.tsx 파일에서 store를 불러와서 Provider에 App을 감싸고 store 프롭스에 store를 넣어주었을 것입니다.)
const { isLoggedIn, login, logout } = useUser();
이제 사용할 컴포넌트에서 이런식으로 내보내준 isLoggedIn과 login 함수를 가져와서 사용하면 됩니다.
'코딩 기록 > 리액트' 카테고리의 다른 글
리액트 리덕스 툴킷 - 리덕스 사가 (React + Redux Toolkit + Redux Saga + TypeScript) - with Next.js (1) 2021.03.08 리액트 회원가입 폼 만들기 (React Hook Form + Antd + yup) (0) 2021.03.04 (React) 리액트 아이콘(react-icons) 사용하기 (0) 2021.02.20 리액트 미니 안경 쇼핑몰 - Glasses Shop App (React, Firebase) (2) 2021.01.21 리액트 투두리스트 앱 - TODO List App (React.js) (0) 2020.12.20 댓글