이제까지 노마드코더의 redux 강의를 따라왔다면, createStore
function에서 아래 이미지와 같이, createStore에 줄이쳐지며 configureStore
메소드를 사용하길 권장한다는 메세지를 확인할 수 있다.
이번 포스트에선 안내 메세지처럼 configureStore
를 비롯해 Redux Toolkit 라이브러리가 가진 효율적인 function과 method들을 알아보고자 한다.
가장 먼저 살펴볼 method는 createAction()이다.
기존의 코드가 Action type 반환과 그 내용을 반환해야했다면 아래처럼, redux-toolkit을 통해 2줄의 코드로 압축할 수 있다.
const addToDo = createAction("ADD");
const deleteToDo = createAction("DELETE");
// const addToDo = (text) => {
// return {
// type: ADD,
// text,
// };
// };
// const deleteToDo = (id) => {
// return {
// type: DELETE,
// id: parseInt(id),
// };
// };
기존 코드는 action 함수를 선언할 경우 직접 타입을 기입해 반환해야 했었는데, redux-toolkit의 createAction() argument로 타입 이름이될 문자열을 간략하게 넘겨주어 바로 type을 선언할 수 있다.
위 예시 코드로 작성한 addToDo와 deleteToDo method가 반환하는 오브젝트는 아래 이미지와 같다.
타입이 자연스럽게 오브젝트안에 담겨있음을 알 수 있다.
다만 여기서, 이미지를 보면 오브젝트 안에 내가 컴포넌트에 전달할 'text' 데이터가 없는 걸 볼 수 있다. createAction()으로 선언한 함수는 object 내부에 type
과 payload
를 전달한다. 여기서 payload가 기존 코드에서 return문 안에 작성해 전달하던 데이터들이 들어갈 영역이다!
reducer의 add action case 리턴문 내부에 payload를 받아가도록 작성하자.
...
const reducer = (state = [], action) => {
switch (action.type) {
case addToDo.type:
return [
{
text: action.payload,
id: Date.now(),
},
...state,
];
...
마찬가지로, delete action case의 리턴문에서도, payload를 통해 동작해야하므로 아래와 같이 코드를 수정한다.
const reducer = (state = [], action) => {
switch (action.type) {
...
case deleteToDo.type:
return state.filter((toDo) => toDo.id !== action.payload);
default:
return state;
...
보통의 reducer 작성 코드는 아래와 같은 형태이다.
const reducer = (state = [], action) => {
switch (action.type) {
case 'type1':
return state + action.payload
case 'type2':
return state + action.payload;
default:
return state;
}
};
하지만 createReducer()
를 사용하면 Switch문을 간소화할 수 있다.
그리고 또 다른 장점을 보면 reducer는 state를 mutate할 수 없었는데 createReducer()로 선언한 reducer는 표면상으론 state object를 쉽게 mutate해 쓸 수 있다. 사실 redux-toolkit은 Immer를 써서 작성된 코드를 바탕으로 새 state를 생성하고 리턴해준다.
다만 편의에 따라 새 state 생성과 mutation 모두 가능하므로 프로그래머의 선택지가 늘어난 셈이다. 이제 기존의 store.js 코드를 수정해 reducer는 아래처럼 작성한다.
const reducer = createReducer([],{
[addToDo] : (state, action) => {
state.push({ text: action.payloadm ,id: Date.now()})
},
[deleteToDo] : (state, action) => {
state.filter((toDo) => toDo.id !== action.payload)
}
})
/*
const reducer = (state = [], action) => {
switch (action.type) {
case addToDo.type:
return [
{
text: action.payload,
id: Date.now(),
},
...state,
];
case deleteToDo.type:
return state.filter((toDo) => toDo.id !== action.payload);
default:
return state;
}
};
*/
위 코드에서 [addToDo] 액션과 [deleteToDo] 액션의 형태가 다른데 [addToDo] 같은 경우 mutation을 사용해 state object에 새로운 오브젝트를 push하는 동작을 하고 [deleteToDo]는 state.filter를 통해 새 오브젝트를 반환하도록 동작한다.
두 방식의 차이에 주의하자.
configureStore는 store 생성과 동시에 기본 middleware로 redux-thunk
를 생성한다. 간략하게 아래 코드처럼 argument로 reducer 오브젝트를 전달해 store를 configureStore로 선언하면 된다.
const store = configureStore({reducer});
이제까지 배운 리덕스의 문법들을 살펴보면 쉬워보이지만, 각각의 function들이 작동하기 위해 필요로하는 코드들(보일러 플레이트)이 많다. 보일러 플레이트는 프로그래머의 실수를 유발하며 이런 이슈를 해결하기 위해 Redux Toolkit에서는 createSlice()를 도입했다고 한다.
createSlice()를 쓰면 분리되어있던 action 선언과 reducer 선언을 통합해 initial state, reducer, action type, action function을 한 번에 편하게 선언 할 수 있다.
주석처리한 기존의 redux코드와 redux-toolkit의 createSlice() 코드를 비교해보자.
import { createStore } from "redux";
import { configureStore, createSlice } from "@reduxjs/toolkit";
// const addToDo = createAction("ADD");
// const deleteToDo = createAction("DELETE");
// const reducer = createReducer([],{
// [addToDo] : (state, action) => {
// state.push({ text: action.payloadm ,id: Date.now()})
// },
// [deleteToDo] : (state, action) => {
// state.filter((toDo) => toDo.id !== action.payload)
// }
// })
const toDos = createSlice({
name: "toDosReducer",
initialState: [],
reducers: {
add: (state, action) => {
state.push({ text: action.payloadm, id: Date.now() });
},
remove: (state, action) => {
state.filter((toDo) => toDo.id !== action.payload);
},
},
});
const store = configureStore({ reducer : toDos.reducer });
export const {
add, remove
} = toDos.actions;
export default store;
createReducer와 마찬가지로 createSlice()도 Immer 라이브러리를 내장해 불변성을 보장하고 이런 개념은 Ducks 패턴이라는데 도입된다는데 아직 전부 이해하진 못했고 아래 포스트를 참고해보면 좋을 것 같다.
https://velog.io/@dolarge/React-Redux-Ducks-패턴
http://blog.hwahae.co.kr/all/tech/tech-tech/6946/