그래서 리덕스가 뭔데?

손준호·2023년 5월 29일

React

목록 보기
3/11
post-thumbnail

Redux와 Redux Toolkit


최고 리덕스야 고맙다! Redux & Redux Toolkit 알아보기

sweet_pumpkin 님의

최고 리덕스야 고맙다!

글을 보고 포스팅 한 글입니다.

❓왜❓


리액트를 다루는 기술을 공부한지 약 2달째, 드디어 상태관리 라이브러리 리덕스를 공부하게 되었다.

액션이 뭐고, 디스패치가 뭐고, 리듀서에 스토어에 어쩌구저쩌구,,,,

일단 따라해보면서 해보긴 했는데 전체적인 리덕스의 흐름을 이해하지 못하고 공부하니 상당히 어려웠다.

그래서 리덕스의 흐름과, 상태관리의 중요성을 먼저 알아보고자 한다.

리덕스가 뭔데?


현재 가장 많이 쓰이고 있는 리액트의 상태 관리 라이브러리 중 하나이다.

여기서 말하는 상태란 변하는 값, 즉 state 를 말하며 상태관리란 이런 상태들을 관리하는 것이다.

UI와 UX에 맞게 데이터를 관리하거나, 서버와 주고 받는 데이터를 관리하는 것을 말한다.

큰 프로젝트에서 상태 관리는 굉장히 어렵다고 한다.

A 컴포넌트의 함수를 props 형태로 하위 컴포넌트인 B컴포넌트 에게 전달하고, 이것을 props 형태로 B의 하위 컴포넌트인 C에게 전달했다고 해보자.

이런 구조에서는 A의 상태에 변화가 생겨 리렌더링이 필요할 때, B는 A에게 props를 전달받으므로 B도 리렌더링 해아하고, B는 C로 props를 전달하므로 C도 리렌더링 해야한다.

B와 C는 변화된 부분이 없어 리렌더링 할 필요가 없는데도 말이다.

이를 Props drilling 이라고 한다.

Props Drilling 이 뭔데?


App 컴포넌트에서 간단히 Props Drilling 을 테스트해보자.

import logo from "./logo.svg";
import "./App.css";

function App() {
  return (
    <div>
      <First content="props" />
    </div>
  );
}

function First({ content }) {
  return (
    <div>
      <h2>First Component</h2>;
      <Second content={content} />
    </div>
  );
}

function Second({ content }) {
  return (
    <div>
      <h3>Second Component</h3>;
      <Third content={content} />
    </div>
  );
}

function Third({ content }) {
  return (
    <div>
      <h4>Third Component</h4>;
    </div>
  );
}

export default App;

위 코드에서 가장 하위 컴포넌트인 Third 컴포넌트는 content props 를 사용하기 위해 second에서 props를 전달받는다.

second는 first에서 전달받는다. first는 App에서부터 전달받는다.

즉, App → First → Second → Third 순으로 props가 전달되는 것이다.

만약, 아래처럼 Third가 변경된다고 해보자.

image

Third 는 변경되었으므로, RealDOM과 React Virtual DOM을 비교해 변화된 부분인 Third 컴포넌트를 리렌더링 할 것이다.

그런데 Third가 리렌더링 되면서 Third에 props를 전달하는 Second가 리렌더링 되고, Second에 props 를 전달하는 First가 리렌더링 되고, First에 Props를 전달하는 App이 리렌더링 된다.

App, First, Second 는전혀 변화된 점이 없는데도 말이다.

이런 문제를 해결하기 위해 상태관리 라이브러리가 등장하였다.

image

위 그림은 기존 상태관리의 시각화 자료이다.

하위 컴포넌트에서 변화가 발생하였다. 이를 알리기 위해 props를 전달하는 상위 컴포넌트에 변화가 있음을 알리고, 상위 컴포넌트는 리렌더링을 함으로써 그 하위 컴포넌트들이 모두 리렌더링 되는것이다.

image (1)

위 자료는 리덕스의 Reducer와 Store를 통해 상태를 보다 쉽게 관리하는 모습이다.

리듀서의 상태관리 시나리오는 아래와 같다.

리덕스의 사용 시나리오를 알아보기 전에 리덕스의 용어를 먼저 알아보자.

Store


스토어는 컴포넌트의 상태를 관리하는 저장소이다.

하나의 프로젝트에는 한 개의 스토어만 가질 수 있다.

redux를 설치하고 createStore() 함수를 이용해 만들 수 있다.

Action


스토어의 상태를 변경하기 위해 사용하는 객체이다.

Action 은 객체이고, 반드시 type이 필요하다.

액션 객체는 액션 생성 함수로 만든다.

Reducer


리듀서 함수는 현재 상태와 액션 객체를 받아 새로운 상태를 반환하는 함수이다.

Dispatch


Dispatch는 스토어의 내장 함수이다.

액션 객체를 넘겨 상태를 업데이트 한다.

Subscribe


스토어의 내장 함수 중 하나이다.

