목차

1. Redux 선택 이유

2. Redux와 contextAPI 구조적 차이

3. Redux 사용 후기(contextAPI와의 비교)

4. 마무리


1. Redux 선택 이유

아직 후기를 쓰진 못했지만 지난 미션에서는 상태관리로 Context API를 사용했었다. Context API를 사용하며 처음 전역상태관리의 개념, Context API와 다른 전역상태관리 라이브러리의 차이점에 대해 공부했었는데, 이 과정에서 전역상태관리 개념의 최초에 redux가 있다는 걸 알게 되었다. 

 

redux에서 처음 나왔다고 알려져있는 reducer와 dispatch의 개념은 언제나 헷갈리는 개념이었다. 또 React에서 상태를 관리하는 방법에는, 요즘 뜨는 recoil, 네이티브 contextAPI, 기타 mobx... 정말 다양하다고 알려져 있지만, 앞으로 어떤 회사에 가서 어떤 상태관리 라이브러리를 사용하게 되든 근본인 redux를 배워두면 컨셉을 이해하는데 유용하지 않을까 생각했다. 

 

그래서 사실 이번 미션의 필수 요구 사항은 오직 React(contextAPI)로만 상태관리를 하는 것이었음에도 불구하고  redux 를 택하게 되었다. 반항심리는 아니었고 근-본은 과연 어떤지, 네이티브 전역상태관리 기능인 contextAPI와는 어떤 차이가 있는지 직접 써보며 확인하고 싶었다.

 

2.  Redux와 ContextAPI의 차이

아래는 한 블로그에서 하나의 도표로 정리한 Redux vs ContextAPI의 차이점으로 분석하며 내 경험과 비교해보았다.(🔗 Redux vs Context API: When to use them, Tapajyoti Bose)

  Redux  Context API
1. 설치 추가 설치 필요, 최종 번들 크기 증가 React와 함께 제공되는 기본 제공 도구
2. 설정 React 애플리케이션과 통합하려면 광범위한 설정이 필요 최소한의 설정 필요
3. 데이터 정적 데이터동적 데이터 모두에 적합 자주 새로 고치거나 업데이트하지 않는 정적 데이터를 위해 특별히 설계
4. 확장성 초기 설정 후 새로운 데이터/액션을 쉽게 추가할 수 있어 쉽게 확장 가능 새로운 컨텍스트를 추가하려면 처음부터 새로 만들어야 함
5. 디버깅 개발 도구에서도 컴포넌트 구조 디버깅을 쉽게 해주는 놀랍도록 강력한 Redux 개발 도구  고도로 중첩된 React에서는 디버깅이 어려울 수 있음.
6. 코드 구성 UI 로직과 상태 관리 로직을 분리하여 코드 구성 개선 UI 로직과 상태 관리 로직이 동일한 구성 요소에 있음

1. 설치

Redux는 npm i redux redux-react @reduxjs/toolkit  으로 redux포함 3가지 라이브러리를 설치해야한다. 이는 최종 프로그램 번들 사이즈의 향상으로 이어진다. 반면 Context API는 다른 설치 없이 사용 가능하다.

 

2. 설정

Redux는 reducer, slice, store, Provider로 감싸기 등 프로그램 사용을 위해 초기 설정해줘야하는 게 많다. 반면 Context API는 context생성 Provider로 감싸주기로 초기 설정이 간단하다.

 

3. 데이터

Redux는 정적 및 동적 데이터 모두에 적합하다고 하는데, 그 이유는 Redux가 불변성을 유지하고 예측 가능한 방식으로 상태를 관리하도록 설계되었기 때문이다(Thinking in Redux, redux). 

 

반면, Context API는 정적 데이터를 위해 것으로 예를 들면, 테마나 인증 정보, 사용자 언어와 같은 수준의 잘 변하지 않는 데이터를 전역적으로 공유할 때 유용하다고 한다.(when to use context, react) 따라서 Redux와 달리 복잡한 상태관리를 위한 툴은 아니라고 할 수 있겠다.  

 

4. 확장성

Redux는 새로운 데이터나 액션을 추가할 때, 서브store 개념인 slice나 새로운 액션인 reducer를 새로 추가하면 되는 반면, context는 서브context 개념이 따로 없어 수정이 어렵다는 말로 들린다. 

 

