이번 솔로 프로젝트를 진행하면서 북마크에 대한 데이터를 처리하는데 있어서 애를 먹었다. 전체적인 데이터는 API를 통해 상태를 저장하고 북마크를 클릭하면 로컬 스토리지에 북마크된 id 를 추가시켜주고, 재클릭시 해당 데이터의 id 를 삭제시켜줌으로써 전역으로 관리해야하는 필요성을 느꼈다. 배웠던 redux 를 활용해보고자 했는데 뭔가 어렵게 느껴졌고, redux 를 쉽게 사용할 수 있는 Redux Toolkit 이라는 라이브러리를 알게 되어서 이를 블로깅 해 보고자 한다.
먼저, Redux의 간단한 이론은 알지만 한 번 더 짚고 넘어가야할 것 같다.
React 는 단방향 데이터 흐름이기 때문에 상태를 사용하기 위해서는 최상위 컴포넌트에서 사용하고자 하는 컴포넌트로 props drilling 을 해야 했었다. 소량의 데이터라면 전달해야하는 props 정도는 괜찮지만, 대량의 데이터라면 관리하기도 어려워지고 중간에 에러가 발생시 에러 발생 시점을 발견하기도 쉽지 않다. 그렇기 때문에 상태를 전역 관리의 필요성이 느껴졌고 이 방법이 바로 Redux 이다.
리덕스를 사용하는 구조는 위와 같다.
즉, Action -> Dispatch -> Reducer -> Store 순으로 데이터가 단방향으로 흐른다.
4가지 개념만 알면 쉽게 느껴진다. 하지만
라는 몇 가지 이유 때문에 Redux 를 더 쉽게 사용할 수 있는 툴킷이 등장한다. 이것이 바로 Redux Toolkit 이다.
//store.js
import { createStore } from "redux";
import reducer from './reducer';
const initialValue = { value: 0 };
const store = createStore(reducer, initialValue);
//reducer.js
function reducer(state, action) {
if (action.type === "PLUS") {
// 반환값은 react의 불변성 때문에 기존의 state 를 복제해서 값을 바꿔줌
return { ...state, value: state.value + action.step };
}
return state;
}
// Counter.js
import { useSelector, useDispatch } from "react-redux";
function Counter() {
const dispatch = useDispatch();
const count = useSelector((state) => state.value);
return (
<div>
<button onClick={() => dispatch({ type: "PLUS", step: 2 })}> +</button>
{count}
</div>
);
}
// App.js
import { Provider } from "react-redux";
import Counter from "./Counter";
export default function App() {
return (
<Provider store={store}>
<div>
<Counter />
</div>
</Provider>
);
}
기존 리덕스와 마찬가지로 Provider
컴포넌트를 이용해 react-redux
에서 리액트 앱에 스토어를 연동할 수 있게 해준다. 연동할 컴포넌트를 감싸주고 props 로 사용할 스토어를 지정해준다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from "react-redux";
import store from "./store/store.js";
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
configureStore()
는 기존 리덕스의 createStore
를 추상화한 것으로, 기존의 번거로운 설정 과정을 자동화 역할을 하는 스토어를 만들어준다.
//store.js
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: counterSlice,
});
기존 리덕스에서 Action 객체를 Dispatch 하기위해 별도의 함수를 작성하고, Reducer를 통해 새로운 Action 객체를 리턴해야 했었다.
createSlice()
는 Action 객체에 대한 함수 설정이나 Reducer 를 따로 생성하지 않아도 된다.
//counterSlice.js
import { craeteSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
up: (state, action) => {
state.value += action.payload;
},
}
})
export const couterActions = counterSlice.actions;
export default counterActions.reducer;
slice.reducer
로 전달한다.//Counter.js
import { useSelector, useDispatch } from "react-redux";
import CouterActions from "./counterSlice.js";
function Counter() {
const dispatch = useDispatch();
const count = useSelecotr(state => state.counter.value);
return(
<div>
<button onClick={() => dispatch({type: 'up' , step: 2})}>+</button>
)
}
useSelector()
로 스토어에서 상태값을 가져온다. 기존 리덕스에서는 state.value 였다면 생성된 createSlice 의 name 값(counter)을 이용하여 state.counter.value 를 불러온다.useDispatch()
를 통해 변경되는 값을 스토어로 전달한다.