여태 진행했던 프로젝트들에선 zustand로만 전역 상태 관리를 했었다.
const { setSelectedTheme, selectedThemeType } = useIngameThemeStore()
그리고 이런 식으로 아주 간단하게 사용하고 있었으나... 최적화를 할 수 있는 방법을 우연찮게 알아냈다. 이걸왜이제서야
바로 Selector와 shallow의 조합이다.
import { shallow } from "zustand/shallow";
const { setSelectedTheme, selectedThemeType } = useIngameThemeStore(
(state) => ({
setSelectedTheme: state.setSelectedTheme,
selectedThemeType: state.selectedThemeType,
}),
shallow
);
먼저 결과 예시부터 보여주면 이런 식으로 사용하는 것이다. 코드가 뭐가 많이 추가되었다. (타입선언은 생략)
이렇게 Selector와 shallow의 조합을 활용하면 동작 방식에 차이가 생긴다.
❓ Selector
- (state) => ({ ... }) 부분에서 필요한 상태와 액션(위 예시에서는 setSelectedTheme, selectedThemeType)만 선택적으로 구독한다.
- 같은 스토어의 다른 상태가 변경되더라도 선택한 값이 변경되지 않으면 리렌더링이 발생하지 않는다.
- 위 예시를 사용하면 useIngameThemeStore에 themeIdx라는 변수가 있을 때, themeIdx가 다른 컴포넌트에서 변경됐을 시에도 위 컴포넌트를 리렌더링한다. (!!)
- 성능 최적화가 부족하지만 코드가 간결하다는 장점이 있다.
❓ shallow
- Zustand는 기본적으로 Selector가 반환한 객체를 참조 동등성으로 비교한다.
- shallow를 사용하면 Selector가 반환한 객체의 내부 속성을 얕은 비교하여, 선택된 값들이 모두 동일한 경우 리렌더링을 방지한다.
🤔 그럼 Selector만 사용하고 shallow를 사용하지 않으면 어떻게 될까?
- Selector가 객체를 반환하면 매번 새로운 객체가 생성되므로, 내부 값이 동일하더라도 참조가 달라져 리렌더링이 발생할 수 있다.
코드가 너무 복잡해지고 별로인데... -> 💡 커스텀 훅을 활용하자!
const useSelectedTheme = () =>
useIngameThemeStore(
(state) => ({
setSelectedTheme: state.setSelectedTheme,
selectedThemeType: state.selectedThemeType,
}),
shallow
);
const { setSelectedTheme, selectedThemeType } = useSelectedTheme();
이런 식으로 코드의 가독성과 재사용성, 성능 최적화까지 챙길 수 있다👍 애용하자!
'React' 카테고리의 다른 글
[React] 리액트의 폴더구조 (0) | 2025.03.31 |
---|---|
[React] Fragment (0) | 2025.03.31 |
[React] useMemo, useCallback (+캐싱) (0) | 2025.03.30 |
[React] useCallback (0) | 2025.03.27 |
[React-Query]prefetchQuery (0) | 2025.02.25 |