들어가기
프로젝트의 규모가 커지고 관리해야 할 상태가 더 많아진다면 여러 가지 문제가 생길 수 있다.
먼저 리듀서 함수의 액션 타입(식별자)에서 오타가 난다거나, 식별자의 이름이 충돌되는 경우가 그렇다.
또 데이터와 상태가 많아질수록 리듀서 함수의 길이가 점점 길어지면서 유지보수하기 어려워질 수 있다.
const counterReducer = (state = initialState, action) => {
if (action.type === 'plus') {
return {
counter: state.counter + 1,
showCounter: state.showCounter,
};
}
if (action.type === 'minus') {
return {
counter: state.counter - 1,
showCounter: state.showCounter,
};
}
물론 이런 경우 const PLUS = 'plus'와 같이 상수로 내보내는 것도 하나의 방법이긴 하지만
Redux Toolkit을 사용하는 것을 강력 권장한다.
Redux Toolkit이란
먼저 Redux Toolkit은 효율적인 Redux 개발을 위해 Redux 팀에서 개발한 추가적인 패키지다.
Redux 로직을 작성하기 위한 표준 방식이 되도록 만들어졌기에 필수는 아니지만 Redux와 함께 사용하는 것을 강력 추천한다고 한다.
Redux Toolkit을 통해 Store 준비, 리듀서 함수 정의, 불변 업데이트 로직, 액션 생산자나 액션 타입을 직접 작성하지 않고도 전체 상태 조각을 만들어내는 기능까지, Redux에서 사용하는 유틸리티 함수들이 들어 있다. 또 아직 접해보진 않았지만 비동기 로직을 위해 사용되는 Redux Thunk와 셀렉터 작성을 위한 Reselct 등 애드온도 포함되어 있다고 한다.
한 마디로 기존의 Redux 기능을 그대로 사용하되 보다 간결하게 쓰고 쉽게 관리할 수 있다는 말인데,
1. 저장소를 설정하는 것이 너무 복잡하다.
2. 많은 패키지들을 추가적으로 설치해야 한다.
3. 보일러 플레이트 코드가 너무 많다.
Redux를 사용하며 겪을 수 있는 불편함 3가지를 해결할 수 있도록 돕는 공식 도구라고 할 수 있다.
Redux Toolkit에 포함된 것은 여러 가지가 있는데, 오늘 강의에서 알아본 것은 아래 두 가지다.
1. configureStore(): createStore를 랩핑해 쓸만한 기본값들과 단순화된 설정을 제공한다. 리듀서 조각들을 합쳐주고, 기본으로 제공되는 redux-thunk를 포함해 미들웨어를 더해주며 데브툴 확장을 사용할 수 있게 한다.
2. createSlice(): 조각 이름과 상태 초기값, 리듀서 함수로 이뤄진 객체를 받아 그에 맞는 액션 생산자와 액션 타입을 포함하는 리듀서 조각을 자동으로 생성한다.
npm install @reduxjs/toolkit
yarn add @reduxjs/toolkit
만약 기존에 Redux를 설치했다면 Redux Toolkit에 이미 Redux가 포함되어 있기 때문에 package.json에서 삭제해준다.
createSlice 사용하기
createSlice는 name, initialState, 리듀서 함수가 필요하다.
const initialState = { counter: 0, showCounter: true };
const counterReducer = (state = initialState, action) => {
if (action.type === 'plus') {
return {
counter: state.counter + 1,
showCounter: state.showCounter,
};
}
if (action.type === 'minus') {
return {
counter: state.counter - 1,
showCounter: state.showCounter,
};
}
if (action.type === 'plus5') {
return {
counter: state.counter + 5,
showCounter: state.showCounter,
};
}
if (action.type === 'toggle') {
return {
counter: state.counter,
showCounter: !state.showCounter,
};
}
이전에는 객체를 다 복사해줘야 하는 불편함이 있었다.
무슨 말이냐면 toggle의 경우 showCounter만 바꾸면 되는데 객체의 불변성을 해지치 않기 위해 counter까지 같이 적어줘야 했고, 그외 counter를 가감하는 액션 역시 counter만 수정하면 되는데 showCounter까지 매번 같이 적어줘야 했다.
액션 타입이 많이 늘어나고, counter나 showCounter 외에 더 많은 전역 상태가 필요하다면 매우 불편했을 것이다.
import { createSlice } from '@reduxjs/toolkit';
const initialState = { counter: 0, showCounter: true };
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
plus(state) {
state.counter++;
},
minus(state) {
state.counter--;
},
plus5(state, action) {
state.counter = state.counter + action.payload;
},
toggle(state) {
state.showCounter = !state.showCounter;
},
},
});
createSlice를 사용하기 위해서는 먼저 import를 해준다.
createSlice는 조각 이름(name)과 상태의 초기값, 리듀서 함수로 이루어진 객체가 필요하다.
따라서 name은 'counter',
초기값은 이전과 같이 counter가 0이고 showCounter가 true인 객체,
리듀서 함수는 reducers 객체로 적어준다.
1. 이전에 toggle 액션을 정의할 때 6줄 정도 사용되었다면 이제는 3줄로 코드가 많이 줄어들었다는 것을 알 수 있다.
원래 객체의 불변성을 지키기 위해서 다이렉트로 값을 수정하면 안되지만, Redux Toolkit에는 내부적으로 Immer라는 패키지를 사용한다.
React 공식 문구의 객체 불변성 관련해서 공부할 때 Immer 라이브러리를 본 적이 있는데, 이 Immer는 객체를 이렇게 수정할 때 코드를 감지하고 자동으로 원래 있던 상태를 복제해 그것을 변경 시키는 특징이 있다. 그래서 코드 상으로는 객체에 다이렉트로 접근해 값을 수정한 것 같아 보이지만, 실제로는 원본 값은 유지하고 복제한 것을 수정했기 때문에 개발자가 직접 코드를 길게 작성할 필요 없이, 불변성을 걱정할 필요 없이 간략한 코드로도 똑같은 기능을 구현할 수 있게 되는 것이다!
2. plus5는 5라는 값을 action 객체의 amount로 주고 있다.
const morePlusHandler = () => {
dispatch({ type: 'plus5', amount: 5 });
};
이런 경우 매개변수로 state와 동시에 action을 받아주면 된다.
action 객체의 amount니까 state.counter = state.counter + action.amount 가 되는 것이다.
그런데 이때, store가 slice를 인식하고 사용하려면 store에 등록을 먼저 해줘야 한다.
configureStore 사용하기
// const store = createStore(counterSlice.reducer);
const store = configureStore({
reducer: counterSlice.reducer,
});
기존의 것을 주석처리 하고, configureStore를 import한다.
configureStore로 createStore를 대신해 counterSlice.reducer를 랩핑해 store를 만든다.
만약 리듀서 함수가 여러 개라면 아래와 같이 적어주면 된다.
지금은 리듀서 함수가 하나 뿐이기 때문에 바로 할당해주면 된다.
const store = configureStore({
reducer: { counter: counter, todos: todos },
});
이제 액션 식별자 값을 얻기 위해 counterSlice.actions를 사용하자.
counterSlice.actions. 만 쳐도 자동완성으로 plus, minus, plus5 등 메서드 이름이 나온다.
counterSlice.actions.plus()와 같은 메서드를 액션 생성자라고 부르며, 이를 통해 액션 객체가 생성된다.
이 객체는 이미 type 프로퍼티를 갖고 있다.
그래서 액션 생성자 메서드를 실행해 리듀서 메서드와 이름이 같으면 액션을 전달한다!
이 때 액션 타입 외에 데이터는 추가 필드명이 payload인 곳에 저장되기 때문에
2번에서 amount로 전달한 5라는 데이터는 payload로 저장되게 된다.
이것은 Redux Toolkit이 액션 생성자를 자동으로 생성하면서 정한 필드명이라 개발자가 다르게 정의할 수 없다.
plus5(state, action) {
state.counter = state.counter + action.payload;
},
그렇기에 plus5 메서드의 action.amount는 action.payload로 수정되어야 정상적으로 접근할 수 있다.
Counter 수정하기
export const counterActions = counterSlice.actions;
마지막으로 counterSlice.actions를 export해준다.
const plusHandler = () => {
dispatch(counterActions.plus());
};
const minusHandler = () => {
dispatch(counterActions.minus());
};
const morePlusHandler = () => {
dispatch(counterActions.plus5(5));
};
const toggleHandler = () => {
dispatch(counterActions.toggle());
};
그리고 이렇게 사용할 곳에 import 해서 액션을 생성하면 끝!
'⚛️ React > ✨ Redux' 카테고리의 다른 글
[Redux] Redux의 payload와 다중 state 작업하기 (0) | 2024.04.12 |
---|---|
[Redux] useSelector, useDispatch로 상태 업데이트 하기 (0) | 2024.04.12 |
[Redux] Redux 알아보기 (1) | 2024.03.31 |