icon안동민 개발노트

useReducer 훅 소개


 useReducer는 React의 내장 훅 중 하나로, 복잡한 상태 로직을 관리하는 데 사용됩니다.

 이 훅은 특히 여러 하위 값을 포함하는 복잡한 상태 객체를 다룰 때 유용합니다.

useReducer의 개념과 사용 목적

 useReducer는 현재 상태와 액션 객체를 받아 새로운 상태를 반환하는 리듀서 함수를 기반으로 작동합니다.

 이는 Redux의 패턴과 유사하며, 다음과 같은 목적으로 사용됩니다.

  1. 복잡한 상태 로직 관리
  2. 관련된 상태 변화를 그룹화
  3. 상태 변화의 예측 가능성 향상

useState와의 차이점

 useState가 단순한 상태 관리에 적합하다면, useReducer는 다음과 같은 경우에 더 적합합니다.

  1. 상태 구조가 복잡할 때
  2. 다음 상태가 이전 상태에 의존적일 때
  3. 여러 상태를 함께 업데이트해야 할 때

useReducer 사용 방법

 useReducer의 기본 구조는 다음과 같습니다.

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

 여기서,

  • state는 현재 상태
  • dispatch는 액션을 발생시키는 함수
  • reducer는 상태를 업데이트하는 함수
  • initialState는 초기 상태

Reducer 함수 작성 방법

 Reducer 함수는 현재 상태와 액션을 인자로 받아 새로운 상태를 반환합니다.

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

Action의 구조

 Action은 일반적으로 type 프로퍼티를 가진 객체입니다.

 추가 데이터가 필요한 경우 다른 프로퍼티를 포함할 수 있습니다.

{ type: 'INCREMENT' }
{ type: 'SET_COUNT', payload: 5 }

Dispatch 함수 사용법

 dispatch 함수는 액션 객체를 인자로 받아 리듀서 함수를 실행시킵니다.

dispatch({ type: 'INCREMENT' });
dispatch({ type: 'SET_COUNT', payload: 5 });

복잡한 상태 관리 예제

 다음은 useReducer를 사용하여 할 일 목록을 관리하는 예제입니다.

import React, { useReducer } from 'react';
 
const initialState = { todos: [] };
 
function reducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return { todos: [...state.todos, action.payload] };
    case 'TOGGLE_TODO':
      return {
        todos: state.todos.map((todo, index) =>
          index === action.payload ? { ...todo, completed: !todo.completed } : todo
        )
      };
    case 'REMOVE_TODO':
      return {
        todos: state.todos.filter((_, index) => index !== action.payload)
      };
    default:
      throw new Error();
  }
}
 
function TodoApp() {
  const [state, dispatch] = useReducer(reducer, initialState);
 
  function addTodo(text) {
    dispatch({ type: 'ADD_TODO', payload: { text, completed: false } });
  }
 
  function toggleTodo(index) {
    dispatch({ type: 'TOGGLE_TODO', payload: index });
  }
 
  function removeTodo(index) {
    dispatch({ type: 'REMOVE_TODO', payload: index });
  }
 
  return (
    <div>
      {/* 할 일 목록 렌더링 및 상호작용 UI */}
    </div>
  );
}

useReducer 사용의 이점

  1. 예측 가능성 : 상태 변화가 리듀서 함수 내에서 명확하게 정의됩니다.
  2. 디버깅 용이성 : 각 액션과 그 결과로 인한 상태 변화를 쉽게 추적할 수 있습니다.
  3. 로직 분리 : 상태 업데이트 로직을 컴포넌트에서 분리할 수 있습니다.
  4. 테스트 용이성 : 리듀서 함수는 순수 함수이므로 테스트하기 쉽습니다.

주의사항

  1. 과도한 사용 주의 : 단순한 상태 관리에는 useState가 더 적합할 수 있습니다.
  2. 불변성 유지 : 리듀서 함수에서 상태를 업데이트할 때 항상 불변성을 유지해야 합니다.
  3. 복잡성 관리 : 리듀서 함수가 너무 복잡해지지 않도록 주의해야 합니다.

useReducer와 Context API의 결합

 useReducer와 Context API를 함께 사용하면 강력한 전역 상태 관리 솔루션을 만들 수 있습니다.

 다음은 간단한 예제입니다.

import React, { createContext, useReducer, useContext } from 'react';
 
const StateContext = createContext();
const DispatchContext = createContext();
 
const initialState = { /* 초기 상태 */ };
 
function reducer(state, action) {
  // 리듀서 로직
}
 
export function StateProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);
 
  return (
    <StateContext.Provider value={state}>
      <DispatchContext.Provider value={dispatch}>
        {children}
      </DispatchContext.Provider>
    </StateContext.Provider>
  );
}
 
export function useState() {
  return useContext(StateContext);
}
 
export function useDispatch() {
  return useContext(DispatchContext);
}
 
// 사용 예:
function SomeComponent() {
  const state = useState();
  const dispatch = useDispatch();
 
  // state와 dispatch 사용
}

 이 패턴을 사용하면 상태와 디스패치 함수를 별도의 컨텍스트로 제공하여, 불필요한 리렌더링을 방지하고 성능을 최적화할 수 있습니다.

 useReducer는 React 애플리케이션에서 복잡한 상태 로직을 관리하는 강력한 도구입니다.

 특히 여러 관련 상태를 함께 업데이트해야 하거나, 상태 변화가 이전 상태에 의존적인 경우에 유용합니다.

 useState에 비해 더 구조화된 접근 방식을 제공하며, 상태 변화의 예측 가능성과 디버깅 용이성을 향상시킵니다.

 그러나 모든 상황에 useReducer가 적합한 것은 아닙니다. 단순한 상태 관리의 경우 useState가 여전히 좋은 선택일 수 있습니다. 상태 관리의 복잡성과 애플리케이션의 요구사항을 고려하여 적절한 훅을 선택해야 합니다.

 useReducer와 Context API를 결합하면 Redux와 유사한 전역 상태 관리 솔루션을 구현할 수 있습니다.

 이 접근 방식은 특히 중간 규모의 애플리케이션에서 유용할 수 있으며, 외부 라이브러리 없이도 효과적인 상태 관리를 가능하게 합니다.

 결론적으로, useReducer는 React의 상태 관리 도구 키트에 중요한 추가 사항입니다. 이를 효과적으로 활용하면 더 체계적이고 유지보수가 용이한 React 애플리케이션을 개발할 수 있습니다.