[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 13일차 - useReducer & Redux

강경서·2023년 7월 4일
0
post-thumbnail

🎲 useReducer

상태를 관리하게 될 때 useState 를 사용하는것 말고도 useReducer 를 사용할 수 있습니다. useReducer 를 사용하면 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있습니다. 상태 업데이트 로직을 컴포넌트 바깥에 작성 할 수도 있고, 심지어 다른 파일에 작성 후 불러와서 사용 할 수도 있습니다.


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

function reducer(state, action) {
  // 새로운 상태를 만드는 로직
  // const nextState = ...
  return nextState;
}

state는 컴포넌트에서 사용 할 수 있는 상태를 가르키게 되고, dispatch 는 액션을 발생시키는 함수입니다. useReducer 에 넣는 첫번째 parameter 는 reducer 함수이고, 두번째 parameter 는 초기 상태입니다. reducer 는 현재 state 와 action 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수입니다. 여기서 action 은 업데이트를 위한 정보를 가지고 있습니다.


  • counter.js
import React, { useReducer } from "react";

const initialState = 0;

const reducer = (state, action) => {
  switch (action.type) {
    case "DECRESE":
      return state - 1;
    case "INJCRESE":
      return state + 1;
    case "RESET":
      return initialState;
    default:
      return state;
  }
};

const Counter = () => {
  /*
    dispatch는 액션을 발생시키는 함수
    */
  const [count, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      <h1>숫자세기</h1>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: "DECRESE" })}>-</button>
      <button onClick={() => dispatch({ type: "INJCRESE" })}>+</button>
      <button onClick={() => dispatch({ type: "RESET" })}>reset</button>
    </>
  );
};

export default Counter;

이렇게 useReducer를 사용함으로써 state를 reducer 함수를 통해 분리하여 관리가 가능합니다.


🚀 Redux

Redux는 React 생태계에서 가장 사용률이 높은 상태관리 라이브러리입니다. Redux를 사용하면 컴포넌트들의 상태 관련 로직들을 다른 파일들로 분리시켜서 더욱 효율적으로 관리 할 수 있으며 글로벌 상태 관리도 손쉽게 할 수 있습니다. 또한 Redux는 React 뿐만 아니라 VUE 와도 잘 결합되며, 또한 순수 JS와도 결합이 됩니다.


Redux 3가지 규칙

  1. 하나의 애플리케이션 안에는 하나의 Store
    하나의 애플리케이션에선 단 한개의 Store 를 만들어서 사용합니다. 여러 개의 Store 를 사용하는 것은 사실 가능하지만 권장하지 않습니다.

  2. State 는 읽기전용
    useState 처럼 Redux 에서도 기존의 State 는 건들이지 않고 새로운 State 를 생성하여 업데이트를 해주는 방식으로 합니다. Redux 에서 불변성을 유지해야 하는 이유는 내부적으로 데이터가 변경 되는 것을 감지하기 위해서 입니다.

  3. 변화를 일으키는 함수, Redux 는 순수한 함수
    Redux 함수는 이전 State 와 액션 객체를 파라미터로 받습니다.
    이전의 State는 절대로 건들이지 않고, 변화를 일으킨 새로운 State 객체를 만들어서 반환합니다.


Redux 예제

React에서 Redux를 사용하기위해 "react-redux", Redux를 효율적으로 사용하기 위해 "@reduxjs/toolkit"와 "redux-logger" 라이브러리 설치를 통해 실습을 했습니다.


  • redux/todoSlice.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = [];

const todoSlice = createSlice({
  name: "Todo",
  initialState,
  reducers: {
    addTodoStore(state, action) {
      return [...state, { ...action.payload }];
    },
    removeTodoStore(state, action) {
      return state.filter((todo) => todo.id !== action.payload);
    },
    clearTodoStore(state, action) {
      return state.forEach((todo) => {
        if (todo.id === action.payload) {
          todo.clear = !todo.clear;
        }
      });
    },
  },
});

export const { addTodoStore, removeTodoStore, clearTodoStore } =
  todoSlice.actions;
export default todoSlice.reducer;

"@reduxjs/toolkit"의 createSlice 함수를 사용하여 reducer와 action을 만들 수 있습니다.


  • reduv/store.js
import { configureStore } from "@reduxjs/toolkit";
import todoSlice from "./todoSlice";
import logger from "redux-logger";

const store = configureStore({
  reducer: {
	TodoStore: todoSlice,
  },
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
});

export default store;

"@reduxjs/toolkit"의 configureStore 함수를 사용하여 store를 생성할 수 있습니다. store에는 createSlice를 통해 만든 reducer를 넣어 사용할 수 있습니다.


  • index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import store from "./redux/store";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

Redux를 전역으로 사용하기 위해 "react-redux"의 Proveider 에 만들어준 store를 넣어 컴포넌트를 감싸줍니다.


  • component/ReduxTodo.js
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
  addTodoStore,
  clearTodoStore,
  removeTodoStore,
} from "../redux/todoSlice";

const ReduxTodo = () => {
  const dispatch = useDispatch();
  const todos = useSelector((state) => state.TodoStore);
  const [value, setValue] = useState("");
  const addTodo = (event) => {
    event.preventDefault();
    dispatch(addTodoStore({ todo: value, id: Date.now(), clear: false }));
    setValue("");
  };

  return (
    <>
      <h1>할 일 목록</h1>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <span style={{ textDecoration: todo.clear && "line-through " }}>
              {todo.todo}
            </span>
            <button onClick={() => dispatch(clearTodoStore(todo.id))}></button>
            <button onClick={() => dispatch(removeTodoStore(todo.id))}></button>
          </li>
        ))}
      </ul>
      <form onSubmit={addTodo}>
        <input
          type="text"
          value={value}
          onChange={(event) => setValue(event.target.value)}
        />
        <input type="submit" value={"추가하기"} />
      </form>
    </>
  );
};

export default ReduxTodo;

Resuc의 state를 관리하기 위해서는 "react-redux"의 useDispatch 와 useSelector를 사용합니다. useDispatch는 export한 action을 이용하여 state를 변경할 수 있습니다. useSelector는 store의 reducer를 선택하여 컴포넌트에 state를 사용할 수 있습니다.


📝 후기

상태를 웹 전역에서 관리하고 사용할 수 있는 Redux를 배웠습니다. 덕분에 React 웹을 개발하는데 생겼던 불편한 부분을 효율적으로 관리할 수 있었습니다. 뿐만 아니라 더 다양한 기능을 추가할 수 있어 보였습니다. 이렇듯 점점 서비스가 가능한 웹에 가까워지는 작업물을 보니 개발자에 한걸음 더 가까워진것 같습니다.


🧾 Reference



본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.

#프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프

profile
기록하고 배우고 시도하고

0개의 댓글