리듀서 함수가 호출될 때 구독된 함수 및 객체를 호출한다.

지아이에프

위 그림으로 리덕스의 사용 시나리오를 알아보자.

1. UI 가 최초로 렌더링 된다. 이 UI 컴포넌트는 리덕스 스토어의 상태에 접근해 상태를 렌더링한다.

2. UI에서 상태가 변경되면, 앱이 Dispatch를 실행해 상태가 업데이트된 액션객체를 반환한다.

3. 업데이트 된 액션 객체를 받은 스토어가 Reducer 함수를 실행한다. 이 리듀서 함수는 새로운 상태를 반환한다.

4. Store 를 구독하고있는 UI 컴포넌트는 업데이트된 상태를 새롭게 렌더링 한다.

RTK 사용하기


npm i @reduxjs@toolkit

위 명령어로 리덕스 툴킷을 설치하자.

import logo from "./logo.svg";
import "./App.css";
import {useState} from "react";

function App() {
  const [counter, setCounter] = useState(0);


  const plus = () => {
    setCounter(prev => prev + 1);
  };
  const minus = () => {
    setCounter(prev => prev - 1);
  };
  return (
    <div>
      <button onClick={minus}>-1</button>
      Value: {counter}
      <button onClick={plus}>+1</button>

    </div>
  );
}

export default App;

기본적인 useState를 활용한 카운터 예제이다.

Store 연동


Provider 컴포넌트는 react - redux에서 리액트 프로젝트에 연동할 수 있게 해주는 컴포넌트이다.

Provider 컴포넌트를 불러와 연동할 컴포넌트를 감싸고, Provider props로 사용할 스토어를 지정해주자.

<ApiProvider store={{ store }}>
  <App />
</ApiProvider>

Store 만들기


import { configureStore } from "@reduxjs/toolkit";

export const store = configureStore({
  reducer: counterSlice,
  middleware: [...middlwares],
});

configureStore() 로 store.js 파일에 스토어를 만들어주자.

configureStore()는 별도의 메서드 없이 바로 미들웨어를 추가할 수 있다.

createSlice 생성


import React from "react";
import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    plus: (state) => {
      state.value += 1;
    },
    minus: (state) => {
      state.value -= 1;
    },
  },
});
export const { plus, minus } = counterSlice.actions;
export default counterSlice.reducer;

기존 리덕스에서는 액션의 상태를 업데이트하기 위해 디스패치를 호출할 때, 별도의 메서드가 필요했다.

또한 액션 객체를 리듀서를 통해 리턴하는 구조였다.

createSlice() 메서드는 액션에 대한 함수 설정과 리듀서를 따로 생성하지 않아도 된다.

import React from "react";
import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
  name: "counter",
  initialState: { value: 0 },
  reducers: {
    plus: (state) => {
      state.value += 1;
    },
    minus: (state) => {
      state.value -= 1;
    },
  },
});
export const { plus, minus } = counterSlice.actions;
export default counterSlice.reducer;
  • initialState를 통해 state의 처음 상태를 정의한다. 여기서는 변수 value의 값이 0이다.
  • reducers 에서 액션을 설정한다. 여기서 액션은 plus와 minus가 있다.
  • 해당 액션들을 export한다.
  • slice는 slice.reducer로 내보낸다. store.js파일에서는 위 파일들을 전부 리듀서로 받고있다.

useSelector, useDispatch로 상태 접근하기


useSelector() 는 리덕스의 connect() 함수를 이용하지 않고 리덕스의 상태를 조회한다.

useDispatch() 는 생성한 액션을 발생 시키고, 액션 생성 함수를 가져온다.

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { minus, plus } from "./components/countSlice";

function App() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  // const plus = () => {
  //   setCounter(prev => prev + 1);
  // };
  // const minus = () => {
  //   setCounter(prev => prev - 1);
  // };
  return (
    <div>
      {/*<button onClick={minus}>-1</button>*/}
      {/*Value: {counter}*/}
      {/*<button onClick={plus}>+1</button>*/}
      <button onClick={() => dispatch(plus())}>+1</button>
      Value: {count}
      <button onClick={() => dispatch(minus())}>-1</button>
    </div>
  );
}

export default App;

위 코드에서는 useSelector() 로 스토어에서 현재 상태를 가져오고 있다.

useDispatch() 로 변경되는 값을 스토어로 전달하고 있다.

확실히 Redux-Tool-kit을 쓰니 코드가 간결해졌다.

count를 조작하는 함수인 plus와 minus 로직을 App 컴포넌트에서 분리해 counterSlice에 리듀서 함수로 분리했다.

또한 onClick 이벤트가 발생할 때 익명 함수로 plus와 minus하는 상태를 dispatch한다는 것이 직관적이게 됐다.

profile
디지털 노마드가 되고싶은 개발자

2개의 댓글

comment-user-thumbnail
2023년 5월 29일

잘 보고 갑니다. 그래서, 리덕스가 뭔데요?

1개의 답글