BlogReactYou Might Not Need Redux

You Might Not Need Redux

Medium | You might not need redux글을 번역하고 정리합니다.

Redux 도입

많은 개발자들이 Redux를 필요 이상으로 빨리 선택합니다. 나중에 확장이 안 될까봐라는 걱정에서 시작되지만, 결국 Redux가 도입한 복잡성 때문에 후회하는 경우가 많습니다. “간단한 기능을 위해 왜 여러 파일을 수정해야 하나요?”와 같은 불만이 나오곤 합니다.

Redux는 상태 관리를 단순히 복잡하게 만드는 것이 아니라, 몇 가지 제약을 통해 trade off를 제공합니다.

Redux의 제약

  1. 애플리케이션 상태는 일반 객체나 배열로 표현해야 합니다.
  2. 시스템 변화를 일반 객체로 설명합니다.
  3. 순수 함수로 상태 변경 로직을 처리합니다.

이러한 제약은 앱 개발에 필수적인 것은 아니며, 도입 전 신중한 고민이 필요합니다. 하지만, 특정 상황에서는 매우 유용할 수 있습니다:

  • 로컬 저장소 상태를 쉽게 유지하고 부팅할 수 있음
  • 서버에서 상태를 미리 로드하여 빠르게 부팅 가능
  • 사용자 작업을 직렬화하여 디버깅에 활용
  • 상태 스냅샷과 함께 자동화된 버그 보고서를 작성할 수 있음
  • 네트워크를 통해 작업 객체를 쉽게 전달하여 협업 가능
  • 실행 취소 기능이나 낙관적 업데이트 구현 가능
  • 제품 개발자가 맞춤형 도구를 쉽게 구축 가능
  • 대체 UI를 쉽게 제공하면서 비즈니스 로직을 재사용 가능

그러나 Redux를 바로 도입하지 말고, 먼저 React 자체에 대한 이해를 깊이 하는 것이 중요합니다. Redux는 강력한 도구이지만, 사용 여부는 신중히 결정해야 하며, 무조건 따라야 할 규칙이 아닙니다.

Redux 없이도 가능한 경우

Redux 없이도 Redux의 아이디어를 적용할 수 있으며, 지역 상태만으로도 충분한 경우가 많습니다. Redux는 “무슨 일이 일어났는가”와 “어떻게 변하는가”를 분리하기 위해 간접성을 추가하는데, 이것이 항상 좋은 것은 아닙니다.

Redux를 사용할 때는 그 트레이드오프가 적절한지 신중히 고려해야 하며, 그에 따른 이점을 실제로 얻고 있는지 점검해야 합니다.

예제 코드

아래는 라이브러리 없이 useReducer를 통해 간단한 Redux를 구현하는 코드입니다.

import React, { useReducer } from 'react'
 
const counter = (state = { value: 0 }, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 }
    case 'DECREMENT':
      return { value: state.value - 1 }
    default:
      return state
  }
}
 
const Counter = () => {
  const [state, dispatch] = useReducer(counter, { value: 0 })
 
  const increment = () => {
    dispatch({ type: 'INCREMENT' })
  }
 
  const decrement = () => {
    dispatch({ type: 'DECREMENT' })
  }
 
  return (
    <div>
      {state.value}
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}
 
export default Counter

Redux 라이브러리 자체는 리듀서를 단일 전역 스토어 객체에 “마운트”하는 헬퍼 세트일 뿐입니다. Redux를 원하는 만큼 적게 또는 많이 사용할 수 있습니다.