React에서 useReducer를 사용하는 이유

🧠 React에서 useReducer를 사용하는 이유와 TypeScript로 구현하는 방법

현대적인 리액트(React) 애플리케이션에서는 컴포넌트의 상태(state)를 관리할 때 useState 훅을 가장 많이 사용하지만, 상태가 복잡해지거나 여러 상태 값들이 서로 연관되어 있을 경우에는 useReducer 훅을 사용하는 것이 더 적합하며, 이러한 패턴은 Redux와 유사한 액션 기반 상태 관리 방식으로도 확장 가능하다는 점에서 큰 장점을 가집니다. 특히 타입 안정성을 확보하고, 유지보수 가능한 코드를 작성하고자 할 때는 TypeScript를 도입하여 상태 객체와 액션 객체에 타입을 명확하게 정의해주는 것이 매우 효과적입니다.


📦 useReducer 기본 구조와 타입스크립트 도입

먼저 타입스크립트와 함께 사용할 때, 상태(state)와 액션(action)에 대해 명확한 타입을 정의하는 것이 핵심입니다.

<pre class="wp-block-syntaxhighlighter-code">import React, { useReducer } from 'react';

// 상태의 타입을 정의
type State = {
  count: number;
};

// 액션의 타입을 정의
type Action = 
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

// reducer 함수: 상태와 액션을 받아서 새로운 상태를 반환
function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: action.payload };
    default:
      return state;
  }
}

// 초기 상태 정의
const initialState: State = { count: 0 };

const Counter: React.FC = () => {
  // useReducer 훅 사용
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <h1>현재 카운트 값: {state.count}</h1>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>리셋</button>
    </div>
  );
};

export default Counter;
</pre>

💡 이 코드에서 주목할 점

  • StateAction 타입을 명시함으로써, 코드의 가독성과 유지보수성이 크게 향상됩니다.
  • reducer 함수는 순수 함수로, 동일한 입력이 주어졌을 때 항상 동일한 출력을 보장합니다. 이 점은 테스트를 작성할 때 매우 유리하게 작용합니다.
  • useReducer를 통해 dispatch 함수를 통해 액션을 발생시키고, 이는 reducer 함수에 전달되어 상태를 업데이트하게 됩니다.
  • reset 액션은 payload를 통해 초기화할 값을 외부에서 주입받을 수 있도록 설계되어 있어 유연성이 높습니다.

🧩 언제 useReducer를 사용하면 좋을까?

  1. 상태 업데이트 로직이 복잡한 경우
    예를 들어, 여러 액션 타입이 있고 그에 따른 상태 변화가 많다면 useReducer로 코드 구조를 분리하는 것이 훨씬 명확합니다.
  2. 컴포넌트 내에서 여러 상태 값이 동시에 존재하고, 이 상태들 간에 관계가 있는 경우
    useState를 여러 번 사용하는 것보다 useReducer 하나로 통합하는 것이 깔끔합니다.
  3. 상태 변경 로직을 외부로 추출해서 테스트하고 싶을 때
    reducer 함수는 순수 함수이기 때문에 별도로 테스트 코드 작성이 쉬우며, 단위 테스트에 유리합니다.
  4. 상태 변경을 명시적이고 예측 가능한 방식으로 처리하고 싶을 때
    액션 타입을 문자열로 명확하게 정의하고 switch문으로 처리하면, 디버깅이나 유지보수가 쉬워집니다.

Leave a Comment