image

zustand

태그
React
상세설명zustand 사용법
작성일자2023.12.06

상태 관리 라이브러리 중 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' 등 다른 상태 관리 라이브러리들과도 쉽게 통합이 가능다.
  • 추가 설명

    Flux 구조

  • Flux는 페이스북이 애플리케이션의 복잡한 데이터 흐름을 관리하기 위해 만든 애플리케이션 아키텍처이다
  • React와 함께 사용되는 경우가 많으며, 단방향 데이터 흐름을 강조하는 것이 주요 특징이다
  • Flux는 MVC(Model-View-Controller) 패턴과는 다른 방식으로 애플리케이션의 상태를 관리하고, 데이터의 일관성을 유지하는 데 중점을 둔다.
  • boilerplate 코드

    "Boilerplate 코드"는 개발할 때 자주 반복적으로 작성해야 하는 기본적인 코드 템플릿을 의미한다. 이 코드는 주로 설정, 초기화, 기본적인 구조 등을 포함하며, 특정 작업을 수행하기 위해 반드시 작성해야 하지만, 대부분의 경우 변경 없이 그대로 사용되는 경우가 많다.

    => 예를 들어, Redux에서는 액션(action), 리듀서(reducer), 스토어(store) 설정 등에서 많은 boilerplate 코드가 필요하다. Zustand와 같은 라이브러리들은 이러한 반복적인 코드 작성 없이 간단하게 상태 관리를 시작할 수 있도록 도와준다

    => Boilerplate 코드의 장점으로는 개발자가 반복적인 작업이나 하드 코딩에 시간을 낭비하지 않고, 중요한 로직이나 기능 구현에 집중할 수 있도록 해준다는 점이 있습니다. 예를 들어, create-react-app을 활용하면 미리 갖춰진 기본 구조를 제공받아, 개발자는 그 위에 필요한 기능을 추가하여 빠르게 개발을 진행할 수 있습니다.

    image

    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>
    		</>
    	)
    }

    결과

    image

    만약 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
        }
      )
    )

    참고