대상 라이브러리
- Redux Toolkit (@reduxjs/toolkit)
- Zustand
- Recoil
비교
NPM Trends
간단 비교
Redux Toolkit | Zustand | Recoil | |
러닝커브 | Redux 를 알고 있으면 거의 없음 | 매우 낮음 | 낮음 (개인적으로는 안낮음 ^~^..) |
디버깅 툴 | Redux Devtools | Redux Devtools | 있으나 불안정함 |
제작자 | Dan Abramov (Redux 개발자) |
카토 다이시 (Jotai 개발자) |
|
현재 버전 | v1.9.2 | v4.3.2 | v0.7.6 |
출시 연도 | 2020 | 2019 | 2020 |
비고 | Redux 에서 영감을 받아 만들었다고 함 | 아직 1.0 이 나오지 않았음 자잘한 버그가 종종 있다고 함 |
소개 및 사용 예제
Redux Toolkit
- RTK 라고도 함
- Store 와 Action, Reducer 로 구성되어있음
- 이전의 순수 Redux 보다 간결하게 코드 작성이 가능함
Store 만들기
예전에 createStore 로 만들고, middleware 를 복잡하게 감쌌던 부분이 보다 간결해졌음
configureStore() 로 store 정의하기
// store/store.js
export const store = configureStore({
reducer: {
todo: todoSlice
},
middleware: getDefaultMiddleware => getDefaultMiddleware({
serializableCheck: false
})
});
<Provider />
로 감싼 후, store 추가하기
// index.js
root.render(
<Provider store={store}>
<RouterProvider router={router}>
<App/>
</RouterProvider>
</Provider>
);
Action 및 Reducer 만들기
createSlice 라는 함수를 통해 slice 를 만들고, 만든 slice 를 통해서 action 과 reducer 를 만들 수 있음
// store/todoSlice.js
const initialState = {
list: [{id: 0, text: "...", isDone: true}]
}
let sequence = 1;
// slice 만듦
const todoSlice = createSlice({
name: "todo", // unique
initialState,
reducers: {
addTodo(state, action) {
// payload = text
const todoItem = new TodoItem(sequence, action.payload);
sequence++;
state.list = [...state.list, todoItem];
},
toggleTodo(state, action) {
// payload = id
state.list = state.list.map((todoItem) => todoItem.id === action.payload ? {
...todoItem,
isDone: !todoItem.isDone
} : todoItem
)
}
}
});
// actions
export const {addTodo, toggleTodo} = todoSlice.actions;
// reducer
export default todoSlice.reducer;
State 를 불러오고, Action 을 Dispatch 하기
이 부분은 Pure Redux 에서 사용했던 방법과 동일함, useDispatch 로 dispatch 를 가져온 후, action dispatch 하기
// page/TodoList/TodoList.jsx
import {useDispatch, useSelector} from "react-redux";
import {addTodo, toggleTodo} from '../../store/todoSlice';
const list = useSelector((state) => state.todo.list);
const dispatch = useDispatch();
return (
<div>
<h2>TodoList</h2>
<div>
<input type="text" value={text} onChange={updateText}/>
<button onClick={() => dispatch(addTodo(text))}>추가</button>
</div>
<div>
{
list.map((todo) => <TodoItem key={todo.id} {...todo}/>)
}
</div>
</div>
);
Zustand
- Redux 에서 영감을 받아 만든 만큼, Redux 와 유사한 부분이 많이 있음
- Store 와 Action, Reducer 로 구성되어있음
- 아주 간결하게 코드 작성이 가능함
store 및 action 정의하기
Redux Devtools 도 간단하게 추가가 가능함
// zustand/store.js
import {create} from 'zustand'
import {devtools} from 'zustand/middleware'
import {TodoItem} from "../domain/TodoItem";
let sequence = 0;
// store 만들기
export const useTodoStore = create(
devtools((set) => ({
list: [],
addTodo: (text) => set((state) => { // action 만들기
const todoItem = new TodoItem(sequence, text);
sequence++;
return {
list: [...state.list, todoItem]
}
}),
toggleTodo: (id) => set((state) => { // action 만들기
return {
list: state.list.map((todoItem) =>
todoItem.id === id ? {...todoItem, isDone: !todoItem.isDone} : todoItem)
}
})
}))
)
State 를 불러오고, Action 을 Dispatch 하기
// page/TodoList/TodoList.jsx
import React, {useState} from 'react';
import {useTodoStore} from '../../zustand/store';
const TodoList = () => {
const [text, setText] = useState('');
const list = useTodoStore((state) => state.list); // state 가져오기
const addTodo = useTodoStore((state) => state.addTodo); // action 가져오기
const updateText = ({target}) => {
setText(target.value);
}
return (
<div>
<h2>TodoList</h2>
<div>
<input value={text} onChange={updateText} type="text"/>
<button onClick={() => addTodo(text)}>추가</button> // action dispatch 하기
</div>
<div>
{
list.map((todo) => <TodoItem key={todo.id} {...todo}/>)
}
</div>
</div>
);
};
Recoil
- atom 과 selector 로 구성되어있음
- atom 은 전역 state 임
- selector 는 state 를 바탕으로 값을 만들어내는 것을 말함, 예를들어 state 의 길이를 구하는 함수를 만들어두고, state 의 길이를 구해야 할 때마다 그 함수를 호출해서 state 처럼 사용할 수 있음
atom 정의하기
key 는 unique 해야함
// recoil/todoList/state.js
import {atom, selector} from "recoil";
// atom 정의
export const todoState = atom({
key: "todo", // unique
default: []
});
Selector 정의하기
selector 는 state 를 바탕으로 값을 만들어내는 것을 말함
예를 들어, todoList
가 아래와 같을 때, [0, 0, 1, 1]
의 배열로 만들려고 함
[
{ id: 0, isDone: false, text: "todo1" },
{ id: 1, isDone: true, text: "todo2" },
{ id: 2, isDone: true, text: "todo3" },
{ id: 3, isDone: false, text: "todo4" }
]
이렇게 작성하면 됨
// recoil/todoList/state.js
import {atom, selector} from "recoil";
// selector 정의
export const getDoneTodoListState = selector({
key: "todo/done",
get: ({get}) => {
const todoList = get(todoState);
return todoList.reduce((acc, curr) => {
const isDone = curr.isDone ? 1 : 0;
return [...acc, isDone];
}, []).sort((a, b) => a-b);
}
})
Selector 에서 만들어둔 함수 사용해서 값 불러오기
import {getDoneTodoListState} from "../../recoil/todoList/state";
const Statistics = () => {
const doneTodoList = useRecoilValue(getDoneTodoListState); // 가져오기
return (
<div>
<h2>Statistics</h2>
<div className="graph">
{
doneTodoList.map((isDone) =>
<div className={clsx("block", {
done: isDone,
})}/>)
}
</div>
</div>
);
};
atom update 하기
TodoItem
추가하기
// page/TodoList/TodoList.jsx
import {useRecoilState} from "recoil";
import {todoState} from "../../recoil/todoList/state";
import {TodoItem} from "../../domain/TodoItem";
const [list, setList] = useRecoilState(todoState); // atom 가져오기
const addTodo = () => {
setList((prevList) => [...prevList, new TodoItem(sequence, text)]); // atom update 하기
setSequence(sequence + 1);
}
사용기
- Redux Toolkit
- Redux 를 써본 적이 있어서 그런지 크게 어렵지 않았음
- Zustand, Recoil 에 비해 코드가 복잡하다고 하지만 그다지 복잡한 것 같지 않음
- Zustand
- Redux 와 개념이 비슷한데다가 간단해서 매우 쉽고 간단했음
- Redux DevTools 를 같이 사용할 수 있어서 편했음
- 다만 너무 쉽고 간단해서 큰 프로젝트에 적용해도 괜찮을지 잘 모르겠음
- Recoil
- 개념은 간단하지만, 용어와 더불어 낯설어서 (atom, selector) 받아들이는데 러닝커브가 있었음
- 개념을 너무 간단하게 가져가서 그런지, 오히려 개발자는 해야 할 일이 많다고 느낌
- Redux Toolkit 이나 Zustand 의 경우, action 에서 todoList 에 todoItem 을 추가하는 할 때에, Redux Toolkit 이나 Zustand 의 경우, action 에서 로직 (text 를 input 으로 받으면 TodoList 를 추가해줌) 을 구현해두고 호출이 가능함
- 하지만 Recoil 은 state 가 너무 간단하다보니, 로직을 component 에서 작성해야 했음
// recoil
setList((prevList) => [...prevList, new TodoItem(sequence, text)]);
// zustand
addTodo(text)
// RTK
dispatch(addTodo(text))
다른 개발자들은 큰 프로젝트에 주로 무엇을 사용할까?
- Jotai
- RTK
'막개발글' 카테고리의 다른 글
CSR, SSR, Rendering (0) | 2023.06.17 |
---|---|
DOM 에 대해서 - 역사 / 의미 / 객체 / 호환성 (0) | 2023.05.11 |
[아티클 정리] 사용자 경험은 어떻게 측정할까요? - 오의택 (0) | 2023.04.09 |
서버의 부하를 줄이는 법 (feat. 로드밸런싱) (0) | 2023.04.07 |
라이브러리와 프레임워크 (feat. 리액트는 라이브러리일까 프레임워크일까) (2) | 2023.03.19 |