4 분 소요


enter image description here

Redex란?

리덕스란 크게 보면 상태를 중앙에서 관리해줍니다. 리액트나 리덕스 개발의 복잡성을 낮춰주는 기능입니다. 아래 사진을 보며 설명 해보겠습니다. 왼쪽은 리액트만으로 구현 했을때의 데이터의 흐름입니다. 오른쪽은 리덕스를 도입했을때의 데이터의 흐름입니다.

리액트는 컴포넌트들로 만들어진 사회입니다. 어떠한 컴포넌트에서 변화가 발생하면 왼쪽의 그림처럼 모든 컴포넌트들로 데이터가 전파됩니다. 마치 소문과 같습니다. 하지만 소문은 단점이 있습니다. 모든 사람이 필요없는 소문을 듣게 된다는 점입니다. 또한 소문이 전파되기 위해선 리액트의 컴포넌트들이 프롭스와 이벤트를 통해 다 연결이 되어있어야합니다. 만약 구현하고자 하는 어플리게이션이 복잡하다면 리액트만으로도 구현이 가능은하지만 엄청난 퍼포먼스 저하와 복잡한 구현이 됩니다.

이를 보안하고자 있는 도구가 리덕스 입니다. 리덕스는 언론사와 같습니다. 어떠한 컴포넌트가 구성원들에게 전달하고자 하는 내용이 있으면 소문을 퍼트려서 모든 컴포넌트가 다 듣게 되는게아닌 리덕스라는 언론사에 제보를 하게 됩니다. 리덕스는 전체 컴포넌트들에게 방송을 합니다.

react-redux를 사용하여 소식이 필요한 컴포넌트 들에게만 정보를 전달할수 있는 장점이 있습니다.

소문으로만 동작하던 리액트 컴포넌트들 사이에 리덕스라는 언론사를 만들어서 어플리 케이션의 복잡성을 획기적으로 낮추고 아주 적은 노력으로 리액트와 리덕스를 연동할수 있습니다.


예제

예제를 통하여 edux를 구현하면서 배워보겠습니다.

아래 예제는 Add Number에 숫자를 넣고 +을 누르면 Display Number의 값이 업데이트가 됩니다. 이를 통해 보여드리고 싶은것은 여러 컴포넌트들이 복잡하게 얽혀있는 컴포넌트이 무수히 많다고 생각했을때 redux를 통하여 쉽게 컴포넌트 데이터를 전달할수 있게 구현해보겠습니다.

Add Number값을 변형 했을때 Add Number Root를 통해 Root까지 전달하고 Root에서 다시 props를 통해 Display Number Root로 가서 Display Number를 통해서 값을 전달하게 되는 과정입니다.

이 과정은 손이 많이가고 과정에서 많은 문제가 발생합니다. 더큰 규모의 어플리케이션이라면 더욱 큰 문제가 발생할수 있습니다.


기본적인 코드 소스입니다.

  • App.js입니다.
App.js;
import "./App.css";
import React, { useState } from "react";

import DisplayNumberRoot from "./components/DisplayNumberRoot";
import AddNumberRoot from "./components/AddNumberRoot";

function App() {
  const [number, setNumber] = useState(0);

  return (
    <div className="App">
      <h1>Root</h1>
      <AddNumberRoot onClick={(size) => setNumber(number + size)} />
      <DisplayNumberRoot number={number} />
    </div>
  );
}

export default App;

  • css 입니다.
App.css div {
  border: 5px solid #764abc;
  margin: 10px;
  color: #764abc;
  padding: 10px;
}

  • components 입니다.
AddNumber.jsx;
import React, { useState } from "react";

function AddNumber(props) {
  const [size, setSize] = useState(1);

  return (
    <div>
      <h1>Add Number</h1>
      <input
        type="button"
        value="+"
        name=""
        id=""
        onClick={() => props.onClick(size)}
      />
      <input
        type="text"
        value={size}
        name=""
        id=""
        onChange={(e) => setSize(Number(e.target.value))}
      />
    </div>
  );
}

export default AddNumber;

AddNumberRoot.jsx;
import React from "react";
import AddNumber from "./AddNumber";

function AddNumberRoot(props) {
  return (
    <div>
      <h1>Add Number Root</h1>
      <AddNumber
        onClick={(size) => {
          props.onClick(size);
        }}
      />
    </div>
  );
}

export default AddNumberRoot;

DisplayNumber.jsx;
import React from "react";

function DisplayNumber(props) {
  return (
    <div>
      <h1>Display Number</h1>
      <input type="text" value={props.number} name="" id="" readOnly />
    </div>
  );
}

export default DisplayNumber;

DisplayNumberRoot.jsx;
import React from "react";
import DisplayNumber from "../components/DisplayNumber";

function DisplayNumberRoot(props) {
  return (
    <div>
      <h1>Display Number Root</h1>
      <DisplayNumber number={props.number} />
    </div>
  );
}

export default DisplayNumberRoot;

