📢 어렵고 정석적인 개념 설명보다는 저같은 초보자도 이해하기 쉽게 정리하는 것을 원칙으로 포스팅하고 있습니다. 😄
Redux Toolkit을 사용하는 이유
Redux Toolkit이란 Redux 상태 관리 라이브러리의 공식적인 패키지로, Redux를 보다 간편하고 효율적으로 활용할 수 있도록 도와주는 도구이다. Redux Toolkit이 아닌 Redux를 처음 접했을 때 느꼈던 막막함은 아직도 기억에 남을 정도로 보일러플레이트 코드가 많아서 작성 부담이 컸었고, 표준 없이 자유로운 구조로 상태 관리를 해야 하는 것에 매우 복잡함을 느꼈었다.
Redux Toolkit은 보일러플레이트 코드* 작성을 최소화하고 표준을 제공하여 개발자로서 더 효율적으로 학습하고 사용할 수 있게 도와주는 도구이다. Redux에 비해 학습 곡선도 낮고 코드 일관성도 높아지는 등 리덕스보다 장점이 훨배 많기 때문에 현재는 Redux Toolkit을 Redux 개발의 표준으로 채택하고 있다고 한다.
Redux Toolkit에서 제공하는 주요 기능으로는 createSlice, configureStore 등이 있고 react-redux의 Hooks인 useSelector와 useDispatch도 Redux Toolkit과 함께 사용된다. 간단한 예시 코드를 보면서 쉽게 알아보자.
🤔 보일러플레이트 코드란?
Redux에서 보일러플레이트 코드란 상태 관리를 위해 Redux를 설정하고 사용할 때 반복적으로 작성되는 코드를 말한다. Redux에서는 하나의 상태를 관리하기 위해 스토어, 액션 타입, 액션 생성자 함수, 리듀서 함수 등 유사한 구조의 함수들을 계속 생성해야 했는데, 이로 인해 Redux를 사용하는 것이 불편하고 복잡해졌다.
Redux Toolkit에서는 이러한 보일러플레이트 코드를 slice 형태로 더 간결하게 작성할 수 있도록 하여 Redux를 더 간단하고 효율적으로 사용할 수 있게 해주었다.
사용 방법
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
npm으로 Redux Toolkit과 react-redux를 설치해 주자. 참고로 Redux Toolkit을 설치하면 redux는 따로 설치할 필요가 없다.
const counterSlice = createSlice({
name: 'counter', // 여기 이름은 그냥 slice 구분하기 위해 지은 것(개발자 편의)
initialState: 0,
reducers: {
increment: (state) => state + 1
}
})
const store = configureStore({
reducer: {
counter: counterSlice.reducer // useSelector에서 가져가는 값
}
})
const Counter = () => {
const count = useSelector(state => state.counter);
const dispatch = useDispatch();
const handleIncrease = () => {
dispatch(counterSlice.actions.increment());
}
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={handleIncrease}>+</button>
</div>
)
}
const App = () => {
return (
<Provider store={store}>
<div>
<Counter />
</div>
</Provider>
)
}
위 코드는 플러스 버튼을 누르면 count가 1씩 증가하는 리액트 리덕스 애플리케이션이다.
먼저, count라는 상태를 사용하기 위해 해당 상태를 생성해야 한다. createSlice()는 상태를 생성하고 초기값을 설정, 액션 및 리듀서를 제공해 준다. 각 slice는 개별 상태를 관리하므로 상태가 필요할 때마다 slice를 생성해 준다.
createSlice()로 상태를 생성했으면, 이들을 관리할 store가 필요하다. configureStore()는 생성한 slice들을 리듀서 안에 포함하여 전역적으로 상태를 관리해 준다. store.reducer안에 slice 이름은 아까 createSlice()로 생성한 slice의 이름과 무조건 같을 필요는 없지만, 협업이나 효율적인 코드 관리를 위해 통일하는 편이 좋다.
이제 상태와 store를 만들었으니 이들을 활용하여 상태값을 출력하고 상태 액션을 호출해 보자. 이를 위해 react-redux의 useSelector와 useDispatch Hook을 사용한다. (useSelector로 상태값을 가져오고 useDispatch로 상태 액션을 호출한다.)
useSelector()는 createSlice로 생성한 slice의 상태값을 가져올 수 있다. 매개변수에 콜백함수로 state => state.counter를 하면, 아까 store의 리듀서에 저장된 counter 이름의 slice 상태값이 호출된다. 즉, 상태값을 가져올 때는 store에서 가져와야 한다.
useDispatch()는 createSlice로 생성한 상태 액션을 호출할 수 있다. 미리 변수에 useDispatch()를 선언해 주고, 그 변수의 콜백함수로 counterSlice.actions.increment하면 counterSlice라는 slice 변수의 reducers 안에 있는 increment가 실행된다. 즉, 상태 액션을 호출할 때는 createSlice로 선언된 변수에 접근해야 한다.
두 Hooks 모두 컴포넌트의 외부에서 선언될 수 없다. 무조건 컴포넌트 안에서 선언되어야 한다.
반대로, createSlice와 configureStore는 컴포넌트 외부에서 선언해줘야 한다.
상태의 불변성 유지
reducers: {
increment: (state) => {
return {
...state, // redux-toolkit에서는 immer.js로 인해 생략 가능
count: state.count + 1,
};
},
},
리액트나 리덕스에서 상태값을 다룰 때 주의할 점이 있다. 바로 상태 업데이트 시 불변성을 유지해야 한다는 것이다.
예를 들어 아까 예시 코드에서 처럼 바로 state + 1을 해버리는 게 아니라, 스프레드 연산자(...)를 이용해서 기존의 상태값을 복사본으로 남겨놔야 한다는 것이다. (그래야 리렌더링 할 때 기존의 상태값과 업데이트된 상태값을 비교하면서 이 부분이 변경됐구나를 확실하게 알 수 있으니까)
하지만, Redux Toolkit을 사용한다면? 굳이 스프레드 연산자를 써가면서 기존의 상태값을 써줄 필요가 없다.
왜냐하면 Redux Toolkit에는 immer.js 라이브러리가 포함되어 있는데 이게 불변성 유지를 도와주는 혜자스러운 도구이기 때문이다. 그렇기 때문에 Redux Toolkit에서는 불변성 유지를 보다 간편하게 처리할 수 있다.
정리하자면, Redux Toolkit은 Redux보다 더 효율적으로 상태 관리를 할 수 있는 도구이다. 보일러플레이트 코드 작성을 줄이고 표준을 제공하여 개발자의 불필요한 부담을 덜어주고, 코드의 일관성과 효율성을 향상시킨다.
(Redux에 데인 코린이들에게 빛과 같은 존재이기도 하다...)
1. createSlice({name, initialState, reducers}) : 상태 생성, 초기값 설정, 액션 생성. 상태 1개당 slice 1개라고 생각.
2. configureStore({reducer}) : slice들을 전역적으로 한 곳에서 관리해 주는 store 역할.
3. useSelector(state) : store에 저장된 slice에 접근해서 상태값을 가져옴.
4. useDispatch(slice.actions.action()) : slice에 직접 접근해서 특정 액션을 실행함.
5. 리덕스 툴킷을 사용하면 상태의 불변성은 알아서 지켜진다. 왜냐하면 immer.js가 있기 때문!
'Redux' 카테고리의 다른 글
[Redux] 바닐라 자바스크립트에서 리덕스 사용해보기 (0) | 2023.08.27 |
---|