diff --git a/Aug/article/React-State-Management-using-Zustand.md b/Aug/article/React-State-Management-using-Zustand.md new file mode 100644 index 0000000..d5b31a9 --- /dev/null +++ b/Aug/article/React-State-Management-using-Zustand.md @@ -0,0 +1,385 @@ +## π [React State Management β using Zustand](https://medium.com/globant/react-state-management-b0c81e0cbbf3) + +### ποΈ λ²μ λ μ§: 2024.08.19 + +### π§ λ²μν ν¬λ£¨: μν(μ΅μμ°) + +--- + + + +λΉμ μ μν(state)μ μν κ΄λ¦¬(state management)κ° React μ ν리μΌμ΄μ μ μ€μν λΆλΆμ΄λΌκ³ μκ°νμλμ? μν κ΄λ¦¬λ₯Ό μ΄λ €μν΄λ³Έ μ μ΄ μκ±°λ κ°λ¨ν μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ°Ύκ³ μμλ€λ©΄, μ΄ κΈμ΄ λΉμ μ μν κ²μ λλ€. μ΄ κΈμμλ κ°λ¨ν μΈλΆ λΌμ΄λΈλ¬λ¦¬μΈ Zustandλ₯Ό μ¬μ©νμ¬ React μνλ₯Ό μ΄λ»κ² λ€λ£° μ μλμ§ λ³΄μ¬μ€ κ²μ λλ€. + +μ λ§λ‘! μνμ μν κ΄λ¦¬λ νμ React μ ν리μΌμ΄μ μ μ€μν μΈ‘λ©΄μ΄μμ΅λλ€. Reactλ μνκ° λ³κ²½λ λλ§ λ€μ λ λλ§νκΈ° λλ¬Έμ λλ€. μνλ μ»΄ν¬λνΈμ λν λ°μ΄ν°λ μ 보λ₯Ό ν¬ν¨ν μ μμ΅λλ€. μ ν리μΌμ΄μ μ΄ μ±μ₯ν¨μ λ°λΌ μνμ μ»΄ν¬λνΈ κ°μ λ°μ΄ν° νλ¦μ κ΄λ¦¬νλ κ²μ΄ λ§€μ° μ€μν΄μ§λλ€. + +### React μν κ΄λ¦¬ + +React μ ν리μΌμ΄μ μ μνλ₯Ό κ΄λ¦¬νλ λ°©λ²μλ μ¬λ¬ κ°μ§κ° μμ΅λλ€. + +1. **React λ€μ΄ν°λΈ μν κ΄λ¦¬.** useState, useReducer, useRef, useContextμ κ°μ ν (hooks)κ³Ό μ¬μ©μ μ μ ν (custom hooks)μ λ€μ΄ν°λΈ μν κ΄λ¦¬λ₯Ό μ§μν©λλ€. +2. **κ°μ μν κ΄λ¦¬μ.** React Routerμ React Queryμ κ°μ μΈλΆ λΌμ΄λΈλ¬λ¦¬λ€μ΄ μμ΅λλ€. μ΄λ€μ μ£Όλ‘ μν κ΄λ¦¬λ₯Ό μν΄ μ¬μ©λμ§ μμ§λ§, λ€μ΄ν°λΈ ν κ³Ό κ²°ν©νλ©΄ μνλ₯Ό μ κ΄λ¦¬ν μ μμ΅λλ€. +3. **μ§μ μν κ΄λ¦¬μ.** μν κ΄λ¦¬λ₯Ό μν΄μλ§ μ¬μ©λλ μλνν° λΌμ΄λΈλ¬λ¦¬λ μμ΅λλ€. Redux, Zustand, Jotai, Valtioκ° μ΄ λ²μ£Όμ μν©λλ€. + +### Zustandλ 무μμΈκ°μ? + +Zustandλ μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬μ λλ€. μκ³ λΉ λ₯΄λ©° νμ₯μ±μ΄ μμ΅λλ€. μ΄λ κ°λ¨ν μμ€ν μΌλ‘, 보μΌλ¬νλ μ΄νΈ μ½λκ° κ±°μ μμ΅λλ€. Redux λ° μ μ¬ν λΌμ΄λΈλ¬λ¦¬λ³΄λ€ μ μ μ½λλ‘ React μνλ₯Ό κ΄λ¦¬ν μ μμ΅λλ€. Zustandλ providerμ μμ‘΄νμ§ μκΈ° λλ¬Έμ, React λ‘μ§μ λ μμ±ν΄λ λλ©°, μ°λ¦¬κ° μ’ μ’ μκΈ° μ¬μ΄ λΆλΆλ€μ μ€μΌ μ μμ΅λλ€. μ΄λ κ°μνλ flux μμΉμ κΈ°λ°μΌλ‘ μλνλ©°, μ£Όλ‘ Hookμ μ¬μ©ν©λλ€. + +μ Zustandλ₯Ό μ νν΄μΌ ν κΉμ? + +- Zustandλ contextλ³΄λ€ λΉ λ¦ λλ€. νΉμ μνλ₯Ό μ νν μ μλ μ΅μ μ μ 곡ν©λλ€. +- μν λ³ν©μ΄ κΈ°λ³Έμ μΌλ‘ μ§μλ©λλ€. κ°μ²΄ μν `{x:1, y:2}`μ λ¨μΌ μμ±μ μ λ°μ΄νΈνλ€κ³ κ°μ ν΄λ΄ μλ€. `{y:3}`μΌλ‘ μ§μ μ€μ ν μ μμ΅λλ€. Zustandλ λ°μ΄ν°λ₯Ό μλμΌλ‘ λ³ν©ν΄ μ€λλ€. κΈ°μ‘΄ μνλ₯Ό λΆλ°°νκ³ `{β¦state, y:3}`μ²λΌ μμ±μ μ λ°μ΄νΈν νμκ° μμ΅λλ€. +- Zustandλ κΈ°λ³Έμ μΌλ‘ νμ₯ κ°λ₯ν©λλ€. λ°λΌμ λ€μν λ―Έλ€μ¨μ΄ μ νμ μ¬μ©ν μ μμ΅λλ€. +- Zustandλ νΉμ λ°©μμ μ½λ§€μ΄μ§ μμ΅λλ€. κΆμ₯λλ μ κ·Ό λ°©μμ΄ μλλΌλ, κ·Έκ²μ λ°λμ μ±νν νμλ μμ΅λλ€. + +### Zustand vs. Redux + +μμλ€μνΌ, Reduxλ Reactμ ν¨κ» μλνλ κ³ μ μ μΈ μν κ΄λ¦¬ λΌμ΄λΈλ¬λ¦¬μ λλ€. μ΄λ React μν κ΄λ¦¬λ₯Ό μν κ°μ₯ μΈκΈ° μλ λΌμ΄λΈλ¬λ¦¬λ‘ κ°μ£Όλ©λλ€. λ°λΌμ μ΄ λ λΌμ΄λΈλ¬λ¦¬μ μν€ν μ²λ₯Ό λΉκ΅νλ κ²μ΄ μ΄ κΈμ μ ν©ν μμκ° λ©λλ€. μλμ μν€ν μ²λ₯Ό μ΄ν΄λ³΄λ©΄μ Reduxκ° μ΄λ»κ² μλνλμ§ νμΈν΄λ³΄μΈμ. + + + +λ¨Όμ , μμ μ€λͺ λ μν€ν μ²μμ λ³Ό μ μλ―μ΄ νλ‘ νΈμλ μ¬μ©μ μΈν°νμ΄μ€κ° μμ΅λλ€. action creatorsλ κ° μ¬μ©μ μμ²μ λν΄ μ¬λ°λ₯Έ μ‘μ μ΄ νΈλ¦¬κ±°λλλ‘ λ³΄μ₯ν©λλ€. μ‘μ μ μ ν리μΌμ΄μ μμ μ΄λ€ μΌμ΄ λ°μνλμ§λ₯Ό μ€λͺ νλ μ΄λ²€νΈλΌκ³ μκ°ν μ μμ΅λλ€. λ²νΌ ν΄λ¦μ΄λ κ²μ μν κ°μ κ²μ΄ μ΄μ ν΄λΉν μ μμ΅λλ€. dispatchersλ μ΄λ¬ν μ‘μ μ storeλ‘ λ³΄λ΄λ λ° λμμ μ€λλ€. μ΄ν reducersλ μνλ₯Ό μ΄λ»κ² μ²λ¦¬ν μ§ κ²°μ ν©λλ€. reducer ν¨μλ νμ¬ μνμ μ‘μ κ°μ²΄λ₯Ό λ°μ μνλ₯Ό λ³κ²½ν©λλ€. νμμ λ°λΌ μλ‘μ΄ μνλ₯Ό λ°ννλ©°, μ λ°μ΄νΈλ μν μμ μ¬νμ΄ UIλ₯Ό λ λλ§νκ² λ©λλ€. + +μ΄μ ν₯λ―Έλ‘μ΄ λΆλΆμ΄ λ€κ°μ΅λλ€! μλμ μ 곡λ μν€ν μ²λ₯Ό ν΅ν΄ Zustandκ° μ΄λ»κ² μλνλμ§ μ΄ν΄λ³΄μΈμ. μ΄ λ¨μνλ λ€μ΄μ΄κ·Έλ¨μ ν₯λ―Έλ₯Ό λλ μλ μμ΅λλ€. + + + +μ¬κΈ°μμλ UI μ»΄ν¬λνΈκ° μμ΅λλ€. λ³κ²½ μμ²μ΄ λ€μ΄μ€λ©΄ κ·Έκ²μ storeλ‘ λΌμ°ν λ©λλ€. storeλ μνκ° μ΄λ»κ² λ³κ²½λμ΄μΌ νλμ§λ₯Ό κ²°μ ν©λλ€. storeκ° μλ‘μ΄ μνλ₯Ό λ°ννλ©΄, UIλ μ λ°μ΄νΈλ λ³κ²½ μ¬νκ³Ό ν¨κ» λ λλ§λ©λλ€. μ¬κΈ°μμλ action creator, dispatcher, reducerκ° λ³΄μ΄μ§ μμ΅λλ€. λμ , Zustandλ μν λ³κ²½μ ꡬλ ν μ μλ κΈ°λ₯μ μ 곡ν©λλ€. μ΄λ₯Ό ν΅ν΄ UIκ° λ°μ΄ν°μ λκΈ°ν μνλ₯Ό μ μ§νλ λ° λμμ΄ λ©λλ€. + +> λ ν¬κ³ 볡μ‘ν ν΄ν·μΈ Reduxμ 볡μ‘μ±μ νΌνλ©΄μλ κ°λ¨νκ³ κ°λ²Όμ΄ μν κ΄λ¦¬ μ루μ μ μ°Ύκ³ μλ κ°λ°μλ€μκ² Zustandλ μλ²½ν μ νμ λλ€. + +μ΄λ‘ μ μ¬κΈ°μ λμ λλ€. μ΄μ μ€μ΅μ μμν΄λ³΄μΈμ! + +### ReactJsμμ Zustand μ¬μ© λ°©λ² + +Zustandλ₯Ό μ¬μ©νμ¬ μνλ₯Ό κ΄λ¦¬νλ λ°©λ²μ 보μ¬μ£ΌκΈ° μν΄ React νλ‘μ νΈλ₯Ό λ§λ€μ΄ λ΄ μλ€. λμ λ°ν λ° λ°λ©μ μΆμ νλ Library Store μ ν리μΌμ΄μ μ μλ‘ λ€μ΄λ³΄κ² μ΅λλ€. λ¨κ³λ μλμ κ°μ΅λλ€. + +#### 1. React μ ν리μΌμ΄μ μμ± + +μλ λͺ λ Ήμ μ¬μ©νμ¬ React μ ν리μΌμ΄μ μ μμ±ν©λλ€. + +```bash +npx create-react-app project_name +``` + +#### 2. Zustand μμ‘΄μ± μ€μΉ + +νλ‘μ νΈ λλ ν λ¦¬λ‘ μ΄λνμ¬ React μν κ΄λ¦¬λ₯Ό μν΄ Zustand μμ‘΄μ±μ μ€μΉν©λλ€. + +```bash +npm i zustand +``` + +#### 3. store μμ± + +bookStore.js νμΌμ μμ±νκΈ° μν΄ μλ μ½λλ₯Ό μ°Έκ³ νμ¬ λμ storeλ₯Ό λ§λλλ€. + +```javascript +import { create } from 'zustand'; + +const bookStore = (set, get) => ({ + books: [], + noOfAvailable: 0, + noOfIssued: 0, + addBook: (book) => { + set((state) => ({ + books: [...state.books, { ...book, status: 'available' }], + noOfAvailable: state.noOfAvailable + 1, + })); + }, + issueBook: (id) => { + const books = get().books; + const updatedBooks = books?.map((book) => { + if (book.id === id) { + return { + ...book, + status: 'issued', + }; + } else { + return book; + } + }); + set((state) => ({ + books: updatedBooks, + noOfAvailable: state.noOfAvailable - 1, + noOfIssued: state.noOfIssued + 1, + })); + }, + returnBook: (id) => { + const books = get().books; + const updatedBooks = books?.map((book) => { + if (book.id === id) { + return { + ...book, + status: 'available', + }; + } else { + return book; + } + }); + set((state) => ({ + books: updatedBooks, + noOfAvailable: state.noOfAvailable + 1, + noOfIssued: state.noOfIssued - 1, + })); + }, + reset: () => { + set({ + books: [], + noOfAvailable: 0, + noOfIssued: 0, + }); + }, +}); + +const useBookStore = create(bookStore); + +export default useBookStore; +``` + +Zustand storeλ hookμ λλ€. κ·Έλμ `useBookStore`κ° μ»΄ν¬λνΈ μ΄λ¦μΌλ‘ μ¬μ©λ©λλ€. `create`λ storeλ₯Ό μμ±νλ λ° μ¬μ©λλ λ©μλμ λλ€. storeλ κ° μ»΄ν¬λνΈκ° 곡μ νλ μ μΌν μ§λ¦¬μ μμ²μ λλ€. `set` ν¨μλ λ³μλ κ°μ²΄μ μνλ₯Ό μμ νλ λ° μ¬μ©λκ³ , `get` ν¨μλ μ‘μ λ΄μμ μνλ₯Ό μ κ·Όνλ λ° μ¬μ©λ©λλ€. + +μ΄ μμ μμ λΌμ΄λΈλ¬λ¦¬ storeμ μν κ°μ²΄λ μΈ κ°μ νλλ₯Ό ν¬ν¨νκ³ μμ΅λλ€. `books`λ μ± μ ID, μ΄λ¦, μ μμ κ°μ μΈλΆ μ λ³΄κ° ν¬ν¨λ λ°°μ΄μ ν¬ν¨νλ©°, `noOfAvailable`μλ λΌμ΄λΈλ¬λ¦¬ λ΄μ μ΄ λμ μκ° μ μ₯λ©λλ€. `noOfIssued`λ μ¬μ©μμκ² λ°νλ λμμ μ΄ μλ₯Ό μ μ₯ν©λλ€. λΌμ΄λΈλ¬λ¦¬ storeλ λ€ κ°μ§ λ©μλλ₯Ό μ 곡ν©λλ€. `addBook` ν¨μλ μ± μ λ°°μ΄μ μλ‘μ΄ μ± μ μΆκ°νκ³ , νμ¬ μ΄μ© κ°λ₯ν μ± μ μλ₯Ό λ리며, μλ‘ μΆκ°λ λͺ¨λ μ± μ μνλ₯Ό "available"λ‘ μ€μ ν©λλ€. `issueBook` ν¨μλ μ¬μ©μκ° λμλ₯Ό λ°νν μ μκ² ν΄μ£Όλ©°, κ΄λ ¨λ λμλ "issued" μνλ‘ λ³κ²½λ©λλ€. λ°ν μκ° μ¦κ°νκ³ μ΄μ© κ°λ₯ν μ± μ μκ° κ°μν©λλ€. `returnBook` ν¨μλ λ°νλ λμλ₯Ό λΌμ΄λΈλ¬λ¦¬μ λ°ννλ λ° μ¬μ©λλ©°, λ°νλ λμμ μνκ° "available"λ‘ λ³κ²½λκ³ λ°νλ λμ μκ° κ°μνλ©° μ΄μ© κ°λ₯ν λμ μκ° μ¦κ°ν©λλ€. λ§μ§λ§μΌλ‘, `reset` λ©μλλ λͺ¨λ μν νλλ₯Ό μ΄κΈ°νν©λλ€. + +#### 4. μ»΄ν¬λνΈλ₯Ό storeμ μ°κ²° + +λ¨Όμ `App.js` νμΌμ μμ±ν΄ λ΄ μλ€. μλ μ½λλ₯Ό μ°Έκ³ νμΈμ. + +```javascript +//App.js + +import { useEffect } from 'react'; +import BookForm from './components/BookForm'; +import BookList from './components/BookList'; +import useBookStore from './bookStore'; +import './App.css'; + +function App() { + const reset = useBookStore((state) => state.reset); + + useEffect(() => { + reset(); + }, [reset]); + + return ( +