새소식

반응형
Front-end

[React] Redux가 좋은 경우 vs Zustand가 좋은 경우

2024.11.10
  • -
반응형

 

1. React 상태 관리

React에서 상태 관리는 애플리케이션 내에서 데이터의 동적인 변화를 추적하고 조작하는 그 행위 자체를 의미합니다.

 

React에서 '상태(state)'는 컴포넌트의 렌더링 결과에 직접적인 영향을 미치는 데이터로, 사용자 입력, 서버로부터 받은 응답, 혹은 기타 이벤트에 따라 변경될 수 있다는 특징을 가지고 있습니다.

 

주요 특징은 아래와 같습니다.

  • 컴포넌트 상태: 각 컴포넌트는 자체적으로 상태를 가질 수 있도록, useState와 같은 훅을 통해 관리합니다.
  • 상태의 복잡성 증가: 애플리케이션 규모가 커지면서 여러 컴포넌트 간에 상태를 공유해야 하는 상황이 발생합니다.
  • 상태 관리의 중요성: 효율적인 상태 관리는 코드의 가독성, 유지보수성, 그리고 애플리케이션의 성능에 직접적인 영향을 줍니다.

 

2. 전역 상태 관리 라이브러리를 사용해야 하는 이유

React로 작은 규모의 애플리케이션을 만드는 경우에는 컴포넌트 내부의 상태 관리(useState)만으로도 충분합니다.

 

하지만 애플리케이션이 성장함에따라 하나의 상태가 여러 컴포넌트에 공유되는 상황이 빈번하게 증가하게 되고, 이때 전역 상태 관리 라이브러리가 필요한 이유를 이해하기 위해 몇 가지 핵심 개념에 대해 알아보도록 하겠습니다.

 

Prop Driling

React에서는 상태를 공유하기 위해 props를 통해 데이터를 내려주는 방식을 사용합니다. Prop Drilling은 상위 컴포넌트에서 하위 컴포넌트로 props를 통해 데이터를 전달하는 과정이 매우 깊게 내려가게 되어 마치 드릴이 파고 내려가는 것과 같다는 의미에서 나온 용어 입니다.

 

이러한 상황이 문제가 되는 이유는 무엇일까요?

 

중간에 거치는 컴포넌트들은 그 하위의 컴포넌트에 상태를 전달하지만 정작 본인 컴포넌트에서는 해당 상태를 사용하지 않는 경우가 있을 수 있습니다. 이렇게 되면 해당 상태의 재렌더링 시에 불필요하게 해당 컴포넌트까지 재렌더링 되는 문제가 발생할 수 있는 것입니다.

 

또한 컴포넌트의 구조가 변경되면 props 전달 경로도 수정해 주어야 하며, 코드의 가독성과 유지보수성 역시 매우 떨어지는 결과를 초래합니다.

 

상태 공유의 복잡성 증가

앞서 말했듯 애플리케이션 규모가 커지게 됨에 따라 여러 컴포넌트에서 동일한 상태나 데이터를 필요로 하는 경우가 빈번하게 발생할 것입니다.

 

예를 들어, 사용자 인증 정보, 테마 설정, 언어 설정 등과 같은 데이터들은 애플리케이션 전반에 걸쳐 사용된다는 것을 우리는 알고 있습니다.

 

하지만 문제는 각 컴포넌트마다 상태를 개별적으로 관리하면 데이터 불일치가 발생할 수 있다는 것인데요. 상위 컴포넌트로 상태를 끌어올리는 "Lifting State Up" 방식은 한계가 있습니다.

 

또한 전역적인 상태 관리를 위한 구조가 마련되어 있지 않다면 상태 관리 로직이 여기저기 흩어져 있어 코드의 복잡성도 증가하게 되죠.

 

비동기 데이터 처리의 어려움

서버로부터 데이터를 가져오거나 업데이트하는 비동기 작업은 안 그래도 복잡한 상태 관리에 복잡함을 한 층 더 높여주게 됩니다.

 

이렇게 비동기 상태를 개별 컴포넌트에서 관리하면 코드 중복이 발생하고 전역적으로는 일관된 비동기 상태 처리가 어려울 것입니다.

 

전역 상태 관리 라이브러리가 제공하는 해결책

