immer로 불변성 관리를 해보자!

혼빈·2024년 9월 26일

React와 Immer: 불변 상태 관리를 간편하게

React 애플리케이션에서 상태 관리는 매우 중요한 역할을 합니다. 상태를 변경할 때 불변성을 유지하는 것은 필수적입니다. 그러나 직접 객체를 복사하고 수정하는 것은 번거롭고 실수를 유발할 수 있습니다. Immer는 이러한 불편을 해결해 주는 라이브러리로, 상태를 간편하게 관리할 수 있게 도와줍니다.

Immer란 무엇인가?

Immer는 자바스크립트 객체를 안전하게 변경할 수 있도록 돕는 라이브러리입니다. Immer를 사용하면 상태를 직접 수정하는 것처럼 보이지만, 내부적으로는 불변성을 유지합니다. 이를 통해 상태 관리가 훨씬 수월해집니다.

주요 개념

  • 프로듀서 함수: Immer가 상태를 변경하기 위해 사용하는 함수입니다.
  • 드래프트(draft) 상태: 프로듀서 함수 내에서 수정할 수 있는 임시 상태입니다. 드래프트는 실제 상태와는 독립적으로 존재하며, 변경사항은 최종적으로 불변 상태로 반영됩니다.

설치하기

Immernpm이나 Yarn을 통해 쉽게 설치할 수 있습니다. 아래의 명령어를 사용하세요.

npm install immer

또는 Yarn을 사용하는 경우:

yarn add immer

기본 사용법

Immer를 사용하는 기본적인 방법은 produce 함수를 사용하는 것입니다. produce 함수는 두 개의 인자를 받습니다: 현재 상태와 상태를 변경하기 위한 프로듀서 함수입니다.

간단한 예제: 카운터

아래는 카운터 애플리케이션을 예로 들어 Immer의 사용법을 설명합니다.

import React, { useState } from 'react';
import produce from 'immer';

const Counter = () => {
  const [state, setState] = useState({ count: 0 });

  const increment = () => {
    setState((prevState) =>
      produce(prevState, (draft) => {
        draft.count += 1; // 드래프트 상태 수정
      })
    );
  };

  const decrement = () => {
    setState((prevState) =>
      produce(prevState, (draft) => {
        draft.count -= 1; // 드래프트 상태 수정
      })
    );
  };

  return (
    <div>
      <h1>Count: {state.count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

상태 변경 과정

  1. increment 또는 decrement 함수를 호출하면 setState가 실행됩니다.
  2. produce 함수는 현재 상태(prevState)를 인자로 받고, 프로듀서 함수를 호출합니다.
  3. 프로듀서 함수 내에서 드래프트 상태(draft)를 수정합니다.
  4. produce는 수정된 드래프트 상태를 기반으로 새로운 상태를 생성하여 setState로 설정합니다.

복잡한 상태 다루기

Immer는 중첩된 객체를 다룰 때 매우 유용합니다. 아래 예제는 Todo 리스트를 관리하는 방법을 보여줍니다.

예제: Todo 리스트

import React, { useState } from 'react';
import produce from 'immer';

const TodoApp = () => {
  const [state, setState] = useState({
    todos: [
      { id: 1, text: 'Learn React', completed: false },
      { id: 2, text: 'Learn Immer', completed: false },
    ],
  });

  const toggleTodo = (id) => {
    setState((prevState) =>
      produce(prevState, (draft) => {
        const todo = draft.todos.find((todo) => todo.id === id);
        if (todo) {
          todo.completed = !todo.completed; // 드래프트 상태를 수정
        }
      })
    );
  };

  const addTodo = (text) => {
    setState((prevState) =>
      produce(prevState, (draft) => {
        draft.todos.push({ id: Date.now(), text, completed: false }); // 새 TODO 추가
      })
    );
  };

  return (
    <div>
      <h1>Todo List</h1>
      <ul>
        {state.todos.map((todo) => (
          <li key={todo.id} onClick={() => toggleTodo(todo.id)}>
            {todo.text} {todo.completed ? '✔️' : '❌'}
          </li>
        ))}
      </ul>
      <button onClick={() => addTodo('New Todo')}>Add Todo</button>
    </div>
  );
};

export default TodoApp;

상태 변경 과정

  1. toggleTodo 함수는 특정 Todo 항목의 완료 상태를 토글합니다.
  2. addTodo 함수는 새로운 Todo 항목을 추가합니다.
  3. 두 함수 모두 produce를 사용하여 상태를 변경합니다.

Immer의 고급 기능

Immer는 기본적인 상태 업데이트 외에도 여러 유용한 기능을 제공합니다.

1. 클래스에 immerable 적용하기

Immer는 클래스 객체에 대해 불변성을 적용할 수 있도록 immerable 속성을 제공합니다. 이를 통해 Immer는 클래스 인스턴스를 다룰 수 있습니다.

import produce, { immerable } from 'immer';

class Todo {
  [immerable] = true; // Immer가 불변성을 적용하도록 표시
  constructor(text) {
    this.text = text;
    this.completed = false;
  }
}

// 사용 예제
const todo = new Todo('Learn Immer');
const updatedTodo = produce(todo, (draft) => {
  draft.completed = true; // 드래프트 상태 수정
});

2. 배열의 불변성 관리

Immer는 배열을 다룰 때도 매우 유용합니다. 아래는 배열의 아이템을 추가, 제거, 수정하는 방법을 보여줍니다.

const manageArray = (prevArray) => {
  return produce(prevArray, (draft) => {
    draft.push('New Item'); // 아이템 추가
    draft.splice(1, 1); // 두 번째 아이템 제거
    draft[0] = 'Updated Item'; // 첫 번째 아이템 수정
  });
};

3. 상태 초기화

Immer는 상태를 초기화할 때도 유용합니다. 아래는 상태를 초기화하는 방법을 보여줍니다.

const initialState = {
  count: 0,
  todos: [],
};

const resetState = () => {
  setState((prevState) => produce(prevState, () => initialState));
};

4. Immer의 enableAllPluginsdisableAllPlugins

Immer에서는 enableAllPluginsdisableAllPlugins를 사용하여 모든 플러그인을 활성화하거나 비활성화할 수 있습니다. 이 기능은 특정 상황에서 성능을 조절할 때 유용합니다.

import { enableAllPlugins, disableAllPlugins } from 'immer';

// 모든 플러그인 활성화
enableAllPlugins();

// 모든 플러그인 비활성화
disableAllPlugins();

성능 고려사항

Immer는 상태를 변경할 때 내부적으로 변경 사항을 추적합니다. 따라서 대규모 상태 트리를 다룰 때는 성능에 영향을 줄 수 있습니다. 이러한 경우에는 다음과 같은 방법을 고려해볼 수 있습니다.

  • 상태를 가능한 한 작게 유지: 큰 상태를 작은 상태로 나누어 관리합니다.
  • 불필요한 렌더링 방지: React.memo를 사용하여 컴포넌트를 메모이제이션합니다.

결론

ImmerReact에서 불변성을 쉽게 관리할 수 있는 강력한 도구입니다. 중첩된 객체나 배열을 다룰 때 특히 유용하며, 개발 효율성을 높여줍니다. Immer를 사용하면 상태 관리를 더욱 간편하고 직관적으로 할 수 있으므로, React 애플리케이션에서의 사용을 적극 추천합니다.

profile
코딩중

0개의 댓글