리덕스를 사용할 때 애플리케이션의 규모가 커질 경우 문제점이 생길 수 있다.
예를 들어 액션 타입에 오타를 낼 가능성이 높아진다거나, 다른 개발자와 작업하다보면 액션 타입의 충돌이 발생할 수도 있다.
또는 관리하는 데이터가 많아질수록 상태 객체도 커지는데, 리듀서에서 반환해야 하는 많은 상태를 복사해야 한다.
그러다보면 파일의 크기가 유지할 수 없을만큼 커질 수도 있다.
대부분의 Redux 작업을 단순화하고, 흔한 실수를 방지하며, Redux 앱을 만들기 쉽게 해주는 모범 사례를 통해 만들어진 Redux Toolkit을 사용해보자!
설치
npm install @reduxjs/toolkit
redux toolkit에 이미 리덕스가 포함되어 있기 때문에 만약 리덕스를 설치해둔 상태라면 리덕스를 삭제한다.
상태 슬라이스 생성 (createSlice)
상태가 만약 여러 조각으로 나뉘어 있다면(예를 들어 인증 상태와 카운터 상태 등 관련 없는 것들은 나누어 관리한다)
그에 맞는 상태 슬라이스를 여러개 생성하면 된다.
import { createSlice } from '@reduxjs/toolkit';
const initialCounterState = { counter: 0, showCounter: true };
const counterSlice = createSlice({
name: 'counter',
initialState: initialCounterState,
reducers: {
increment(state) {
state.counter++;
},
decrement(state) {
state.counter--;
},
increase(state, action) {
state.counter = state.counter + action.payload;
},
toggle(state) {
state.showCounter = !state.showCounter;
},
},
});
export const counterActions = counterSlice.actions;
export default counterSlice.reducer;
name: slice의 이름
initialState: 초기 상태 설정
reducers: 메서드(리듀서)가 있는 객체를 넣는다.
이 메서드들은 나중에 리덕스에 의해 호출되고, 현재 상태를 인자로 받는다.(state), 만약 추가 데이터를 디스패치해서 보낸다면 두 번째 인자는 action이다.
이 메서드 안에서 상태를 state.counter++ 처럼 상태를 변경할 수 있을 것처럼 보이지만 내부적으로 기존 상태를 조작하지 않도록 보장한다.
redux toolkit은 내부적으로 immer를 사용하는데, 상태를 변경하는 것처럼 보이는 코드를 감지하고 자동으로 원래 있는 상태를 복제한다.
그리고 새로운 상태 객체를 생성하고 모든 상태를 변경할 수 없게 유지한다. 즉, 불변성을 유지하며 업데이트할 수 있는 것이다.
createSlice는 각 리듀서에 대한 액션 생성자 함수를 자동으로 생성하고 리듀서 이름에 기반하여 내부적으로 액션 타입 문자열을 생성한다.
이때 스토어를 생성하기 위해 필요한 리듀서 counterSlice.reducer 와 컴포넌트에서 액션을 디스패치하기 위해 counterSlice.actions를 export해준다.
만약 counterSlice.actions.toggle(); 와 같이 메서드를 호출하면 { type: "자동으로 생성되는 고유한 식별자" } 같은 식으로 액션 객체가 생성된다.
store 생성 (configureStore)
기존에 createStore에는 리듀서를 전달했었다. 하지만 이 함수에는 하나의 리듀서만 전달해야 하는데 slice가 여러 개라면 리듀서도 여러 개이다.
툴킷에서는 configureStore를 제공하는데 이것은 여러 개의 리듀서를 하나의 리듀서로 쉽게 합칠 수 있게 한다. 또한 configureStore를 통해 저장소 설정을 간단하게 할 수 있다.
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counter';
import authReducer from './auth';
const store = configureStore({
reducer: {
counter: counterReducer,
auth: authReducer,
},
});
export default store;
configureStore 함수 내부에 설정 객체를 전달하는데, 이 객체에는 reducer 라는 프로퍼티가 있는데 여기에 리듀서 함수를 전달한다.
사용하는 상태 슬라이스가 하나라면 하나의 리듀서 함수를 전달하면 되지만, 여러 개라면 객체(리듀서 맵)을 전달한다.
이 객체 안에는 key: value형태로 value에는 리듀서를, key에는 원하는 리듀서 이름을 설정해주면 된다.
그러면 configureStore가 모든 리듀서를 하나의 큰 리듀서로 병합해준다.
리듀서 사용하기(액션 전송하기)
import { useSelector, useDispatch } from 'react-redux';
import { counterActions } from '../store/counter';
const dispatch = useDispatch();
const decrementHandler = () => {
dispatch(counterActions.decrement());
};
const increaseHandler = () => {
dispatch(counterActions.increase(5));
};
마찬가지로 useDispatch로 dispatch 함수를 가져오고,
상태 슬라이스의 actions 객체를 import 해서 사용한다.
액션을 보낼 때 dispatch 함수 안에 counterActions.decrement()와 같이 자동으로 생성된 액션 생성자 메서드를 호출하면 액션 객체가 반환되고 dispatch의 인자로는 결국 액션 객체가 들어가는 것이다.
redux toolkit을 사용하기 전에는 직접 액션 객체를 생성해서 넣어주어야 했는데 이제 액션 객체를 직접 생성할 필요가 없다.
만약 추가 데이터를 보내야 한다면 이 액션 생성자 메서드 안에 값을 전달한다. redux toolkit은 이 전달한 값을 액션 객체의 payload 필드에 저장한다.
따라서 리듀서 함수에서 접근할 때 action.payload로 접근하는 것이다!
데이터 접근하기
configureStore로 저장소를 생성했을 때 여러 리듀서를 하나의 리듀서로 병합했기 때문에 스토어에서 데이터에 접근하는 방식이 바뀐다.
기존에는 아래와 같이 상태에 접근했다면,
const counter = useSelector((state) => state.counter);
const show = useSelector((state) => state.showCounter);
이제는 configureStore에서 reducer에 리듀서 맵에서 할당했던 key(식별자)를 이용해서 특정 상태 slice에 접근하면 된다.
(counter, auth와 같은 식별자)
const store = configureStore({
reducer: {
counter: counterReducer,
auth: authReducer,
},
});
const counter = useSelector((state) => state.counter.counter);
const show = useSelector((state) => state.counter.showCounter);
즉, state.counter는 리덕스에게 counter와 관련된 슬라이스에 접근한다는 걸 알려주는 것이다.
이렇게 Redux Toolkit을 사용하면 여러 개의 상태 슬라이스를 쉽게 관리할 수 있다.
'React' 카테고리의 다른 글
[React] Routing (0) | 2023.09.25 |
---|---|
[React] Redux에서 비동기 작업하기 (0) | 2023.09.22 |
[React] 리액트에서 리덕스 사용하기 (0) | 2023.09.21 |
Redux(리덕스)란? (0) | 2023.09.21 |
[React] Hook의 규칙 (0) | 2023.09.17 |