그렇다면 전역 상태 관리 라이브러리는 앞선 문제점들을 어떻게 해결해줄 수 있을까요?

 

중앙 집중식 상태 관리

전역 상태 관리 라이브러리는 애플리케이션의 상태를 하나의 중앙 저장소에서 관리합니다. 마치 회사의 중앙 서버에서 모든 데이터를 관리하고, 각 부서는 필요한 데이터를 서버에서 가져와 사용하는 것과 마찬가지로 볼 수 있습니다. 이를 통해 데이터의 일관성과 신뢰성을 유지할 수 있습니다.

 

코드 구조 개선 및 유지보수성 향상

상태 관리 라이브러리를 사용하면 보통 해당 부분과 관련된 로직은 별도로 마련된 곳에서 관리하는 것이 관례입니다. 상태 관리 로직을 한 곳에 모으고 명확한 패턴을 따르기 때문에 코드의 가독성과 유지보수성이 매우 향상됩니다.

  • Redux의 경우는 action과 reducer를 사용하여 상태 변경 로직을 더욱 명확하게 분리합니다.

이처럼 상태 관리는 컴포넌트 간의 데이터 흐름을 조율하고 원할한 사용자 경험을 보장하는 React 애플리케이션에서 중추적인 역할을 합니다. 프로젝트가 발전하고 복잡성이 증가함에 따라 개발자는 상태를 효과적으로 관리할 수 있는 강력한 솔루션을 찾게 됩니다.

 

앞으로 소개드릴 상태 관리 라이브러리는 react 에코시스템의 대표적인 두 라이브러리, Redux와 Zustand에 대해 소개해드리려고 합니다. 각각의 장점을 살펴보고 접근 방식을 비교하여 프로젝트에 가장 적합한 솔루션을 선택할 수 있도록 도움을 드리겠습니다.

 

역동적으로 변화하는 웹 생태계에서 상태 관리 방식은 프로젝트 요구 사항, 팀 전문성, 그리고 성능 고려 사항 등에 따라 크게 달라지곤 합니다.

 

Redux는 대규모 애플리케이션에서 상태 관리를 위한 솔루션으로 오랫동안 사랑받아 왔지만, 소규모 프로젝트나 정말 사소한 상태 요구사항이 있는 특정 컴포넌트에 맞춘 가벼운 대안이 최근 Zustand라는 평가를 받고 있습니다. 

 

그래서 최근 핫한 화두이기도 한 "모든 React 애플리케이션에 Redux가 정말 필요할까?"라는 질문은 여전히 오르내리고 있다고 합니다.

 

3. Redux

Redux가 나오면서 내세웠던 상태 관리 전략은 전체 애플리케이션에서 관리될 수 있는 상태를 단일 전역 'store'에 저장하고 상태 수정은 그 저장소에 전달되는 'action'이라는 것을 사용하여 이루어지도록 한 것입니다.

 

그렇게 되면 저장소는 위 작업들을 기반으로 상태를 업데이트할 수 있으며, 이를 필요로 하는 곳에는 업데이트된 상태를 제공할 수 있습니다.

 

Redux 하면 Flux 아키텍쳐가 빠질 수 없는데요. Flux 아키텍쳐를 근간으로 만든 라이브러리가 Redux입니다.

 

Redux는 예측 가능한 상태 컨테이너를 제공하여 애플리케이션 상태를 단일 저장소에 중앙 집중화하고 체계적인 데이터 접근를 용이하게 합니다. 엄격하게 제어되는 단방향 데이터 흐름은 디버깅과 테스트를 간소화할 수 있으며, 굉장히 큰 규모의 커뮤니티와 성숙한 라이브러리 생태계는 그 매력을 더욱 강화합니다. 하지만 Redux의 boilerplate 코드에 대한 의존도와 굉장히 가파른 러닝 커브가 소규모 프로젝트나 상태 관리를 처음 접하는 개발자들이 Redux를 사용하기 꺼려하는 이유가 되는 것입니다.

 

