개발/React

[React] useReducer 언제 써야 될까?

hr0513 2025. 2. 18. 16:16
728x90
반응형

useReducer는 상태가 여러 개일 때, 복잡한 상태 변화 로직을 처리할 때 유용한 React Hook입니다.

 

주로 여러 개의 상태 값을 한 번에 관리해야 할 때 사용되며,

상태 업데이트가 특정 규칙을 따르는 경우 유용하게 사용할 수 있습니다.

 

아래 예시는 useStateuseReducer를 각각 사용한 예제를 통해 그 차이를 비교해보겠습니다.

반응형

1. useState를 사용한 상태 관리

useState는 간단한 상태를 관리할 때 유용하지만,

상태가 많거나 여러 상태가 서로 연관되어 있을 경우 관리가 복잡해질 수 있습니다.

 

예를 들어, 데이터를 가져오는 과정에서 로딩 상태, 에러 상태,

그리고 실제 데이터 상태를 관리할 때 useState를 사용하면 아래와 같은 코드가 됩니다.

 

Post.jsx

import React from "react";
import { useState } from "react";

const Post = () => {
  const [loading, setLoading] = useState(false);
  const [post, setPost] = useState({});
  const [error, setError] = useState(false);

  const handleFetch = () => {
    setLoading(true);
    setError(false);

    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((res) => {
        return res.json();
      })
      .then((data) => {
        setPost(data);
        setLoading(false);
      })
      .catch((err) => {
        setError(true);
        setLoading(false);
      });
  };

  return (
    <div>
      <button onClick={handleFetch}>
        {loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{post?.title}</p>
      <span>{error && "Something went wrong!"}</span>
    </div>
  );
};

export default Post;

이 코드는 간단하지만,

상태가 많아지면 각 상태 업데이트를 개별적으로 처리해야 하므로 복잡도가 증가합니다.

 

특히, 상태 변화가 많아질수록 코드가 지저분해지고 유지보수가 어려워질 수 있습니다.


2. useReducer로 상태 관리 개선하기

useReducer는 상태 로직을 분리하고 중앙 집중식으로 관리할 수 있어,

상태가 많거나 복잡한 로직을 처리할 때 유리합니다.

 

아래는 useReducer를 사용한 코드입니다.

 

Post.jsx

import React, { useReducer } from "react";
import postReducer, { INITIAL_STATE } from "../hooks/postReducer";
import { ACTION_TYPES } from "../hooks/postActionTypes";

const Post = () => {
  const [state, dispatch] = useReducer(postReducer, INITIAL_STATE);

  const handleFetch = () => {
    dispatch({ type: ACTION_TYPES.FETCH_START });

    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((res) => res.json())
      .then((data) => {
        dispatch({ type: ACTION_TYPES.FETCH_SUCCESS, payload: data });
      })
      .catch((err) => {
        dispatch({ type: ACTION_TYPES.FETCH_ERROR });
      });
  };

  return (
    <div>
      <button onClick={handleFetch}>
        {state.loading ? "Wait..." : "Fetch the post"}
      </button>
      <p>{state.post?.title}</p>
      <span>{state.error && "Something went wrong!"}</span>
    </div>
  );
};

export default Post;

useReducer를 사용하면 상태 업데이트를 하나의 reducer 함수에서 처리하므로,

로직이 명확하게 분리되어 유지보수가 쉬워집니다.

 

dispatch를 통해 상태 변화에 대한 액션을 보낼 수 있고,

postReducer 함수가 상태 변경을 담당하게 됩니다.


reducer와 actionTypes 파일 분리

reducer와 actionTypes를 별도의 파일로 분리하면,

코드의 재사용성과 유지보수성이 더 좋아집니다.

 

postReducer.js

import React from "react";

export const INITIAL_STATE = {
  loading: false,
  post: {},
  error: false,
};

const postReducer = (state, action) => {
  switch (action.type) {
    case "FETCH_START":
      return {
        ...state,
        loading: true,
        error: false,
        post: {},
      };
    case "FETCH_SUCCESS":
      return {
        ...state,
        loading: false,
        post: action.payload,
      };
    case "FETCH_ERROR":
      return {
        ...state,
        error: true,
        loading: false,
        post: {},
      };
    default:
      return state;
  }
};

export default postReducer;

 

postActionTypes.js

export const ACTION_TYPES = {
  FETCH_START: "FETCH_START",
  FETCH_SUCCESS: "FETCH_SUCCESS",
  FETCH_ERROR: "FETCH_ERROR",
};

useReducer는 여러 상태를 다루고 복잡한 로직을 처리해야 할 때 매우 유용합니다.

 

useState로 간단한 상태를 다룰 수 있지만, 상태가 많아지면 useReducer를 사용해보세요.

이를 통해 코드가 더 깔끔하고 관리하기 쉬운 구조로 바뀔 수 있습니다.

 

참고 자료

https://www.youtube.com/watch?v=RZPAQV7JvNU

 

728x90
반응형