본문 바로가기

내직업은 IT종사자/javascript

Reducer 개념 및 구조와 예시 코드, 어떤 상황일때 사용해야 하는 걸까?

반응형

 

 

 

 

 

Reducer란?


reducer상태(state)를 업데이트하는 함수로, 주로 ReduxuseReducer 훅을 사용할 때 등장하는 개념입니다.

 

 

기본개념


  • reducer는 현재 상태(state)와 액션(action)을 받아서 새로운 상태를 반환하는 순수 함수입니다.
  • 기존 상태를 직접 변경하는 게 아니라, 새로운 상태를 반환하는 방식으로 동작합니다.

 

기본 구조


function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state; // 기본적으로 현재 상태를 유지
  }
}

 

위에서 state는 현재상태를 가르키며,

action은 상태를 변경하는 객체 형태(예를들면 { type: "INCREMENT" } )입니다.

switch 문을 사용해서 action.type에 따라 새로운 상태를 반환 합니다.

 

 

 

React useReducer  훅에서 사용


react에서 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(reducer, initialState)를 사용하면 상태(state)와 상태를 변경하는 dispatch함수가 반환됩니다.

- dispatch({type: 'INCREMENT'}) 를 호출하면 reducer가 실행되어 상태가 업데이트 됩니다.

 

 

 

Redux에서 사용


redux에서는 reducer 가 전체 어플리케이션의 상태를 관리하는 역할을 합니다.

 

const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

// Redux Store 생성
const store = createStore(counterReducer);

 

redux에서는 store를 생성할때  reducer(위에서 counterReducer) 를 넘겨주면 전체 상태 관리를 할 수 있습니다.

 

 

 

 

그렇다면 언제 Reducer를 사용해야할까요?


- useState로 관리하기 어려운 복잡한 상태를 다룰때 useReducer 활용

- Redux를 사용할 때 전역 상태를 관리할 때

- 상태 변경 로직을 한 곳에서 관리하고 싶을 때

 

👉  단순한 상태(Boolean 값이나 간단한 string)는 useState로 충분하지만, 다양한 액션에 따라 상태가 변하는 경우에는 useReducer나 redux의 reducer를 사용하는게 좋다

👉 여러 개의 상태(state)를 관리해야 하거나, 상태 변경이 여러 액션(action)에 따라 달라지는 경우

 

 

1) useState로 관리하기 복잡한 경우

예: 폼 입력 상태관리

// AS-IS
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");




// TO-BE

const initialState = {
  name: "",
  email: "",
  password: "",
};

function reducer(state, action) {
  return {
    ...state,
    [action.field]: action.value,
  };
}

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


<input
  type="text"
  value={state.name}
  onChange={(e) => dispatch({ field: "name", value: e.target.value })}
/>;

 

여러개의 useState를 관리하는 대신, 하나의 reducer로 여러 상태를 한꺼번에 업데이트 가능합니다.

 

 

2) 다양한 액션에 따라 상태가 변하는 경우

예1: 모달 상태관리 (모달이 여러개인 경우)

const initialState = {
  isLoginModalOpen: false,
  isSignupModalOpen: false,
};

function reducer(state, action) {
  switch (action.type) {
    case "OPEN_LOGIN":
      return { ...state, isLoginModalOpen: true, isSignupModalOpen: false };
    case "OPEN_SIGNUP":
      return { ...state, isSignupModalOpen: true, isLoginModalOpen: false };
    case "CLOSE_ALL":
      return { ...state, isLoginModalOpen: false, isSignupModalOpen: false };
    default:
      return state;
  }
}

 

 

- OPEN_LOGIN, OPEN_SIGNUP, CLOSE_ALL 같은 다양한 액션을 처리할 수 있음

-  useState를 각각 만들어서 관리하면 코드가 길어지지만, useReducer를 사용하면 더 깔끔하게 관리 가능

 

 

 

예2: 복잡한 상태변화( 로딩, 성공, 에러상태)

const initialState = {
  loading: false,
  data: null,
  error: null,
};

function reducer(state, action) {
  switch (action.type) {
    case "FETCH_START":
      return { loading: true, data: null, error: null };
    case "FETCH_SUCCESS":
      return { loading: false, data: action.payload, error: null };
    case "FETCH_ERROR":
      return { loading: false, data: null, error: action.error };
    default:
      return state;
  }
}

// 사용 예시
dispatch({ type: "FETCH_START" });
fetchData()
  .then((data) => dispatch({ type: "FETCH_SUCCESS", payload: data }))
  .catch((error) => dispatch({ type: "FETCH_ERROR", error }));

 

 

결론


▷ 상태가 단순한 boolean이나 string이라면 useState 사용

▷ 여러 상태를 한 번에 관리해야 하거나, 액션이 다양하면 useReducer가 더 효율적

▷  비동기 처리(loading, success, error` 상태 관리)나 다양한 UI 상태 관리(모달, 폼, 토글 등)에 적합

🔥 결론: 상태 업데이트 로직이 점점 복잡해진다면 useReducer를 고려하는 게 좋다! 🔥

반응형