장점

  1. 중앙 집중식 상태 관리: Redux는 애플리케이션 상태를 단일 저장소에 저장하기 때문에 유지 관리와 데이터 접근을 간소화하였습니다.
  2. 예측 가능한 상태 변경: Redux는 엄격한 단방향 데이터 흐름을 따라야 하기 때문에 디버깅 및 테스트 프로세스가 쉬워집니다.(이 부분이 대규모 프로젝트에서 사용되는 가장 큰 이유이겠죠.)
  3. 대규모 커뮤니티 지원: Redux는 굉장히 오랫동안 사랑을 받아왔고 대규모 프로젝트에서 자주 사용되다 보니 커뮤니티가 굉장히 거대하고 성숙합니다.

 

단점

  1. Boilerplate 코드: Redux를 구현하기 위해서 굉장히 많은 양의 Boilerplate(상용구) 코드를 작성해야 하기 때문에 개발 시간이 늘어난다는 단점이 있습니다.
  2. 가파란 학습 곡선(learning curve): Redux의 reducer나 action과 같은 개념은 이 아키텍쳐를 이해하고 있지 못하는 개발자들에게는 공부해야 할 것이 하나 더 늘어난 것이며, 제대로 이해하기까지 시간 역시 오래걸립니다.
  3. Redux Toolkit: Redux 개발을 쉽게 만들어주는 Redux toolkit은 Boilerplate 코드의 대부분을 추상화하여 Redux 개발을 간소화합니다. 장바구니 시나리오를 예를 들면 아래 코드와 같을 것입니다.
// 장바구니를 관리하는 Redux Toolkit slice
import { createSlice } from '@reduxjs/toolkit';

const shoppingCartSlice = createSlice({
  name: 'shoppingCart',
  initialState: {
    items: [],
  },
  reducers: {
    addItem: (state, action) => {
      state.items.push(action.payload);
    },
    removeItem: (state, action) => {
      state.items.splice(action.payload, 1);
    },
  },
});

export const { addItem, removeItem } = shoppingCartSlice.actions;

// Redux Toolkit를 사용하여 Redux 스토어 세팅
import { configureStore } from '@reduxjs/toolkit';

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

