Node.js와 React로 간단한 웹페이지를 만들어보자 7편

Angela·2022년 7월 13일
0

<목차>
Redux 기초
Redux UP
React Hooks

Redux 기초

Redux란?
JavaScript App을 위한 predictable state container라 하는데 상태(state) 관리 라이브러리 라고 알아두면 됩니다.

그렇다면 여기서 말하는 state란 무엇일까요?
React에서는 주요하게 Props와 State라는게 있는데, 이 둘을 비교해보면서 알아보도록 하겠습니다.

Props

  • 컴포넌트 간에 무언가를 주고 받을 때 사용.
  • 소통 방식은 위에서 아래로만 가능. (부모 컴포넌트->자식 컴포넌트)
  • 내용을 수정할 때 자식 컴포넌트에서는 수정할 수 없음.
  • 수정을 위해선 부모 컴포넌트에서 새로 내용을 보내주어야 함.

state

  • 컴포넌트 안에서 데이터 교환, 전달 시에 사용. (컴포넌트 간 통신 아님.)
  • mutable -> 컴포넌트 내부에서 내용 변경 가능. (state 변경)
  • state 변경 시 re-rendering 됨.

Redux가 있을 때와 없을 때의 모습을 비교한 그림입니다.
없을 때에는 상위 컴포넌트가 하위 컴포넌트의 변경 사항을 기록합니다. 이렇게 하나하나 변경 사항이 타고 올라가는 것이죠. 모든 변경사항들이 위아래로 움직이면서 복잡해집니다.
Redux가 있으면 변경사항을 상위 컴포넌트에 두는 게 아니라 Redux Store에 저장해둡니다. 정보를 주고 받을 때에 Redux Store와 각각이 주고 받으면 되기 때문에 비교적 간편하게 수정, 변경이 가능합니다.

그러면 Redux의 data flow를 보도록 하겠습니다.
아래의 그림을 보면 action -> reducer -> store 처럼 한 방향 흐름을 볼 수 있습니다.

그리고 Action은 무엇이 일어났는지 설명하는 객체입니다.
위의 그림에서 type: 'LIKE_ARTICLE', articled: 42 라고 되어있는 것은 Article ID 42번은 Like(좋아요)로 사용한다는 의미입니다.
그 외에도 FETCH_USER_SUCCESS의 경우 Mary라는 유저의 정보를 가져오는 것을 성공했다는 것을 객체로 알려준 것입니다.
세 번째줄은 뒤의 텍스트('Read~')를 TODO 라는 list에 Add했다는 의미입니다.

Reducer는 state의 변화를 설명하고 변한 state를 리턴하는데요.
예시에서는 previousState가 action을 통과하면 nextState로 바뀌었다는 의미를 지니고 있습니다.

그 외에도 store는 app 전체의 state들을 감싸주는 역할을 하고 있으며, 또한 store 안에 여러 메소드들이 존재하는데, 그 메소드를 이용하여 state들을 관리하기도 합니다.

Reducer는 pure function이기에 reducer 내부에서 하지 말아야 할것들도 있는데 아래의 3줄을 참고하시면 될 것 같습니다.

Redux UP (+SetUp)

다운 받아야 할 Dependency들
1.redux
2.react-redux
3.redux-promise (Redux Middleware)
4.redux-thunk (Redux Middleware)

npm install redux react-redux redux-promise redux-thunk --save

store는 언제나 객체라는 형식의 action을 받는 것은 아니고, promise나 function 형태로 된 것을 받을 때도 있습니다. 하지만 store 자체는 객체의 형식만 받을 수 있기 때문에 나머지도 받기 위해서는 별도로 설치해주어야 하는 것이 있습니다. 그게 바로 위의 미들웨어들입니다.

redux-thunk는 dispatch에게 어떻게 function 형태의 action을 받는지 알려주는 역할을 합니다.
redux-promise는 dispatch에게 promise 형태의 action이 왔을 때 어떻게 대처해야하는지 알려주는 역할을 합니다.

