๐Ÿ“ฐ Front-end/์ƒํƒœ๊ด€๋ฆฌ

[Zustand] Zustand๋กœ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•ด๋ณด์ž!

s2ylvia 2024. 6. 8. 14:31

 

๋งŽ์€ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ์š”์ฆ˜ ๋– ์˜ค๋ฅด๋Š” 'Zustand'.

 

 

npm trends ์‚ฌ์ดํŠธ๋ฅผ ๊ฐ€์„œ ๊ฒ€์ƒ‰์„ ํ•ด๋ณด๋‹ˆ ์—ญ์‹œ 1์œ„๋Š” redux์ง€๋งŒ ๋†€๋ž๊ฒŒ๋„ 2์œ„๊ฐ€ Zustand๋‹ค.

2๋…„์ „๋งŒํ•ด๋„ recoil์ด ๋ง‰ ๋œฌ๋‹ค. ๋ผ๋Š” ์†Œ๋ฌธ์ด ์žˆ์–ด์„œ ์‚ฌ์šฉํ•ด๋ดค๋Š”๋ฐ... ์ด์   ์ € ๋ฐ‘์— ์žˆ๋„ค..

(recoil ๋งŒ๋“œ์‹  ๊ฐœ๋ฐœ์ž๋ถ„๋„ ํ‡ด์‚ฌํ–ˆ๋‹ค๋Š” ์†Œ์‹์ด......)

 

 

๊ทธ๋ž˜์„œ Zustand๊ฐ€ ์™œ ์ธ๊ธฐ๊ฐ€ ์žˆ๋Š”๋ฐ? ๋ฌด์Šจ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๋ฐ?

Zustand๋Š” ๋…์ผ์–ด๋กœ '์ƒํƒœ'๋ผ๋Š” ๋œป์„ ๊ฐ€์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ,

๊ฐ„๊ฒฐํ•œ ํ”Œ๋Ÿญ์Šค(Flux) ์›์น™์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘๊ณ  ๋น ๋ฅด๊ฒŒ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

ํ•œ ๊ฐœ์˜ ์ค‘์•™์— ์ง‘์ค‘๋œ ํ˜•์‹์˜ ์Šคํ† ์–ด ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•˜๊ณ  ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์ตœ์†Œํ™”๋˜์–ด ์žˆ์–ด ์Šคํ† ์–ด ํ˜•ํƒœ์ž„์—๋„ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋˜ํ•œ,

  • useSelector hook์„ ์ œ๊ณตํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊ตฌ๋… => Provider๋กœ ๊ฐ์Œ€ ํ•„์š” X
  • memoization์„ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ปดํฌ๋„ŒํŠธ ์žฌ์‹คํ–‰์„ ๋ฐฉ์ง€ => Context API๋ณด๋‹ค ๋ฆฌ๋ Œ๋”๋ง ๊ฐ์†Œ

๊ทธ๋ ‡๋‹ค.

redux๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋ฉด ๋งŽ์€ ์žฅ์ ์ด ์žˆ์ง€๋งŒ ์ ์–ด์•ผํ•˜๋Š” ์ˆ˜๋งŽ์€ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ์— ๋‹ค์†Œ ํ”ผ๋กœ๊ฐ์„ ๋А๋ผ๊ฒŒ ๋œ๋‹ค.

๊ทธ๋Ÿฐ ๋‹จ์ ์„ ๋ณด์™„ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์ด Zustand์ธ ๊ฒƒ๊ฐ™๋‹ค.

 

 

Zustand๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž

์„ค์น˜๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค.

# NPM
npm install zustand
# Or, use any package manager of your choice.

 

์„ค์น˜๋ฅผ ํ•˜๋ฉด store.js ํŒŒ์ผ์„ ์ƒ์„ฑ ํ›„, create ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด store๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

import { create } from 'zustand'

const useStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
  updateBears: (newBears) => set({ bears: newBears }),
}))

 

์Šคํ† ์–ด๋Š” ์ƒํƒœ ๋ณ€์ˆ˜์™€ ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์•ก์…˜(ํ•จ์ˆ˜)์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ์ƒํƒœ ๋ณ€์ˆ˜์™€ ์•ก์…˜(ํ•จ์ˆ˜)๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์—์„œ useStore ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค.

import { useStore } from "./store/store";

