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의 동작 과정
- 초기 상태 (initialState) 를 설정한다.
- useReducer를 호출하여 [state, dispatch]를 반환받는다.
- 상태를 변경하려면 dispatch({ type: "INCREMENT" }) 와 같은 방식으로 액션을 실행한다.
- reducer 함수가 실행되며, 기존 상태(state)와 액션(action)을 기반으로 새로운 상태를 반환한다.
4. 언제 useReducer를 사용해야 할까?
✅ useReducer가 적합한 경우
- 상태 로직이 복잡한 경우
- 여러 개의 상태를 함께 업데이트해야 할 때
- 상태가 특정 로직에 따라 변해야 할 때
- 서로 관련된 액션이 존재할 경우
- 여러 개의 상태가 동시에 변경될 때
- 상태 변경이 단순한 값 수정이 아니라 다양한 액션을 포함할 때
- 상태 변경 로직을 한 곳에서 관리하고 싶을 때
- 상태 로직을 분리하여 유지보수성을 높이고 싶을 때
- 상태 변경 과정을 예측 가능하게 만들고 싶을 때
❌ 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를 고려해보는 것이 좋은 선택이 될 수 있다.

'Frontend > React' 카테고리의 다른 글
브라우저의 렌더링 과정과 React의 최적화 기법 (0) | 2025.01.31 |
---|---|
리액트 useEffect 제대로 이해하고 똑똑하게 사용하기 - useEffect의 정의 (1) | 2024.04.03 |
리액트 useState 똑똑하게 쓰기 - lazy Initialization (게으른 초기화) (0) | 2024.03.27 |
댓글