패스트캠퍼스 데브캠프 63~64일차 [React, Zustand]

Su Min·2024년 8월 21일
0
post-thumbnail

🔗 Zustand

React 상태관리 라이브러리로 Redux처럼 단순화된 Flux패턴을 사용하지만 Redux와 다르게 보일러플레이트 코드(Boilerplate code)가 거의 없을 정도로 코드가 간단하며 연결부터 상태관리까지 쉽다는 장점이 있어 Zustand를 많이 도입하고 있다.

Boilerplate Code

Boilerplate Code란 최소한의 변경으로 여러 곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드를 의미한다. 프로그래밍에서 반복되는 작업이나 코드를 작성하기 위해 항상 필요한 부분이 Boilerplate Code에 해당한다. 예를 들면 리액트에서 import, export 등 처럼 수정없이 반복적으로 사용하는 코드를 보일러플레이트 코드라고 한다.

보일러플레이트 코드의 장점으로 재사용성, 유지보수성, 개발 시간 단축 등이 있지만 Redux는 반복적으로 비슷한 코드를 작성해야하는 단점이 있어 Zustand가 리덕스의 단점을 보완하여 보일러플레이트 코드를 줄이고 최소한의 코드로 상태관리를 가능하게 한다.

설치

npm i zustand

🔗 사용하기

Zustand의 create함수로 스토어를 생성한다.

import { create } from 'zustand'

const use이름Store = create((set) => ({
  상태: 초깃값,
  액션: (변경값) => set((state) => ({
    상태: state.상태 + 변경값
  })) 
}))

export default use이름Store

set 매개변수는 use이름Store 의 상태를 업데이트한다. 주스탠드는 간단한 API를 제공하여 상태를 변경하거나 조회할 수 있다.

위의 기본 형태처럼 주스탠드가 콜백형태를 띄우는 이유는 상태관리의 일관성을 유지하고 유지보수성을 용이하게 하기 위해서이다. 만약 액션이 동기코드에서 비동기코드로 변경된다면 해당 코드가 비동기 코드 구조와 똑같아서 async만 추가하면 되기때문에 이는 코드구조의 일관성을 유지하고 유지보수성이 용이하다는 점을 나타낸다. 또한 비동기와 동기 처리가 따로있는 리덕스와 다르게 주스탠드는 하나의 스토어에서 처리가 가능함을 의미한다.

useTodosStore.ts

import { create } from 'zustand'

interface Todo {
  id: string
  title: string
  content: string
}

interface State {
  todos: Todo[]
}

interface Actions {
  addTodo: (todo: Todo) => void
}

const useTodosStore = create<State & Actions>((set) => ({
  todos: [],
  addTodo: (todo) => set((state) => ({
    todos: [...state.todos, todo],
  }))
}))

export default useTodosStore

생성한 useTodosStore는 Provider없이 컴포넌트에서 바로 사용 가능하다.

Todos.tsx

import { useState } from 'react'
import useTodosStore from '../store/useTodosStore'
import TodoList from './TodoList'

const Todos: React.FC = () => {
  const addTodo = useTodosStore(state => state.addTodo)

  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')

  const handleAddTodo = () => {
    const newTodo = {
      id: Date.now().toString(),
      title,
      content,
    }
    addTodo(newTodo)
    setTitle('')
    setContent('')
  }

  return (
    <div>
      <div>
        <input
          type="text"
          placeholder="Title"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <input
          type="text"
          placeholder="Content"
          value={content}
          onChange={(e) => setContent(e.target.value)}
        />
        <button onClick={handleAddTodo}>추가</button>
      </div>
      <TodoList />
    </div>
  )
}

export default Todos

Todos컴포넌트는 useTodosStore의 addTodo액션으로 Todos의 상태를 변경한다.

TodoList.tsx

import React from 'react'
import useTodosStore from '../store/useTodosStore'

const TodoList: React.FC = () => {
  const todos = useTodosStore((state) => state.todos)

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          <h3>{todo.title}</h3>
          <p>{todo.content}</p>
        </li>
      ))}
    </ul>
  )
}

export default TodoList

TodoList컴포넌트는 변경된 Todos상태로 리렌더링된다.
const { todos } = useTodosStore() 이런식으로 구조분해하여 가져오는 방식은 간편하지만 todos가 useTodosStore의 전체를 구독하여 useTodosStore에서 하나의 상태만 바껴도 전체가 다시 그려지기때문에 위의 컴포넌트 로직처럼 개별적으로 가져온다. 그러면 불필요한 리렌더링을 하지 않게 되어 성능도 향상된다.

🔗 Redux & Zustand

store 구현과 상태관리의 차이

리덕스는 스토어 및 상태선언, Action선언, Reducer구현 이후 Provider로 컴포넌트에 연결하고 훅을 호출한다. 반면 주스탠드는 스토어에 모두 구현하고 컴포넌트에서 호출한다.

profile
성장하는 과정에서 성취감을 통해 희열을 느낍니다⚡️

0개의 댓글

관련 채용 정보