들어가며😃


프로젝트를 진행하며 상태관리를 모두 useState로 했었다. 그러다보니 어마무시한 props drilling 현상이 나타났는데…🤢 이를 해결할 수 있는 방법을 찾다가 useReducer()와 useContext()에 대한 개념을 배우게 되었다. 오늘은 우선 useReducer() 훅에 대해 배운 개념들을 정리해보려한다.


useReducer()란?


useReducer는 React에서 제공하는 Hook 중 하나로, 상태 관리를 위해 사용된다.


엥? 이미 useState 가 있는데요?🤷🏻‍♀️

그렇다. 이미 useState 라는 상태관리 훅이 있고, 이와 비슷하게 state를 관리하고 업데이트할 수 있는 hook이다.


그럼 어떤 점이 다른걸까?🤷🏻‍♀️

useReducer는 useState와 비슷한 목적을 가지고 있지만, 상태가 복잡하고 다양한 액션에 대응해야 할 때 더 적합하다. useState는 간단한 상태 값의 관리에 사용된다. 예를 들어, 숫자를 증가시키거나 감소시키는 경우.

반면에 useReducer는 복잡한 상태 관리를 위해 사용된다. 여러 개의 상태 값이 필요하거나 상태 간의 의존성이 있거나 다양한 액션을 처리해야 하는 경우.
⭐️ 또한, 매우 매력적인 것은 useReducer를 사용하면 관련 로직을 한 곳에 모으고 컴포넌트 밖에 분리할 수 있어 Props Drilling을 막을 수 있다!

로직에서도 분명한 차이가 있다. useState의 경우 상태값을 직접적으로 변경시켜야하지만, useReducer의 경우 action(요구사항)을 전달하고 그 action 값에 따라 값을 어떻게 처리하고 보낼 것인지에 대한 함수를 만들어줘야한다. 조금 더 쉽게 이해하기 위해 아래를 통해 비교해보자.



기존 useState() 사용 로직


자 그럼 본격적으로 useReducer() 함수 사용법을 알아보기 전에 우선 익숙한 useState()를 사용하여 코드를 짜보고, 이후 해당 코드를 useReducer()를 적용하여 변경해보겠다.

import React, { useState } from "react";
function App() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>+1</button>
      <button onClick={() => setCount((prev) => prev - 1)}>-1</button>
      <button onClick={() => setCount(0)}>reset</button>
    </div>
  );
}

count : 1

위의 코드는 아주 간단한 count 로직이다. 이를 useReducer() 함수를 사용해서 변경해보자.



useState() -> useReducer()


import React, { useReducer } from "react";
function App() {
  // 과정 2 : setState 와는 다르게 값을 직접 바꾸는 식이 아니라, 어떤 요구사항이 있는지, 어떻게 바꿀건지 action 값을 정해줌
  const up = () => {
    // 🗣️ 나 카운트 'up' 할꺼야!
    countDispatch("up");
  };
  const down = () => {
    // 🗣️ 나 카운트 'down' 할꺼야!
    countDispatch("down");
  };
  const reset = () => {
    // 🗣️ 나 카운트 'reset' 할꺼야!
    countDispatch("reset");
  };

  // 과정 3 : 현재 값과 action 값을 받아 어떻게 처리할 것 인지 정의
  const countReducer = (presentCount, action) => {
    if (action === "up") {
      return presentCount + 1;
    } else if (action === "down") {
      return presentCount - 1;
    } else return 0;
  };

  // 과정 1 : 선언부
  const [count, countDispatch] = useReducer(countReducer, 0);
  // count : 초기값,
  // countDispatch : 어떤 변경사항을 원하는지 전달 (up? down? reset?)0,
  // useReducer : 이 모든것을 가능하게 하는 훅
  // countReducer : 원하는 변경사항에 따라 값을 변경시킬 함수
  // 0 : 초기값

  return (
    <div>
      <div>{count}</div>
      <button onClick={up}>+1</button>
      <button onClick={down}>-1</button>
      <button onClick={reset}>reset</button>
    </div>
  );
}

count : 1

코드는 완전히 바뀌었지만, 작동되는 방식은 똑같다. 설명은 과정에 따라 이해하기 쉽도록 위의 코드에서 주석으로 처리해주었다. 우선 선언부가 필요하다. useReducer를 통해 초기값, 액션을 전달할 dispatch, 액션을 받아 실행할 reducer 를 정의해준다. 그리고 그 함수들을 구현하면 끝!


그리고 조금 더 업그레이드 시켜보자! 😀 지금 처럼 정해진 1로만 숫자를 카운드하는 방식이 아닌, 사용자가 원하는 값을 input 값에 넣고 그 값만큼 카운트 해주는 것이다.

import React, { useReducer, useState } from "react";
function App() {
  // 사용자가 설정한 값으로 number 상태를 변경해준다.
  const [number, setNumber] = useState(0);
  const onChangeNum = (e) => {
    setNumber(Number(e.currentTarget.value));
  };

  // 전달해 줄 상태값이 여러개라 객체로 전달한다.
  // 설정할 액션 값의 키 값은 일반적으로 type 이라는 이름을 많이 쓴다.
  const up = () => {
    countDispatch({ type: "up", number: number });
  };
  const down = () => {
    countDispatch({ type: "down", number: number });
  };
  const reset = () => {
    countDispatch({ type: "reset", number: number });
  };

  // reducer 함수가 아래의 number 값을 직접적으로 사용하는 것은 좋지않다. 리듀서 함수는 순수 함수여야하며 외부의 영향을 받으면 안된다.
  const countReducer = (presentCount, action) => {
    if (action.type === "up") {
      return presentCount + action.number;
      // 변경된 부분 : 1 -> state에 저장된 number 값
    } else if (action.type === "down") {
      return presentCount - action.number;
    } else return 0;
  };

  const [count, countDispatch] = useReducer(countReducer, 0);

  return (
    <div>
      <div>{count}</div>
      <button onClick={up}>+</button>
      <button onClick={down}>-</button>
      <button onClick={reset}>reset</button>
      <input
        placeholder="원하는 값"
        type="text"
        value={number}
        onChange={onChangeNum}
      />
    </div>
  );
}

count : 사용자 전달 값

위와 같이 사용자가 입력한 값을 dispatch 함수를 통해 전달받아 reducer 함수에서 상태가 변경되고 있다. 후훗 useReducer(),,아주 재밌는 녀석이다😎





날이 갈수록 배우는 개념, 배워야할 개념, 정리해야할 개념들이 쏟아진다. 그리고 느끼고 있는건 이거 없이도 프로젝트가 되네? 의 놀라움과 더불어 이거 해볼껄ㅜ 이라는 두가지의 언벨런스한 생각과 감정들이다. 앞으로 정리할 개념들이 남아있는데 useContext와 Redux를 좀 더 공부한 후에 프로젝트때 아쉬웠던 상태 관리부분을 다시 구현해봐야겠다. 여기까지 정리하며 오늘의 포스팅은 끝!🚀