jotai 와 zustand, redux 등등 스토어들의 상태관리 멘탈모델을 비교해보자
1. jotai & recoil 과 같은 원자단위 상태
- jotai에서 지향하는 'primitive atom'은 뭘까?
- 상태 관리에 있어서 가장 기본적이고 작은 단위(원자)를 사용함
- 필요한 부분만 업데이트/구독 가능함
- 상태 간의 의존성을 명확하게 표현 가능함
- 상태간의 의존성이 명확하다는건? 즉, 상태간의 의존성 체인이 명확하다는 것
- 각 atom 이 어느 atom 에 의존적인지 코드상으로 명확함
// 기본 상태 atoms
const priceAtom = atom(100); // 가격
const quantityAtom = atom(2); // 수량
const taxRateAtom = atom(0.1); // 세율
// 의존성이 있는 파생 상태
const subtotalAtom = atom( // 소계
(get) => get(priceAtom) * get(quantityAtom)
);
const taxAtom = atom( // 세금
(get) => get(subtotalAtom) * get(taxRateAtom)
);
const totalAtom = atom( // 총액
(get) => get(subtotalAtom) + get(taxAtom)
);
그런데 만약 이걸 리덕스로 구현한다면, 리듀서로 개선하거나 selector 로 계산해주어야함
즉, jotai 는 의존성이 명시적임 (get함수를 사용하여 각 atom 들이 어떤 atom 을 참조하는지 바로 알 수 있음)
atom 들은 무엇으로 구분되는가?
- 리코일은 명시적으로 key 를 붙여줘야하고, 이 때 key 가 중복되면 안됨.
- 조타이는 atom 생성 자체가 유니크한 식별자가 됨.
2. redux, zustand 와 같은 단일스토어 상태
리덕스의 경우 액션, 리듀서를 각각 정의하고 그걸 스토어에 등록해야한다.
// 1. Action 정의
const INCREMENT = 'INCREMENT';
const addTodo = (text) => ({
type: 'ADD_TODO',
payload: text
});
// 2. Reducer - 순수 함수
const reducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
default:
return state;
}
};
// 3. Store 생성
const store = createStore(reducer);
// 4. 사용
const Component = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<button onClick={() => dispatch({ type: INCREMENT })}>
{count}
</button>
);
};
- zustand 의 경우 상태와 액션을 한번에 정의하여 사용 할 수 있다.
// 1. Store 생성 - 상태와 액션을 한번에 정의
const useStore = create((set) => ({
count: 0,
todos: [],
increment: () => set((state) => ({
count: state.count + 1
})),
addTodo: (text) => set((state) => ({
todos: [...state.todos, text]
}))
}));
// 2. 사용
const Component = () => {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
return (
<button onClick={increment}>
{count}
</button>
);
};
- zustand 와 redux 결국 단일 스토어를 갖는다.
- 즉, 상태가 하나의 큰 객체 트리구조로 관리됨!
- 물론 zustand 는 여러개의 스토어를 만들 수는 있다.
- 따라서 상태 업에이트 를 할 때에도 트리기반으로 상태가 관리되고 있음으로, 전체 경로를 고려하여 상태 업데이트가 필요하다.
- 또한, redux에서 상태를 나누고싶을 때는 리듀서를 사용하여 나누게 됨
- 즉, 스토어는 하나지만 그 안의 상태 업데이트 로직들을 분리해서 관리하는 것.
트리기반이 왜 필요할까?
1차원적으로 봤을 때, 굳이 아톰단위로 나눠도 될 상태를 트리기반으로 나눠서 중앙에서 관리해야하는 이유가 뭘까? 아톰기반 상태관리 툴에 비해 트리기반 상태관리 툴의 이점은 뭘까?
- 트리기반은, 전체 상태 스냅샷을 확인하기 용이하다.
- 그리고, 모든 상태 변화를 한곳에서 추적 가능함!
- 그니까.. 데이터 흐름이 명확함! 반면 아톰은 사용하기 쉽고, 변경하기 간단하지만 이러한 데이터 흐름은 존재하지 않기 때문에 각 상태가 분산되긴 함