Redux-toolkit 은 Redux를 보다 편리하게 사용하기 위해 제공되어 지는 redux 개발 도구로 기존 Redux 사용 시 문제점 기존 리덕스는 스토어를 구성하는 것이 복잡하다는 단점과 리덕스를 유용하게 사용하려면 추가적으로 여러 패키지들이 필요하다는점 그리고 한 작업에 많은 코드를 작성해야한다는 문제점이 있었지만, 이런 문제점들을 보완하기위해 redux-toolkit이 출시 되었다고한다.
저번 Redux포스팅에서 사용했던 예제에서 Redux-Toolkit로 변경하는 식으로 포스팅을 이어나가겠습니다.
npm install @reduxjs/toolkit react-redux
// redux/reducer/counter.js
// 액션 타입 정의
const INCREASE = "counter/INCREASE";
const SET_NUMBER = "counter/SET_NUMBER";
// 액션 생성 함수
export const increase = (count) => {
return {
type: INCREASE,
count
};
}
export function set_number() {
return {
type: SET_NUMBER
};
}
// 초기 상태 정의
const initialState = { count: 0 };
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case 'INCREASE':
return {
count: ++state.count
};
case 'SET_NUMBER':
return {
count: action.numberValue
};
default:
return state;
}
};
export default counter;
// redux/reducer/counter.js
import { createSlice } from "@reduxjs/toolkit";
let initialState = {
count: 0,
}
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increase(state, action) {
state.count = state.count + 1;
},
setNumber(state, action) {
state.count = action.payload;
}
}
})
export default counterSlice.reducer;
export const { increase, setNumber } = counterSlice.actions;
이전의 방식보다 createSlice 메서드로 개선하였을때 코드수가 훨씬 적다. 그 이유는 RTK의 CreateSlice메서드에는 리듀서를 작성하면 해당 리듀서의 키값으로 액션함수가 자동으로 생성되기때문에 기존 리덕스에서는 액션 타입, 액션 생성 함수, 리듀서를 각각 선언해줘야했던것과 달리 CreateSlice메서드를 이용한다면 액션과 리듀서를 합쳐서 선언이 가능하다.
list.js변경과정 생략
//redux/store.js
import { combineReducers } from "redux";
import { createStore } from 'redux';
import counter from "./counter";
import list from "./list";
const rootReducer = combineReducers({ counter, list });
const store = createStore(rootReducer);
export default store;
//redux/store.js
import { configureStore } from "@reduxjs/toolkit";
import counter from "./reducer/counter";
import list from "./reducer/list";
const store = configureStore({
reducer: {
counter,
list,
}
})
/* // middleware, devTools 등을 사용하는 경우
const logger = createLogger();
const store = configureStore({
reduce: {
counter,
list,
}
middleware: [logger, ...getDefaultMiddleware()],
devTools: process.env.NODE_ENV !== "production", // DevTool 옵션 선택하기
});
*/
export default store;
기존 configureStore를 사용했을때는 createStore를 사용했을 때와 달리 combineReducers 사용할 필요없이 결합이 가능하며, 별도의 메소드 없이 바로 미들웨어를 추가할 수 있고 configureStore 에는 기본적으로 ReduxTool이 사용되도록 적용되어 있어 별도의 설정이 필요없다.
import '@/styles/globals.css'
import { Provider } from 'react-redux';
import store from '@/redux/store';
export default function App({ Component, pageProps }) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}
redux를 사용했을때와 달라진건 없다
//components/CounterComponent.jsx
import { useDispatch, useSelector } from "react-redux";
import { increase, setNumber } from "../redux/reducer/counter";
import { useState } from "react"
export default function CounterComponent() {
const dispatch = useDispatch();
const count = useSelector((state) => state.counter.count);
const [inputNum, setInputNum] = useState(0);
const handleIncrement = () => {
dispatch(increase());
};
const handleSet_number = () => {
const num = inputNum;
dispatch(setNumber(num));
};
return (
<div>
<h2> Counter</h2>
<h3>{count}</h3>
<button onClick={handleIncrement}> 증가 </button>
<input type="number" onChange={(() => setInputNum(event.target.value))} />
<button onClick={handleSet_number}>값 설정 </button>
<br />
<hr />
</div >
);
}
//components/MemberListComponent.jsx
import { useDispatch, useSelector } from "react-redux";
import { addMember } from "@/redux/reducer/list";
import { useState } from "react"
export default function MemberListComponent() {
const dispatch = useDispatch();
const memberlist = useSelector((state) => state.list.memberlist);
const [inputName, setInputName] = useState("");
const [inputAge, setInputAge] = useState(0);
const handleAddMember = () => {
let name = inputName;
let age = inputAge;
dispatch(addMember({ name, age }));
};
return (
<div>
<h2> MemberList </h2>
<br />
<div>
<label> 이름 : </label>
<input type="string" onChange={(() => setInputName(event.target.value))} />
<label> 나이 : </label>
<input type="number" onChange={(() => setInputAge(event.target.value))} />
<button onClick={handleAddMember}>값 설정 </button>
</div>
<br />
<h3> 명단 </h3>
{
memberlist && memberlist.map((member) =>
<p>이름 : {member.name} / 나이 : {member.age} </p>
)
}
<hr />
</div >
);
}
useSelector & useDispatch 사용법 역시 동일하다.
import CounterComponent from "../components/CounterComponent"
import MemberListComponent from "@/components/MemberListComponent"
export default function Home() {
return (
<div>
<CounterComponent />
<MemberListComponent />
</div>
)
}
참고
https://redux-toolkit.js.org/
생활코딩 RTK영상
https://darrengwon.tistory.com/1406