Zustand์˜ Slice ํŒจํ„ด

๐Ÿ’ป ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ Zustand ์‚ฌ์šฉ ๋ฐฉ์‹

zustand๋Š” React ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ฒฝ๋Ÿ‰ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. zustand๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , localStorage์— ์ƒํƒœ๋ฅผ ์ง€์†(persist)ํ•˜๋„๋ก ์„ค์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ์˜ zustand ๊ตฌ์กฐ๋Š” v4๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ๊ทผ(24๋…„ 10์›” 14์ผ) v5๊ฐ€ ๋‚˜์™”์ง€๋งŒ, ์•„์ง ์ต์ˆ™ํ•˜์ง€ ์•Š์•„์„œ ์ ์šฉ ์‹œํ‚ค์ง€๋Š” ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ๋ธ”๋กœ๊ทธ๊ธ€์ด ์žˆ์–ด ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค (๋ธ”๋กœ๊ทธ)

ํ˜„์žฌ ํŒจํ„ด์€ ์Šฌ๋ผ์ด์Šค(Slice) ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Zustand์˜ ์Šฌ๋ผ์ด์Šค(slice) ํŒจํ„ด์€ ์ƒํƒœ(store)๋ฅผ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

zustand

1. ์Šฌ๋ผ์ด์Šค ํŒจํ„ด์ด ํ•„์š”ํ•œ ์ด์œ 

Zustand๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ƒํƒœ๊ฐ€ ์ปค์ง€๋ฉด ํ•˜๋‚˜์˜ ํฐ create ์Šคํ† ์–ด์— ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์Šฌ๋ผ์ด์Šค ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๊ธฐ๋Šฅ(์˜ˆ: ์œ ์ € ์ƒํƒœ, ํ…Œ๋งˆ ์ƒํƒœ ๋“ฑ)๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โœ… ์žฅ์ 

  • ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ์ฆ๊ฐ€: ์ƒํƒœ๋ฅผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Œ
  • ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ: ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜์ •ํ•  ๋•Œ ๋‹ค๋ฅธ ์ƒํƒœ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ
  • ๋ชจ๋“ˆํ™” ๊ฐ€๋Šฅ: ์ƒํƒœ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ •์˜ํ•˜์—ฌ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ

2. ์Šฌ๋ผ์ด์Šค(Slice) ๋ถ„๋ฆฌํ•˜๊ธฐ

๊ฐ ์ƒํƒœ๋ฅผ ๊ฐœ๋ณ„์ ์ธ **์Šฌ๋ผ์ด์Šค(slice)**๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

1๏ธโƒฃ ์Šฌ๋ผ์ด์Šค ์ •์˜

// counterSlice.ts
import { StateCreator } from 'zustand';

export interface CounterSlice {
  count: number;
  increase: () => void;
  decrease: () => void;
}

export const createCounterSlice: StateCreator<
  CounterSlice,
  [],
  [],
  CounterSlice
> = (set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
});
  • CounterSlice ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜์—ฌ ์ƒํƒœ์™€ ์•ก์…˜์„ ํƒ€์ž…์œผ๋กœ ๋ช…์‹œ
  • createCounterSlice ํ•จ์ˆ˜์—์„œ set์„ ์ด์šฉํ•ด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„

2๏ธโƒฃ ์—ฌ๋Ÿฌ ์Šฌ๋ผ์ด์Šค ํ•ฉ์น˜๊ธฐ

// useStore.ts
import { create } from 'zustand';
import { CounterSlice, createCounterSlice } from './counterSlice';
import { ThemeSlice, createThemeSlice } from './themeSlice';

type Store = CounterSlice & ThemeSlice; //์—ฌ๋Ÿฌ๊ฐœ์˜ slice ํ†ตํ•ฉ

const useStore = create<Store>()((...a) => ({
  ...createCounterSlice(...a),
  ...createThemeSlice(...a),
}));

export default useStore;
  • create ํ•จ์ˆ˜์—์„œ ์—ฌ๋Ÿฌ ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ณ‘ํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์Šคํ† ์–ด๋กœ ์ƒ์„ฑ
  • ...createCounterSlice(...a)๋ฅผ ํ†ตํ•ด ์ƒํƒœ๋ฅผ ๊ฒฐํ•ฉ

3. ์ •๋ฆฌ

โœ… ์Šฌ๋ผ์ด์Šค ํŒจํ„ด์˜ ํ•ต์‹ฌ ๊ฐœ๋…

  • ๊ธฐ๋Šฅ๋ณ„๋กœ ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ž„
  • ์—ฌ๋Ÿฌ ์Šฌ๋ผ์ด์Šค๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ƒํƒœ๋กœ ํ†ตํ•ฉ
  • Zustand์˜ StateCreator๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์Šฌ๋ผ์ด์Šค ํƒ€์ž…์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌ

โœ… ์–ธ์ œ ์‚ฌ์šฉํ• ๊นŒ?

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ๊ฐ€ ์ ์  ์ปค์งˆ ๋•Œ
  • ์—ฌ๋Ÿฌ ์ƒํƒœ(์˜ˆ: user, theme, cart ๋“ฑ)๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ๋•Œ
  • ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ

๋Œ“๊ธ€