본문 바로가기

Tech develop

[웹] React 상태 관리 라이브러리 비교

반응형

React로 웹 애플리케이션을 개발할 때 상태 관리는 필수적인 요소입니다. 상태 관리 없이 복잡한 애플리케이션을 유지보수하기란 무척 어렵습니다. 다양한 상태 관리 라이브러리가 존재하지만, 각각의 장단점이 있습니다. 이번 글에서는 React의 상태 관리 라이브러리들을 비교하여 여러분의 프로젝트에 가장 적합한 라이브러리를 선택하는 데 도움을 드리고자 합니다.

상태 관리의 필요성

React는 컴포넌트 기반으로 UI를 구축하는 데 탁월한 라이브러리입니다. 그러나 여러 컴포넌트가 상태를 공유해야 할 때 효율적인 상태 관리가 필요합니다. 그 이유는 상태가 여러 컴포넌트에 걸쳐 있을 때 이를 중앙에서 관리하면 데이터 흐름을 쉽게 추적할 수 있고, 버그 수정과 기능 추가가 용이해지기 때문입니다.

Redux

Redux는 가장 널리 알려진 상태 관리 라이브러리 중 하나입니다. Redux는 애플리케이션의 전체 상태를 하나의 저장소에서 관리하며, 액션과 리듀서를 통해 상태를 변화시킵니다. 이러한 방식은 애플리케이션의 상태를 예측 가능하게 하고, 디버깅을 용이하게 합니다.

import { createStore } from 'redux';

const initialState = { count: 0 };

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

const store = createStore(reducer);

store.dispatch({ type: 'INCREMENT' });
console.log(store.getState());

위의 예제는 Redux의 기본적인 사용법을 보여줍니다. createStore를 통해 Redux 스토어를 생성하고, 리듀서를 통해 상태를 관리합니다. 액션을 디스패치하면 리듀서가 호출되어 상태가 변경됩니다. Redux는 상태를 중앙에서 관리하여 상태 흐름을 명확히 파악할 수 있도록 돕습니다.

MobX

MobX는 상태를 더 직관적으로 관리할 수 있도록 하는 라이브러리입니다. MobX는 상태가 변경되면 자동으로 UI가 갱신됩니다. 이를 통해 개발자는 상태 변경에 신경 쓰지 않고 비즈니스 로직에 집중할 수 있습니다.

import { makeAutoObservable } from 'mobx';

class Counter {
  count = 0;

  constructor() {
    makeAutoObservable(this);
  }

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

const counter = new Counter();

counter.increment();
console.log(counter.count);

MobX는 makeAutoObservable 함수를 사용하여 클래스의 상태를 관찰 가능하게 만듭니다. 상태 변경은 자동으로 UI에 반영되며, 따로 액션을 설정하지 않아도 된다는 점에서 Redux와 차별화됩니다.

Context API

React의 내장 기능인 Context API는 비교적 간단한 상태 관리에 적합합니다. Context API는 전역적으로 상태를 공유할 때 유용하며, 복잡한 설정 없이도 사용할 수 있습니다.

import React, { createContext, useState, useContext } from 'react';

const CountContext = createContext();

function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
}

function Counter() {
  const { count, setCount } = useContext(CountContext);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>{count}</p>
    </div>
  );
}

Context API는 createContext를 통해 컨텍스트를 생성하고, useContext 훅을 통해 이를 소비합니다. 상태를 전역적으로 제공하기 위해 CountProvider 컴포넌트를 사용합니다. 이 방식은 간단한 상태 공유에 적합하지만, 상태가 복잡해질 경우 성능 이슈가 발생할 수 있습니다.

Recoil

Recoil은 Facebook에서 개발한 새로운 상태 관리 라이브러리입니다. Recoil은 React의 동시성 모드와 잘 통합되며, 상태를 아토믹 단위로 관리할 수 있습니다. 이는 상태의 일부만 갱신할 수 있다는 장점이 있습니다.

import React from 'react';
import { atom, useRecoilState } from 'recoil';

const countState = atom({
  key: 'countState',
  default: 0,
});