function BearCounter() {
  const bears = useStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

function Controls() {
  const increasePopulation = useStore((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

 

 

Zustand๋ฅผ ํ™œ์šฉํ•œ Todo App

 

store.ts

import { create } from "zustand";

interface TodosType {
  id: number;
  text: string;
  isDisabled: boolean;
  isChecked: boolean;
}

interface Store {
  Todos: TodosType[];
  addTodo: (todo: string) => void;
  remove: (id: number) => void;
  updateDisabled: (id: number, disabled: boolean) => void;
  updateChecked: (id: number, check: boolean) => void;
  update: (id: number, newText: string) => void;
}

//store ์ƒ์„ฑ
export const useStore = create<Store>((set) => ({
  Todos: [{ id: 0, text: "๊ณต๋ถ€ํ•˜๊ธฐ", isDisabled: true, isChecked: false }],
  addTodo: (todo: string) =>
    set((state) => {
      const lastId = state.Todos.length > 0 ? state.Todos[0].id : 0;
      const newId = lastId + 1;
      const newTodo = { id: newId, text: todo, isDisabled: true };
      return { Todos: [newTodo, ...state.Todos] };
    }),
  remove: (id: number) =>
    set((state) => ({ Todos: state.Todos.filter((v, i) => v.id !== id) })),
  updateDisabled: (id: number, disabled: boolean) =>
    set((state) => ({
      Todos: state.Todos.map((todo) =>
        todo.id === id ? { ...todo, isDisabled: disabled } : todo
      ),
    })),
  update: (id: number, newText: string) =>
    set((state) => ({
      Todos: state.Todos.map((todo) =>
        todo.id === id ? { ...todo, text: newText } : todo
      ),
    })),
  updateChecked: (id: number, check: boolean) =>
    set((state) => ({
      Todos: state.Todos.map((todo) =>
        todo.id === id ? { ...todo, isChecked: check } : todo
      ),
    })),
}));

 

page.tsx

"use client";

import { useStore } from "./store/store";
import { useState } from "react";
import {
  MdOutlineCheckBoxOutlineBlank,
  MdOutlineCheckBox,
  MdRocketLaunch,
  MdCheck,
  MdDelete,
  MdEdit,
} from "react-icons/md";

export default function Home() {
  const [todo, setTodo] = useState("");
  const { Todos, addTodo, remove, updateDisabled, update, updateChecked } =
    useStore((state) => state);

  const handleAdd = () => {
    if (!todo) return alert("Please enter a todo");
    addTodo(todo);
    setTodo("");
  };

  return (
    <main>
      <div className="w-full bg-indigo-50 text-center min-h-screen">
        <h1 className="text-5xl font-bold pt-10 pb-5">Hello Todo App!</h1>
        <div className="p-10 flex items-center">
          <input
            className="w-5/6 placeholder:text-gray-400 border-0 p-5 rounded-l-lg focus:outline-none"
            placeholder="your todo here"
            value={todo}
            onChange={(e) => setTodo(e.target.value)}
          />
          <button
            className="w-1/6 bg-indigo-200 rounded-r-lg p-5"
            onClick={handleAdd}
          >
            <div className="flex items-center justify-center">
              <MdRocketLaunch size="24" />
            </div>
          </button>
        </div>

        <div className="px-10">
          {Todos.map((todo, index) => (
            <div className="flex pt-5" key={todo.id}>
              <div className="flex-none mr-2 content-center">
                {todo.isChecked ? (
                  <button onClick={() => updateChecked(todo.id, false)}>
                    <MdOutlineCheckBox size="30" />
                  </button>
                ) : (
                  <button onClick={() => updateChecked(todo.id, true)}>
                    <MdOutlineCheckBoxOutlineBlank size="30" />
                  </button>
                )}
              </div>
              <input
                className="flex-1 placeholder:text-gray-400 border-0 p-5 rounded-lg focus:outline-none w-16 md:w-32 lg:w-48"
                disabled={todo.isDisabled}
                value={todo.text}
                onChange={(e) => update(todo.id, e.target.value)}
              />
              {todo.isDisabled ? (
                <button
                  className="flex-none mx-4 bg-indigo-200 p-5 rounded-lg"
                  onClick={() => updateDisabled(todo.id, false)}
                >
                  <div className="flex items-center justify-center">
                    <MdEdit size="30" />
                  </div>
                </button>
              ) : (
                <button
                  className="flex-none mx-4 bg-indigo-200 p-5 rounded-lg"
                  onClick={() => updateDisabled(todo.id, true)}
                >
                  <div className="flex items-center justify-center">
                    <MdCheck size="30" />
                  </div>
                </button>
              )}
              <button
                className="flex-none bg-indigo-200 p-5 rounded-lg"
                onClick={() => remove(todo.id)}
              >
                <div className="flex items-center justify-center">
                  <MdDelete size="30" />
                </div>
              </button>
            </div>
          ))}
        </div>
      </div>
    </main>
  );
}

 

๊ฐ„๋‹จํ•˜๊ฒŒ Zustand์˜ ์‚ฌ์šฉ๋ฒ•์„ ์ตํžˆ๊ธฐ ์œ„ํ•ด todo App์„ ๋งŒ๋“ค์–ด๋ดค๋Š”๋ฐ,

(๋„ˆ๋ฌด ์ž‘์€ ํ† ์ดํ”„๋กœ์ ํŠธ๋ผ์„œ ์•„์ง ์ž˜ ์•„๋Š”๊ฑด ์•„๋‹ˆ์ง€๋งŒ........)

ํ™•์‹คํžˆ ๋‹ค๋ฅธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณด๋‹ค ์‚ฌ์šฉํ•˜๋Š” ๋ฒ•๋„ ๊ฐ„๋‹จํ•˜๊ณ  ํ”„์—”์„ ์ข€ ๋‹ค๋ฃจ๋Š” ์‚ฌ๋žŒ์ด๋ผ๋ฉด ์‰ฝ๊ฒŒ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

์•ž์œผ๋กœ Zustand๋ฅผ ์‚ฌ์šฉํ•œ ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ด์•ผ๊ฒ ๋‹ค.

 

 

 

 

 

์ฐธ๊ณ 

Zustand Documentation (pmnd.rs)

 

Zustand Documentation

Zustand is a small, fast and scalable bearbones state-management solution, it has a comfy api based on hooks

docs.pmnd.rs

[React] Zustand ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌํ•˜๊ธฐ (JS/TS) (velog.io)

 

[React] Zustand ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌํ•˜๊ธฐ (JS/TS)

๋ฆฌ์•กํŠธ์—์„œ zustand ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ์ƒํƒœ๊ด€๋ฆฌํ•˜๋Š” ๋ฒ•์„ ์•Œ์•„๋ณด์ž!

velog.io

ํ›„๋‹ˆ๋„ค ๊ฐœ๋ฐœํ•˜์šฐ์Šค | ๋„Œ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด? ๋‚œ Zustand๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์žˆ์–ด! (hooninedev.com)