상태 관리 라이브러리 중 zustand를 종종 사용해 정리하고자 한다.
리액트에서의 전역 상태 관리
전역 상태 관리란 앱의 중앙에 위치함으로써 어느 컴포넌트든 이 데이터(상태)에 접근할 수 있으며 상태 변경이 가능하다.
전역 상태관리를 지원하는 api에는 react에 내장된 useContext 훅을 포함하여 redux, recoil, zustand, zotai, react-query 등 다양한 서드파티 라이브러리들까지 존재한다.
zustand 란 ?
Zustand는 작은 용량(1.16kb의 번들 사이즈), 빠른 속도, 그리고 확장 가능성을 자랑하는 상태 관리 라이브러리입니다. 단방향 데이터 흐름(Flux패턴과 유사), React hooks 기반으로 설계되어 있어, 복잡한 보일러플레이트 코드( boilerplate 코드 ) 없이도 간단하게 상태를 관리할 수 있습니다.
- zustand는 작고 빠르며 확장 가능한 React 프로젝트에서 사용하는 상태 관리(Store) 라이브러리
- 간단한 사용: 직관적이고 간단한 API로 쉽게 상태 관리를 시작할 수 있다
- Async 지원: 비동기 작업을 쉽게 처리할 수 있다
- 함수 기반 상태 업데이트: Recoil과 달리, Zustand에서는 Store 내에서 상태를 업데이트하는 함수를 직접 정의할 수 있습니다. 이를 통해 복잡한 로직을 Store 안에 캡슐화하여 사용할 수 있다.
- Middleware: immer를 사용해 복잡한 객체 업데이트를 단순화할 수 있고, persist를 통해 상태를 로컬 스토리지에 저장하는 등 다양한 미들웨어를 지원한다
- 유연한 통합: 'Redux'와 'React Context API' 등 다른 상태 관리 라이브러리들과도 쉽게 통합이 가능다.
/* 추가 설명 */
* boilerplate 코드
"Boilerplate 코드"는 개발할 때 자주 반복적으로 작성해야 하는 기본적인 코드 템플릿을 의미한다. 이 코드는 주로 설정, 초기화, 기본적인 구조 등을 포함하며, 특정 작업을 수행하기 위해 반드시 작성해야 하지만, 대부분의 경우 변경 없이 그대로 사용되는 경우가 많다.
=> 예를 들어, Redux에서는 액션(action), 리듀서(reducer), 스토어(store) 설정 등에서 많은 boilerplate 코드가 필요하다. Zustand와 같은 라이브러리들은 이러한 반복적인 코드 작성 없이 간단하게 상태 관리를 시작할 수 있도록 도와줍니다.
=> Boilerplate 코드의 장점으로는 개발자가 반복적인 작업이나 하드 코딩에 시간을 낭비하지 않고, 중요한 로직이나 기능 구현에 집중할 수 있도록 해준다는 점이 있습니다. 예를 들어, create-react-app을 활용하면 미리 갖춰진 기본 구조를 제공받아, 개발자는 그 위에 필요한 기능을 추가하여 빠르게 개발을 진행할 수 있습니다.
* Zustand와 Flux패턴
단방향 데이터 흐름때문에 zustand가 flux 패턴인가 에대해 의문이있어서 알아보았습니다.
Flux 패턴 관련글 : https://minsun309.tistory.com/entry/Flux-%ED%8C%A8%ED%84%B4
공통점
단방향 데이터 흐름: Zustand와 Flux 모두 데이터 흐름이 한 방향으로만 진행됩니다. 상태가 업데이트되면 그 상태를 사용하는 컴포넌트가 재렌더링됩니다.
차이점
Flux | Zustand | |
구조 | 구성 요소
|
flux 구조적 요소를 사용X
|
복잡성 | 전체적인 데이터 흐름을 중앙에서 관리하는 Dispatcher와 여러 Store를 사용하므로 상대적으로 복잡할 수 있습니다. 액션이 발생하면 모든 Store가 액션을 처리하고, 상태가 변경된 후 View를 업데이트하는 방식입니다. |
상태 저장소와 React Hooks를 사용하여 상태 관리가 매우 간단하고 직관적입니다. 별도의 Dispatcher나 복잡한 구조 없이 상태를 정의하고 관리할 수 있습니다. |
보일러플레이트 | Action, Dispatcher, Store 등의 구성 요소를 정의해야 하며, 이는 보일러플레이트 코드를 요구합니다. |
상태 저장소를 정의하고, Hook을 통해 상태를 가져오고 업데이트하는 방식이므로 보일러플레이트 코드가 거의 없습니다. |
결론
Zustand는 단방향 데이터 흐름이라는 개념을 공유하지만, Flux 패턴의 모든 요소를 사용하지는 않습니다. Zustand는 보다 간단하고 직관적인 상태 관리 방법을 제공하며, Flux의 복잡한 구조 없이 상태를 효과적으로 관리할 수 있게 해줍니다. 그래서 Zustand는 Flux와 유사한 부분이 있지만, Flux 패턴의 전체적인 구조와는 다릅니다.
Npm Trend
아직 redux에 비해 적지만 Zustand 사용량이 올라가고있다.
zustand 사용법
설치
npm install zustand
//or
yarn add zustand
공식 홈
상태 관리
아래 zustand는 토글 상태를 공유하기 위한 코드이다.
//store/helpStore.ts
import { create } from "zustand";
interface HelpState {
showHelp: boolean;
toggleShowHelp: () => void;
}
export const helpStore = create<HelpState>((set) => ({
showHelp: false,
toggleShowHelp: () => set((state) => ({ showHelp: !state.showHelp })),
}));
생성된 상태를 필요한 곳에 불러다가 사용하면 된다.
import { helpStore } from "@/store/helpStore";
export default function Test() {
const { showHelp, toggleShowHelp } = helpStore();
return (
<>
<button onClick={toggleShowHelp} style={{ backgroundColor: "#4072ee" }}>
도움말
</button>
{showHelp && (
<div css={selectHelpArea}>
<div className="ment">
<p>도움이 필요한 영역을 선택하세요.</p>
<div onClick={toggleShowHelp} className="close">
닫기
</div>
</div>
</div>
)}
</>
);
}
localstorage에 저장하는 방법
zustand에서는 Persist middleware을 이용해 state를 storage에 저장할 수 있다.
아래 zustand는 블로그에서 포스트 뷰 스타일과 순서를 저장하고 있다.
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface PageStore {
viewStyle: string;
sortedContent: string;
setViewStyle: (style: string) => void;
setSortedContent: (content: string) => void;
}
const createPageStore = (name: string) =>
create(
persist<PageStore>(
(set) => ({
viewStyle: "gallery",
sortedContent: "latest",
setViewStyle: (style: string) => set({ viewStyle: style }),
setSortedContent: (content: string) => set({ sortedContent: content }),
}),
{ name: `${name}_page_state` }
)
);
export const useBlogPageStore = createPageStore("blog");
export const useProjectPageStore = createPageStore("project");
생성된 상태를 필요한 곳에 불러다가 사용하면 된다.
import { useBlogPageStore, useProjectPageStore } from "@/store/pageStore";
export default function Test() {
const { viewStyle, sortedContent, setViewStyle, setSortedContent } = getStore;
return (
<>
<div>
<p>레이아웃</p>
<div onClick={() => setViewStyle("gallery")}>
<span>갤러리</span>
</div>
<div onClick={() => setViewStyle("list")}>
<span>리스트</span>
</div>
</div>
<div>
<p>정렬</p>
<div onClick={() => setSortedContent("latest")}>
<span>최신순</span>
</div>
<div onClick={() => setSortedContent("registration")}>
<span>등록일순</span>
</div>
</div>
</>
);
}
결과
만약 localStorage외에 다른 storage에 저장하고 싶으면 storage: createJSONStorage(() => sessionStorage) 추가하면 된다.
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
export const useBearStore = create(
persist(
(set, get) => ({
bears: 0,
addABear: () => set({ bears: get().bears + 1 }),
}),
{
name: 'food-storage', // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
}
)
)
'React' 카테고리의 다른 글
모바일 메뉴 오픈 시 스크롤 방지 (0) | 2024.08.25 |
---|---|
react-highlight (0) | 2024.08.25 |
React에서 input 관리 (0) | 2024.08.24 |
swr 기본 사용법 (0) | 2024.08.24 |
ExcelJS 다운로드 & 엑셀 파일 업로드 (0) | 2024.08.24 |