function Counter() {
  const [count, setCount] = useRecoilState(countState);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>{count}</p>
    </div>
  );
}

Recoil의 atom은 상태의 최소 단위를 정의합니다. useRecoilState 훅을 사용하여 상태를 읽고 갱신할 수 있습니다. Recoil은 상태를 독립적으로 관리하기 때문에 필요한 부분만 갱신할 수 있어 효율적입니다.

Zustand

Zustand는 가볍고 사용하기 쉬운 상태 관리 라이브러리입니다. Zustand는 Flux 패턴을 따르지 않고, 간단한 API를 통해 상태를 관리합니다. 이는 작은 애플리케이션에서 특히 유용합니다.

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const { count, increment } = useStore();
  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>{count}</p>
    </div>
  );
}

Zustand는 create 함수를 통해 스토어를 생성합니다. useStore 훅을 통해 상태를 읽고 업데이트할 수 있습니다. Flux 패턴을 따르지 않기 때문에 코드가 간결하고 직관적입니다.

Redux Toolkit

Redux Toolkit은 Redux의 보일러플레이트 코드를 줄이기 위한 라이브러리입니다. Redux의 모든 기능을 간편하게 사용할 수 있도록 도와줍니다. 특히 간단한 설정과 코드의 간결함이 장점입니다.

import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
    increment: (state) => { state.count += 1 },
    decrement: (state) => { state.count -= 1 },
  },
});

const store = configureStore({
  reducer: counterSlice.reducer,
});

store.dispatch(counterSlice.actions.increment());
console.log(store.getState());

Redux Toolkit은 createSlice를 통해 리듀서를 간단하게 생성하고, configureStore를 통해 스토어를 설정합니다. 이는 Redux의 사용성을 크게 향상시키며, 더욱 직관적인 코드를 작성할 수 있게 합니다.

Jotai

Jotai는 최소한의 API로 상태를 관리할 수 있는 라이브러리입니다. Jotai는 단일 원자(atom)를 상태의 최소 단위로 사용하며, 간단한 상태 관리에 적합합니다.

import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>{count}</p>
    </div>
  );
}

Jotai의 atom은 상태의 기본 단위를 정의합니다. useAtom 훅은 상태를 읽고 갱신하는 데 사용됩니다. Jotai는 간단하고 직관적인 API를 제공하여, 작은 애플리케이션이나 모듈화된 상태 관리를 원하는 경우 적합합니다.

Apollo Client

Apollo Client는 GraphQL API와의 상태 관리를 위해 설계된 클라이언트 라이브러리입니다. 아폴로 클라이언트는 캐싱, 쿼리, 뮤테이션 등을 통해 클라이언트 측 상태를 관리할 수 있습니다.

import { ApolloClient, InMemoryCache, gql, useQuery } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://example.com/graphql',
  cache: new InMemoryCache(),
});

const GET_COUNT = gql`
  query GetCount {
    count
  }
`;

function Counter() {
  const { loading, error, data } = useQuery(GET_COUNT);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <div>
      <p>{data.count}</p>
    </div>
  );
}

Apollo Client는 GraphQL을 통해 상태를 관리하며, 서버와 클라이언트 간의 데이터 통신을 효율적으로 처리합니다. GraphQL API와의 통합이 필요한 경우 유용하게 사용할 수 있습니다.

정리 및 요약

이번 글에서는 React 상태 관리 라이브러리인 Redux, MobX, Context API, Recoil, Zustand, Redux Toolkit, Jotai, Apollo Client를 비교했습니다. 각 라이브러리는 고유의 장단점이 있으며, 프로젝트의 규모와 요구사항에 따라 적절한 선택이 필요합니다. Redux와 Redux Toolkit은 대규모 애플리케이션에 적합하며, MobX와 Recoil은 복잡한 상태 관리와 성능 최적화에 유리합니다. Context API와 Zustand, Jotai는 간단하고 직관적인 상태 관리를 제공합니다. Apollo Client는 GraphQL API와의 통합에 강점을 보입니다. 각 라이브러리의 특성을 잘 이해하고 프로젝트에 맞는 라이브러리를 선택하시길 바랍니다.

반응형