// Redux store를 사용하는 컴포넌트
function ShoppingCart() {
  const items = useSelector((state) => state.shoppingCart.items);
  const dispatch = useDispatch();

  const handleAddItem = (item) => {
    dispatch(addItem(item));
  };

  const handleRemoveItem = (index) => {
    dispatch(removeItem(index));
  };

  return (
    <div>
      <h2>Shopping Cart</h2>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item.name} - ${item.price}
            <button onClick={() => handleRemoveItem(index)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

 

Redux Toolkit에서 상태 업데이트는 reducer에게 전달된 action을 통해 비동기적으로 이루어집니다. 상태가 잘 업데이트 되었다면 UI에서 이러한 변경사항이 반영됩니다.

 

간단한 기능을 하는 코드임에도 상당한 코드량을 보여줍니다.

 

 

4.  Zustand

zustand는 경량화된 라이브러리입니다. 직관적인 API와 동기적인 업데이트 방식, Redux와는 대조되는 중앙화된 스토어를 통해 상태 관리를 단순화시킨 것이 특징입니다.

 

매우 단순한 접근을 통해 예측 가능함과 디버깅에 용이함을 향상시킨 Zustand는 초급자나 소규모 프로젝트에 굉장히 적합하다고 볼 수 있습니다. 또한 boilerplate 코드를 감소시켰고 단순함과 용이함에 우선순위를 맞추어 그런지 학습 곡선 역시 매우 얕습니다.

 

장점

  1. 최소화된 세팅: zustand는 Redux에 비해서 boilerplate 코드를 덜 요구하며 빠르고 쉬운 세팅을 제공합니다.
  2. 경량화: 성능에 초점을 맞추어, 소규모 프로젝트에 적합한 작은 라이브러리이면서 더욱 짧은 로딩 시간을 제공합니다.
  3. 쉬운 통합: zustand는 애플리케이션 구조의 유연함을 제공하면서, 다른 상태 관리 솔루션들과도 쉽게 통합할 수 있습니다.(Redux, MobX 등)

 

단점

  1. 미성숙한 라이브러리 생태계: zustand의 생태계는 너비와 폭이 Redux에 비해 매우 부족하며, 그로 인해 써드 파티 툴이나 라이브러리가 거의 없습니다.
  2. 복잡한 애플리케이션에 적합하지 않음: zustand는 단순하기 때문에, Redux와 같은 보다 포괄적인 솔루션이 필요한 대규모의 복잡한 애플리케이션에서 상태를 관리하는 데에는 적합하지 않을 수 있습니다.

 

zustand의 경쟁력: 동기식 특성과 그 너머의 무언가

zustand가 경쟁력을 갖도록 하는 한 가지 주목할 만한 점은 직접적인 상태 변경과 즉각적인 UI 업데이트가 가능한 동기식 특성을 갖는다는 점입니다.

 

이러한 동기식 동작은 Redux의 비동기식 접근 방식과 대조적으로 개발자가 상태 변경 및 렌더링 최적화를 더 잘 제어할 수 있도록 해줍니다.

 

// 장바구니를 관리하는 zustand 스토어 예시
import create from 'zustand';

const useShoppingCartStore = create((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
  removeItem: (index) => set((state) => ({ items: state.items.filter((_, i) => i !== index) })),
}));

// zustand 스토어를 사용하는 컴포넌트
function ShoppingCart() {
  const items = useShoppingCartStore((state) => state.items);
  const addItem = useShoppingCartStore((state) => state.addItem);
  const removeItem = useShoppingCartStore((state) => state.removeItem);

  return (
    <div>
      <h2>Shopping Cart</h2>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item.name} - ${item.price}
            <button onClick={() => removeItem(index)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

 

5. 한 가지를 고른다면?

Redux와 Zustand 사이에서 무엇을 고를지 딜레마에 빠졌다면, 프로젝트 요구사항의 특성을 고려해야 할 것입니다.

 

Redux는 엄격한 데이터 흐름과 표준화된 관행이 요구되는 대규모 애플리케이션에서 압도적인 성능을 보여주기 때문에 엔터프라이즈급 작업에 이상적인 선택입니다.

 

반대로 Zustand는 단순함과 성능을 우선시하는 소규모 프로젝트에서 빛을 발할 수 있으며, 설정에서 발생하는 오버헤드를 최소화하는 가벼운 대안을 제공해 줍니다.

 

6. 대규모 프로젝트에서 Redux가 적합한 이유

코드의 확장성과 유지보수성

redux는 모듈화된 코드 구조를 가지기 때문에 action, reducer, middleware 등으로 코드가 명확히 분리됩니다. 또한 기능별로 폴더 구조를 나누어 관리할 수 있기 때문에 개발 인원이 많은 프로젝트에서 더욱 유리한 것입니다.

src/
  actions/
    userActions.js
    productActions.js
  reducers/
    userReducer.js
    productReducer.js
  store.js
  App.js

 

이는 팀 규모가 커져도 코드 구조를 유지하기 쉽고 공통 기능을 모듈화하기 때문에 재사용성이 증가합니다.

 

 

7. 결론

Zustand는 크기가 작고 미니멀한 디자인으로 인해 boilerplate 코드가 자연스럽게 줄어듭니다. 이러한 간소화된 접근 방식은 개발자의 생상성을 높이고 코드 가독성을 향상시켜 장기적인 프로젝트 확장성을 촉진합니다. 

 

Redux가 널리 채택되고 있는 편이지만 그렇다고 해서 Redux가 무작정 좋다고 하기에는 Redux에서 다른 라이브러리로 넘어가는 기업들도 종종 보이는 상황이라 장단점을 신중하게 평가하여 내 프로젝트에 필요한 라이브러리가 무엇일지를 생각해보는 것이 중요할 것 같습니다.

 

Redux는 거의 모든 애플리케이션에 적합할 수 있지만 새롭게 등장한 Zustand의 단순성, 성능 이점, 테스트 용이성, 코드 간결성으로 인해 많은 개발자에게 매력적으로 다가가고 있는 상황입니다. 특히 간소화된 상태 관리를 원하는 소규모 프로젝트나 팀에게 zustand의 단순성과 효율성을 수용하면 상당한 이점을 제공할 수 있다고 생각합니다. 하지만 프로젝트의 고유한 요구사항, 팀의 전문성, 장기적인 확장성을 고려하여 결정해야 함은 틀림없습니다.

 

 

 

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.