ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Redux] React + Redux App 만들기(생활코딩)
    Front-End(Web)/React - 라이브러리들 2021. 2. 16. 16:16
    반응형

    이번엔, React 컴포넌트와 Redux의 조합을 공부하려고 한다. (생활코딩 react-redux 과제와 함께)

    Global State를 Redux 문법을 통해서 다루는 것 뿐만 아니라, 디렉토리와 파일구조(store, reducer 등)를 배우는 것에도 중점을 둬야겠다.


    💜 React Without Redux 💀

    이번엔, 아래와 같이 숫자를 수정하고 표시하는데 있어 각각 두 번의 Depth를 거치는 컴포넌트를 React로 만들었다.

    // 1. AddNumber.js
    const AddNumber = ({ plusNum }) => {
      const [size, setSize] = useState(1);
      return (
        <div>
          <h3>Add Number</h3>
          <input type="text" value={size} onChange={(e) => {
            setSize(Number(e.target.value));
          }} />
          <input type="button" value="+" onClick={() => plusNum(size)}/>
        </div>
      )
    }
    
    
    // 2. AddNumberRoot.js
    const AddNumberRoot = ({ plusNum }) => {
      return (
        <div>
          <h2>Add Number Root</h2>
          <AddNumber plusNum={plusNum} />
        </div>
      )
    }
    
    
    // 3. App.js
    function App() {
      const [number, setNumber] = useState(0);
    
      const plusNum = (size) => {
        const newNum = number + size
        setNumber(newNum);
      }
    
      return (
        <div className="App">
          <h1>React Redux: 생활코딩</h1>
          <AddNumberCon plusNum={plusNum} />
          <ShowNumberCon number={number} />
        </div>
      );
    }
    
    
    // 4. ShowNumberRoot.js
    const ShowNumberRoot = ({ number }) => {
      return (
        <div>
          <h2>Show Number Root</h2>
          <ShowNumber number={number} />
        </div>
      )
    }
    
    
    // 5. ShowNumber.js
    const ShowNumber = ({ number }) => {
      return (
        <div>
          <h2>Show Number</h2>
          <input type="text" value={number} />
        </div>
      )
    }

    숫자를 변경하고 이것이 표현될때까지 컴포넌트 흐름순으로 코드를 나열했다.

    App.js에서 plusNum 함수를 props로 하달하고, AddNumber.js는 size(증가량) 상태를 포함해서 다시 props를 전달해야 한다.

    또한, ShowNumber.js로 number 상태를 props로 하달해서 이를 표현시켜야 한다.

     

    이 때, 중간의 Container 컴포넌트들은 상태를 활용하지는 않지만, 이를 전달하기 위해 props 수용/하달을 진행하는 문제가 있다.

    또한, Depth가 깊어지면 그만큼 props Drilling을 위한 로직들이 추가되어야 한다.


    💜 React + Redux 😃

    - 설치(npm)

    1) Redux 코어 설치: 리덕스 기본 라이브러리 설치

    npm install redux

     

    2) Redux Toolkit(RTK): Redux 공식추천 방법. 리덕스에 필요한 패키지와 함수를 번들러로 받을 수 있다.

    npm install @reduxjs/toolkit

       

     3) React Redux 앱 만들기:  React의 CRA와 Redux Toolkit을 조합한 템플릿으로 초기세팅을 바로 할 수 있는 설치방법이다.

    npx create-react-app [디렉토리명] --template redux

     

    - 코드수정

    1) Store.js : 스토어, 리듀서 함수 제작

    import { createStore } from 'redux';
    
    const reducer = (state, action) => {
      if (!state) {
        return {
          number: 0,
        }
      }
      if (action.type === 'INCREMENT') {
        return {...state, number: state.number + action.size}
      }
    }
    
    const store = createStore(
      reducer,
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );
    
    
    export default store;

    먼저, Store.js 파일을 만든다. 여기에는 Redux의 스토어와 리듀서 함수가 들어가 있다.

    기존에, App.js에서 관리하던 number 상태와, 숫자를 증가시키는 로직을 이제 여기서 관리한다.

     

    2) AddNumber.js : onClick(plusNum 함수) props 불필요, 버튼 클릭시 디스패치 발생

    import React, { useState } from 'react';
    import store from '../Store';
    
    const AddNumber = () => {
      const [size, setSize] = useState(1);
    
      return (
        <div>
          <h3>Add Number</h3>
          <input type="text" value={size} onChange={(e) => {
            setSize(Number(e.target.value));
          }} />
          <input type="button" value="+" onClick={() => {
            store.dispatch({
              type: 'INCREMENT',
              size,
            })
          }}/>
        </div>
      )
    }
    
    export default AddNumber

     

    3) ShowNumber.js : number props 불필요, store.getState() 로 상태 불러오기

    // ShowNumber.js
    
    import React, { useState } from 'react';
    import store from '../Store';
    
    const ShowNumber = () => {
      const [number, setNumber] = useState(0);
    
      store.subscribe(() => {
        setNumber(store.getState().number)
      })
      
      return (
        <div>
          <h2>Show Number</h2>
          <input type="text" value={number} />
        </div>
      )
    }
    
    export default ShowNumber

     

    4) AddNumberRoot.js, ShowNumberRoot.js 등 컴포넌트들의 props 전달이 불필요해짐

     

     

    - Container 컴포넌트 & Presentationl 컴포넌트

    위 AddNumber.js 컴포넌트를 예시로 보자. 이 컴포넌트는 재활용이 까다롭다. (리덕스 스토어에 종속되어 있기 때문)

    만약, 이 컴포넌트의 UI를 재활용하려고 하는데, 내부의 값은 Global State와 무관할 경우 코드를 복붙해야만 할 것이다.

     

    재활용성 향상을 위해, 우리는 컴포넌트를 기능적으로 분리한다.

    • Container 컴포넌트: Redux 관련기능만을 담당한다. 부모 컴포넌트로서 Presentational 을 감싸며, props를 내려준다.
    • Presentational 컴포넌트: Redux 기능은 배제되고 UI만 담당한다. 자식 컴포넌트로 Container의 props 값을 활용한다.
    // Container: AddNumberCon.js
    import React from 'react'
    import AddNumber from '../components/AddNumber';
    import store from '../Store';
    
    const AddNumberCon = () => {
      return (
        <AddNumber onClick={(size) => {
          store.dispatch({
            type: 'INCREMENT',
            size,
          })
        }}/>
      )
    }
    // Presentational: AddNumber.js
    import React, { useState } from 'react';
    
    const AddNumber = ({ onClick }) => {
      const [size, setSize] = useState(1);
    
      return (
        <div>
          <h3>Add Number</h3>
          <input type="text" value={size} onChange={(e) => {
            setSize(Number(e.target.value));
          }} />
          <input type="button" value="+" onClick={() => onClick(size)}/>
        </div>
      )
    }

    Container인 <AddNumberCon> 를 만든다. Redux와 관련된 디스패치 함수가 포함되며, props로 <AddNumber>에 내려준다.

    <AddNumber> 는 Presentational 을 맡는다. props로 받은 onClick 함수(디스패치)에, 현재 size값만 넘겨주면 된다. (No Redux)


    분명, Redux 를 통한 상태관리와 props Drilling 제거는 좋은 현상이다.

    하지만, 마지막처럼 우리는 컴포넌트의 기능분리를 위해 Container & Presentational 로 각각 나눠주어야 했다.

    또한, 규모가 커질수록 이 역시 이중작업이 되며, 다루는 상태가 많아질수록 Container가 내리는 props도 증가한다.

     

    이러한 한계에 다시 봉착하면서, React와 Redux가 각각의 역할에 좀 더 충실하도록 해주는 것이 바로 react-redux 라이브러리다.

    다음 포스팅에선, 이 react-redux에 대해 좀 더 다뤄보겠다.

    반응형
Designed by Tistory.