redux 도입

먼저 src디렉토리 안에 store.js를 만들었습니다. createStore api를 이용해서 스토어를 만들겠습니다. createStore를 이용하여 스토어를 만들때에는 reducer인 함수를 주어야합니다.

✔ Reducer - 리듀서는 항상 state와 action을 받아서 state를 return 해주어야합니다. state는 데이터이고 action은 데이터에 가해저야하는 여러가지 행위입니다. 간단히만 말하자면 Store에 대해 뭔가를 하고싶거나 store의 state를 업데이트하고 싶을 때 발생하는 이벤트 드리븐같은 것입니다. 이 Action은 Reducer로 전달됩니다. 업데이트 방식은 store의 state를 변형(muatate)하는 것이 아닌 ‘이전 state와 Action을 참고하여 새로운 state를 만들어서 반환하는 방식입니다.

redux가 도입되고 첫번째 해야할 일은 redux를 도입전에 어플 작동 방식이었던 컴포넌트들을 타고 올라가서 내려가는 방식이 아닌 Add Number의 +를 클릭했을때 store의 state값이 바뀌게 할것입니다.

AddNumber.jsx에서 store에게 접근하기위해 dispatch를 주었습니다.

✔ Dispatch - dispatch(action)을 사용하여, store의 reducer에 action을 전달합니다. redux에서 상태 변경을 일으킬 수 있는 유일한 방법입니다. dispatch가 store의 reducer에게 action을 전달해주면 reducer가 action의 type을 보고 그에 맞는 행동을 해주는 것입니다.

❗props.onClick(size) 이벤트는 삭제해주었습니다.

AddNumber.jsx;
import React, { useState } from "react";
import store from "../store";

function AddNumber() {
  const [size, setSize] = useState(1);

  return (
    <div>
      <h1>Add Number</h1>
      <input
        type="button"
        value="+"
        name=""
        id=""
        onClick={() => {
          store.dispatch({ type: "INCREMENT", size: size });
        }}
      />
      <input
        type="text"
        value={size}
        name=""
        id=""
        onChange={(e) => setSize(Number(e.target.value))}
      />
    </div>
  );
}

export default AddNumber;

먼저 잘 작동하는지 확인하기 위해 redux의 state값을 확인해보겠습니다. 먼저 redux의 devtools를 사용해보겠습니다. ❗devtools는 더 자세하게 글을 올리겠습니다.

window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

이 코드를 store의 두번째 인자로 넣어주면 잘 작동합니다.

이어서 AddNumber를 클릭시 dispatch를 하고있습니다. 수령하는쪽의 state쪽을 작성해보겠습니다. 아래 코드를 보자면 state 값이 undefined 즉 아직 정의되어있지 않다면 return값으로 number를 0으로 줍니다.

if 만약에 받은 action의 type값이 “INCREMENT”라면 기존 state.number + action.size 값까지 더합니다.

import { createStore } from "redux";

const store = createStore((state, action) => {
  if (state === undefined) {
    return {
      number: 0,
    };
  }
  if (action.type === "INCREMENT") {
    return { ...state, number: state.number + action.size };
  }
  return state;
}, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

export default store;

이제 클릭해보면 state값이 증가하는걸 확인할수 있습니다.


이번에는 클릭시 증가한 값이 DisplayNumber에 더해져 값이 출력되게 전달해보겠습니다. DisplayNumber.jsx에서 더이상 value값을 props에 의해 전달되는 값이아닌 store에 있는 값을 사용합니다. store에서 getState()를 이용해 number 값을 가져옵니다. 하지만 state의 값이 변하여도 변경되는것이 없습니다. subscribe() 를 이용하여 구독을 해줍니다. 변화가 감지되면 setState에 number 값을 전달해줍니다.

✔Subscribe -애플리케이션 상태 변경을 구독(subscribe, 감지) 하여 상태가 업데이트 되면 등록된 리스너(listener)를 실행시킵니다.

import React, { useState } from "react";
import store from "../store";

function DisplayNumber() {
  const [state, setState] = useState({ number: store.getState().number });
  store.subscribe(() => {
    setState({ number: store.getState().number });
  });

  return (
    <div>
      <h1>Display Number</h1>
      <input type="text" value={state.number} name="" id="" readOnly />
    </div>
  );
}

export default DisplayNumber;

마무리

어플리케이션이 잘 작동하고 있습니다. 위의 리덕스가 도입전의 코드를 보면 Add Number를 작동하고 값을 Display Number로 보내기 위해 컴포넌트들이 연결되어있습니다. 하지만 redux의 store로 직접 값을 전달받기 때문에 필요 없어진 코드들이 많습니다. 아래 코드들은 지워도 잘 작동하는것을 확인할수 있습니다.

App.js
onClick={(size) => setNumber(number + size)}
AddNumberRoot.jsx
onClick={(size) => {
          props.onClick(size);
        }}
DisplayRoot.js

number={props.number}

댓글남기기