하지만  Context API가 보통 가장 상단이 되는 컴포넌트를 감싸는 식으로 사용되는 것과 달리, 컴포넌트와 상태를 별도의 파일로 분리해 보다 확장성 있게 사용하는 방법도 있다고 한다(How to use context effectively, Kent C. Dodds) 따라서 새로운 데이터나 액션을 추가하기 위해 컨텍스트를 처음부터 새로 짜야만한다는 이 부분에 대해서는 의구심이 든다. 

 

5. 디버깅

Redux에서는 리덕스 개발자 도구를 처음 개발하고 배포한 개발자 중 한 명인 Zalmoxisus라는 개발자가 만든 크롬 확장 Redux DevTools이 존재한다. 아래와 같은 디버깅을 위한 다양한 기능을 제공하고, 이를 이용해 훨씬 쉽게 디버깅을 할 수 있다:

  • 시간여행(time-travel) 기능: 과거 상태로 돌아가서 상태 변경 과정을 디버깅할 수 있습니다.
  • 상태 스냅샷 기능: 현재 상태를 스냅샷으로 저장하여 나중에 다시 볼 수 있습니다.
  • 액션 필터링 기능: 특정 액션만 필터링하여 디버깅할 수 있습니다.

반면 Context API는 Provider Hell을 야기할 수 있고(🔗 Context API가 존재하지만 여전히 사람들이 redux와 전역 상태관리 라이브러리를 쓰는 이유, nanalog) 이러한 고도의 중첩의 경우 디버깅이 상당히 복잡하고 짜증나는 작업이 될 수 있다.

 

6. 코드 구성

Redux는 UI 로직과 상태 관리 로직을 분리하여 보다 클린한 코드 구성이 가능하하지만, context API는 UI를 업데이트하는 로직과 상태 관리 로직이 동일한 구성 요소에 있다고 한다. 

 

하지만 이 역시 context에서 reducer 함수나 custom hook의 사용을 통해 충분히 분리할 수 있을 거라 생각해 공감이 가진 않는다.

// 1. useReducer 사용해 UI로직과 상태관리 로직 분리하기

import React, { useReducer } from "react";

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

export const CounterContext = React.createContext();

function CounterProvider(props) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {props.children}
    </CounterContext.Provider>
  );
}

export default CounterProvider;
// 2. custom hook(useCounter) 사용해 UI로직과 상태관리 로직 분리하기

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

const CounterContext = createContext();

function CounterProvider(props) {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  const value = { count, increment, decrement };

  return <CounterContext.Provider value={value} {...props} />;
}

function useCounter() {
  const context = useContext(CounterContext);
  if (context === undefined) {
    throw new Error("useCounter must be used within a CounterProvider");
  }
  return context;
}

export { CounterProvider, useCounter };
// 이렇게 하면, useCounter hook을 사용하여 UI를 업데이트하는 로직과 상태 관리 로직을 분리할 수 있다.

import React from "react";
import { CounterProvider, useCounter } from "./CounterContext";

function Counter() {
  const { count, increment, decrement } = useCounter();

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

function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  );
}

export default App;

 

3. 마무리하며

 

처음 상태관리 라이브러리로 redux를 직접 사용해보며, 시중에 나와있는 redux vs contextAPI 글을 분석해봤다. 4번과 6번 특징에 대해서는 동의하지 않지만, 그외의 특징에는 동의한다. 특히

 

1. 초기 세팅이 너무 많다는 점에 동의한다.

이를 위해 reduxjs/toolkit이 등장했다고 해도 여전히 contextAPI보다는 초기 설정 너무 많아 번거로움.

 

2. 디버깅이 쉽다는 점에 동의한다.

redux 개발자 도구를 사용해서 전역 관리 상태를 가시적으로 볼 수 있는 점이 너무너무 편리했다. redux 자체보다는 redux 개발자 도구을 쓸 수 있다는 점이 좋았다. 

 

3. 단순한 상태라면 redux를 사용할 필요는 없다.

redux는 초기 설정이 번거롭지만, 이후 데이터 확장성이 좋고 보다 복잡하고 많은 상태를 관리할 수 있다는 장점이 있다고 한다. 하지만 굳이 cartSlice 하나의 상태로도 충분했던 이번 미션에서 ContextAPI로도 충분했으리라는 생각이 든다. 

 

'Extracurricular > 교육' 카테고리의 다른 글

[NEXTSTEP] CDD with React 온보딩 미션 회고  (0) 2023.02.12
[글또 8기] 글또를 시작하며  (0) 2023.01.31

+ Recent posts