그리고 앞으로의 진행을 위해 하나의 툴을 다운로드 할 필요가 있습니다. ReduxDevTools를 다운 받아주세요.

그 후에 아래의 문장을 적절한 곳에 넣어주면 됩니다.
window.REDUX_DEVTOOLS_EXTENSION &&
window.REDUX_DEVTOOLS_EXTENSION()

+) createStore에서 configureStore로 바뀌면서 위의 코드들을 굳이 적을 필요가 없게 되었습니다.
이 부분에서 도움을 받은 사이트1, 사이트2 입니다.

createStore를 configureStore로 바꾸는데에 시간이 꽤 소요되었는데요.
일단 구글링을 통해서 최대한 바꾸어 보았으나 오류가 난다면 여기나 다음 포스팅에 추가해보도록 하겠습니다.

일단은 코드는 이런 식으로 바꾸어보았습니다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import 'antd/dist/antd.css';
import { applyMiddleware, createStore } from 'redux';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-thunk';
import Reducer from './_reducers';

const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore)

ReactDOM.render(
    <Provider
        store={createStoreWithMiddleware(Reducer,
            window.__REDUX_DEVTOOLS_EXTENSION__ &&
            window.__REDUX_DEVTOOLS_EXTENSION__()
        )}
    >
        <App />
    </Provider>
    , document.getElementById('root'));

serviceWorker.unregister();

아래는 바뀐 코드입니다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import 'antd/dist/antd.css';
import { applyMiddleware } from 'redux';
import { configureStore } from '@reduxjs/toolkit';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-thunk';
import Reducer from './_reducers';


const configureStore1 = configureStore({ reducer: Reducer, middleware: [promiseMiddleware, ReduxThunk] })

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

reportWebVitals();

React Hooks

React의 component에는 대표적으로 2가지가 있습니다.
class component와 functional component 입니다.
아래의 그림은 두 component의 차이를 비교한 것 입니다.

아래의 사진에 나온 것들 (life cycle sys.)은 class component로는 다 구현할 수 있지만 functional component로는 불가능합니다. 그러기 때문에 대부분 class component를 사용했습니다.

아래는 리액트의 사이클인데 이러한 순서로 작업이 진행된다고 참고하시면 될 것 같습니다.

하지만 React 16.8 Hooks가 update 되면서 Functional Component로도 전에는 구현하지 못했던 것들을 구현할 수 있게 되었습니다.
아래의 코드는 각각 class component와 functional component로 똑같은 기능을 하는 것을 구현한 것입니다.

componentDidMount는 useEffect로, this.state도 useState로 대체되었다고 보면 됩니다.

+) 영어 설명 덧붙임
The render() method is required, and is the method that actual outputs HTML to the DOM
DOM ready means that all the HTML has been received
and parsed by the browser into the DOM tree
which can now be manipulated.
It occurs before the page has been fully rendered
(as external resources may have not yet fully downloaded - including images, CSS,
JavaScript and any other linked resources).
The actual event is called DOMContentLoaded.
componentDidMount() is invoked immediately
after a component is mounted (inserted into the tree).
Initialization that requires DOM nodes should go here.
If you need to load data from a remote endpoint,
this is a good place to instantiate the network request.

강의 내 업데이트 사항 (추후 수정될 수 있음)

이 강의는 2020 ver.이라 그 사이에 조금씩 문법이나 사용하는 모듈, 툴이 바뀐 경우가 있었습니다.

  1. createstore 대신 configurestore를 사용 권고
    createStore를 사용하려고 하면 createStore에 중앙선이 그어져있고 redux/toolkit의 configureStore메서드를 사용할 것을 추천한다는 메세지를 받을 수 있었습니다.

  2. ReactDOM.render는 React 18에서 더 이상 지원되지 않으므로, 대신 createRoot를 사용할 것이라는 권고가 뜹니다.

//예전의 코드
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

serviceWorker.unregister();
//바뀐 코드
import ReactDOMClient from "react-dom/client";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOMClient.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

reportWebVitals();

0개의 댓글