React.js basic manual

React.js - useReducer - 리액트 유즈리듀서

lshjju 2025. 7. 15. 23:02

React useReducer 란 무엇인가요?


useState는 간단한 상태 관리에 유용합니다.

useReducer는 조금 더 복잡하고 체계적인 상태 관리에 사용됩니다.

 




1. useReducer는 왜 필요한가요? (useState의 한계)


useState Hook은 간단한 숫자, 문자열, 불리언 값 등 하나의 독립적인 상태를 관리할 때 매우 편리합니다.

하지만 다음과 같은 상황에서는 코드가 복잡해질 수 있습니다.



여러 개의 상태가 서로 연관되어 있을 때: 

예를 들어, 게시물의 제목, 내용, 작성자, 태그 등 여러 상태가 한 번에 변경되어야 할 때 useState를 여러 번 사용하면 코드가 길어지고 관리하기 어려워질 수 있습니다.



상태 변경 로직이 복잡할 때: 

어떤 상태의 변경이 다른 상태에 영향을 미치거나, 상태 변경 로직 자체가 복잡해지면 컴포넌트 내부에서 모든 로직을 처리하기가 부담스러워집니다.



useReducer는 바로 이런 경우에 상태(State)를 업데이트하는 복잡한 로직을 컴포넌트로부터 분리시켜, 코드를 더 깔끔하고 관리하기 쉽게 만들어줍니다.  



2. useReducer는 무엇인가요? (체계적인 상태 관리 도우미)


useReducer는 React의 Hook 중 하나로, 컴포넌트의 상태를 더 체계적으로 관리하게 해주는 도구입니다. 

Redux(리덕스)와 같은 상태 관리 라이브러리의 핵심 개념인 '리듀서 패턴'을 React에서 바로 사용할 수 있도록 해줍니다. 


핵심 개념:



Reducer (리듀서): 

상태를 실제로 어떻게 변경할지 결정하는 '함수'입니다. 

현재 상태(State)와 어떤 일이 발생했는지 알려주는 '액션(Action)'을 받아서 새로운 상태를 반환합니다.



Dispatch (디스패치): 

상태 변경을 '요청'하는 함수입니다. 

이 함수에 '액션(Action)' 객체를 전달하면, React는 그 액션을 리듀서로 보냅니다.



Action (액션): 

어떤 상태 변경이 일어나야 하는지를 설명하는 '객체'입니다. 

주로 type이라는 속성으로 어떤 종류의 변경인지 나타내고, 필요하면 추가 데이터도 포함합니다.



State (상태): 

컴포넌트가 가지고 있는 데이터입니다.

 



3. 비유로 이해하기 (빵집 주방장과 주문서)


useReducer의 작동 방식은 빵집에서 빵을 만드는 과정과 비슷합니다. 


손님 (컴포넌트): 

빵을 주문하고 싶습니다.



주문서 작성 (액션): 

"크루아상 하나 주세요!" 라고 주문서(action 객체)를 작성합니다.



주문서 전달 (dispatch): 

작성한 주문서를 주방장에게 전달(dispatch)합니다.



주방장 (리듀서): 

주방장은 현재 빵 재료(state)와 손님이 작성한 주문서(action)를 확인합니다. 

"크루아상 주문이네? 그럼 밀가루, 버터, 설탕을 반죽해서... 오븐에 굽자!" 라고 결정하고 빵을 만듭니다. 

그리고 새로운 빵(new state)을 손님에게 제공합니다.


여기서 주방장이 바로 reducer 역할을 하는 것이고, dispatch는 주문서를 주방장에게 보내는 행위, action은 주문서 내용, 그리고 state는 현재의 빵 재료 및 만들어진 빵이라고 생각하면 됩니다.



4. useReducer 사용 방법


useReducer Hook은 state와 dispatch 함수를 반환하며, reducer 함수와 initialState (초기 상태)를 인자로 받습니다.

import React, { useReducer } from 'react';

// 1. Reducer 함수 정의
// 이 함수는 '상태를 어떻게 바꿀지' 정의합니다.
// currentState: 현재 상태
// action: 어떤 일이 발생했는지 (객체 형태, 주로 type 속성 포함)
function reducer(currentState, action) {
  switch (action.type) {
    case 'INCREMENT': // 'INCREMENT' 액션이 오면
      return { count: currentState.count + 1 }; // count를 1 증가시킨 새 상태 반환
    case 'DECREMENT': // 'DECREMENT' 액션이 오면
      return { count: currentState.count - 1 }; // count를 1 감소시킨 새 상태 반환
    case 'RESET': // 'RESET' 액션이 오면
      return { count: 0 }; // count를 0으로 초기화한 새 상태 반환
    default:
      throw new Error(); // 알 수 없는 액션은 에러 발생
  }
}

// 2. 컴포넌트에서 useReducer 사용
function Counter() {
  // useReducer(reducer 함수, 초기 상태)
  // useReducer는 [현재 상태, dispatch 함수]를 반환
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>현재 카운트: {state.count}</p>
      {/* 3. dispatch 함수를 호출하여 액션 전달 */}
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>증가</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>감소</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>초기화</button>
    </div>
  );
}

export default Counter;

jsx



5. useReducer를 사용해야 할 때 (장점)



복잡한 상태 로직: 

여러 하위 값으로 구성된 복잡한 상태를 관리해야 할 때 유용합니다. 



서로 관련된 상태: 

한 상태의 변경이 다른 상태에 영향을 미치거나, 여러 상태가 동시에 변경되어야 할 때 좋습니다.



상태 로직 분리: 

상태 업데이트 로직을 컴포넌트 밖으로 분리하여 코드를 더 깔끔하게 유지할 수 있습니다. 



재사용성: 

동일한 상태 로직을 여러 컴포넌트에서 재사용할 수 있습니다.



결론



useReducer는 useState보다 조금 더 복잡하게 느껴질 수 있지만, 상태 관리가 복잡해질수록 그 진가를 발휘하는 강력한 도구입니다.