본문 바로가기
Frontend/React

React 상태 관리 최적화: useState에서 useReducer로 전환하기

by Lizzie Oh 2025. 2. 3.

useState에서 useReducer로의 전환: 복잡한 상태 관리 최적화

React 애플리케이션에서 상태를 관리하는 방법은 다양하다. 가장 기본적인 상태 관리 방법으로는 useState가 있지만, 상태 로직이 복잡해지거나 관련된 액션이 많아질 경우 useReducer를 활용하는 것이 더 효율적이다. 이 글에서는 useState와 useReducer의 차이점과 useReducer를 적용하는 방법에 대해 살펴본다.


1. useState vs useReducer

useState

useState는 React 컴포넌트 내부에서 상태를 관리하는 가장 간단한 방법이다.
그러나 상태 로직이 복잡해질수록 useState를 여러 개 사용하는 경우가 늘어나며, 상태 업데이트 로직이 분산될 가능성이 있다.

useState 예제

import { useState } from "react";

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

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

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

위와 같은 단순한 카운터 기능에서는 useState가 적합하다.
그러나 상태 변경 방식이 많아지거나 여러 상태가 상호작용할 경우, useState로는 관리가 어려워질 수 있다.


useReducer

useReducer는 상태 로직이 복잡한 경우여러 개의 관련 액션이 존재하는 경우에 useState보다 적합하다.
useReducer는 현재 상태(state)액션(action) 을 받아 새로운 상태를 반환하는 리듀서(reducer) 함수를 사용하여 상태를 관리한다.

useReducer의 기본 사용법

const [state, dispatch] = useReducer(reducer, initialState);
  • reducer : 상태를 변경하는 함수
  • initialState : 초기 상태 값
  • dispatch(action) : 특정 액션을 실행하기 위한 함수

2. useReducer로 상태 관리하기

리듀서(reducer) 함수란?

리듀서는 순수 함수(pure function) 로, 현재 상태(state)와 액션(action)을 받아서 새로운 상태(new state) 를 반환하는 역할을 한다.
즉, 상태 변경 로직을 한 곳에서 관리할 수 있도록 해준다.

리듀서 함수 예제

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

위 함수는 단순한 카운터 상태를 관리하며, INCREMENT와 DECREMENT 액션을 처리한다.


3. useReducer 적용하기

이제 useReducer를 활용하여 useState를 대체하는 예제를 살펴보자.

useReducer를 활용한 카운터 예제

import { 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:
      return state;
  }
}

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

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
    </div>
  );
}

useReducer의 동작 과정

  1. 초기 상태 (initialState) 를 설정한다.
  2. useReducer를 호출하여 [state, dispatch]를 반환받는다.
  3. 상태를 변경하려면 dispatch({ type: "INCREMENT" }) 와 같은 방식으로 액션을 실행한다.
  4. reducer 함수가 실행되며, 기존 상태(state)와 액션(action)을 기반으로 새로운 상태를 반환한다.

4. 언제 useReducer를 사용해야 할까?

✅ useReducer가 적합한 경우

  1. 상태 로직이 복잡한 경우
    • 여러 개의 상태를 함께 업데이트해야 할 때
    • 상태가 특정 로직에 따라 변해야 할 때
  2. 서로 관련된 액션이 존재할 경우
    • 여러 개의 상태가 동시에 변경될 때
    • 상태 변경이 단순한 값 수정이 아니라 다양한 액션을 포함할 때
  3. 상태 변경 로직을 한 곳에서 관리하고 싶을 때
    • 상태 로직을 분리하여 유지보수성을 높이고 싶을 때
    • 상태 변경 과정을 예측 가능하게 만들고 싶을 때

❌ useState가 더 적합한 경우

  • 단순한 상태 관리 (예: boolean 값 토글, 단일 입력 필드 관리)
  • 독립적인 상태가 여러 개 존재하는 경우

5. useState에서 useReducer로의 전환 예제

기존 useState 기반 코드:

const [count, setCount] = useState(0);
const [isActive, setIsActive] = useState(false);

const increment = () => setCount(count + 1);
const toggleActive = () => setIsActive(!isActive);

위와 같이 여러 개의 useState를 사용하는 경우, useReducer를 활용하여 다음과 같이 변경할 수 있다.

useReducer로 전환 후:

const initialState = { count: 0, isActive: false };

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "TOGGLE_ACTIVE":
      return { ...state, isActive: !state.isActive };
    default:
      return state;
  }
}

const [state, dispatch] = useReducer(reducer, initialState);

<button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
<button onClick={() => dispatch({ type: "TOGGLE_ACTIVE" })}>
  {state.isActive ? "Deactivate" : "Activate"}
</button>

이처럼 useReducer를 활용하면 상태 변경 로직을 하나의 리듀서 함수로 관리할 수 있으며, 여러 개의 useState를 사용할 필요가 없어진다.


결론

useState와 useReducer는 각각의 용도가 있다.
단순한 상태는 useState로 관리하는 것이 효율적이지만, 복잡한 상태 로직이 필요한 경우 useReducer를 활용하면 더 체계적인 상태 관리가 가능하다.

React 애플리케이션을 개발할 때 상태 변경 방식이 복잡해진다면 useReducer를 고려해보는 것이 좋은 선택이 될 수 있다.

 

반응형