React-flux-Redux

정원·2023년 4월 5일

React+Spring

목록 보기
5/8

flux

페이스북(현 메타)은 MVC 패턴이 스케일업 하기에는 좋지 않고 그래서 Flux 패턴을 개발했다고 한다.

MVC

MVC는 Model, View, Controller의 약자입니다. Model에 데이터를 저장하고, Controller를 이용하여 Model의 데이터를 관리(CRUD)합니다. Model의 데이터가 변경되면 View로 전달되어 사용자에게 보여집니다. 또한 중요한 점은 사용자가 View를 통해 데이터를 입력하면 View 역시 Model을 업데이트할 수 있다는 점입니다. 즉 데이터가 양방향으로 흐를 수 있다는 것이죠.

MVC는 규모가 커질수록 controller의 규모가 커지면서 V와 M 사이의 데이터 복잡도, M들간의 종속 업데이트로 개발이 어려워진다.

Flux는 데이터가 한방향으로 흘러 직관적이고 규모가 커져도 각 역할에 맞는 개발이 가능하다고 말한다.

flux 패턴

Flux는 사용자 입력을 기반으로 Action을 만들고 Action을 Dispatcher에 전달하여 Store(Model)의 데이터를 변경한 뒤 View에 반영하는 단방향의 흐름으로 애플리케이션을 만드는 아키텍처입니다. 구조는 다음의 그림과 같습니다.

Action

Action이란 데이터를 변경하는 행위로서 Dispatcher에게 전달되는 객체를 말합니다. Action creator 메서드는 새로 발생한 Action의 타입(type)과 새로운 데이터(payload)를 묶어 Dispatcher에게 전달합니다.

{
  type: 'SET_PROFILE',
  data: {
    'name': 'Harry',
    'age': 458
  }
}

Dispatcher

Dispatcher는 모든 데이터의 흐름을 관리하는 중앙 허브입니다. Dispatcher에는 Store들이 등록해놓은 Action 타입마다의 콜백 함수들이 존재합니다. Action을 감지하면 Store들이 각 타입에 맞는 Store의 콜백 함수를 실행합니다. Store의 데이터를 조작하는 것은 오직 Dispatcher를 통해서만 가능합니다. 또한 Store들 사이에 의존성이 있는 상황에서도 순서에 맞게 콜백 함수를 순차적으로 처리할 수 있도록 관리합니다.

Store (Model)

Store는 상태 저장소로서 상태와 상태를 변경할 수 있는 메서드를 가지고 있습니다. 어떤 타입의 Action이 발생했는지에 따라 그에 맞는 데이터 변경을 수행하는 콜백 함수를 Dispatcher에 등록합니다. Dispatcher에서 콜백 함수를 실행하여 상태가 변경되면 View에게 데이터가 변경되었음을 알립니다.

View

View는 리액트 컴포넌트로 생각하면 됩니다. Store에서 View에게 상태가 변경되었음을 알려주면 최상위 View(Controller View)는 Store에서 데이터를 가져와 자식 View에게 내려보냅니다. 새로운 데이터를 받은 View는 화면을 리렌더링합니다. 또한 사용자가 View에 어떤한 조작을 하면 그에 해당하는 Action을 생성합니다.

Redux란?

리덕스 패턴은 MVC 패턴을 대체하기 위해서 페이스북이 사용한 Flux 패턴을 살짝 바꾼 겁니다.

eact 프로젝트의 규모가 커질때마다 자식으로 넘겨주어야 하는 props의 깊이도 점점 깊어진다. 따라서, 어디에서든 내가 원하는 state를 사용할 수 있는 라이브러리 Redux가 나타났다.

Redux만 사용하여 React에서 사용할 수 있지만, 더 편하게 사용하기 위해 React-Redux가 나왔다.
React-Redux 공식문서

Redux설치

npm install react-redux

예제

Top.js에는 데이터를 넘기고
Bottom.js에는 함수를 넘겼다.
이렇게하면 제어를 할수있지만 자식 컴포넌트가 많아질수록 그 개수대로 상태값을 계속 넘겨주어야한다...!!!

그렇기 때문에 상태값을 store에 보관하여 사용한다.

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

  const addNumber = () => {
    setNumber(number + 1);
  };

  return (
    <div>    
      {/* 데이터 넘기기 */}
      <Top number={number} />
      {/* 함수 넘기기 */}
      <Bottom addNumber={addNumber} />
    </div>
  );
}

export default App;

1. store 만들기

src안에 store.js 생성

// 액션 만들기
export const increase = () => ({ type: 'INCREMENT' });
export const decrese = () => ({ type: 'DECREMENT' });

// 상태 만들기(초기상태)
const inistate = {
  number: 0,
};

// reducer:액션의 결과를 걸러내는 친구
const reducer = (state = inistate, action) => {
  switch (action.type) {
    case 'INCREMENT':
      // return되면 그걸 호출한 쪽에서 받는게아니라 return되는 순간 ui가 변경됨
      return { number: state.number + 1 };
    case 'DECREMENT':
      return { number: state.number - 1 };
    default:
      return state;
  }
};
export default reducer;

index.js

index.js안에 생성한 store 변수를 생성하고
App 컴포넌트를 Provider로 감싸서 store을 전달하면
모든 컴포넌트에서 store에 접근할 수 있다.


✨ createStore가 deprecated되었다고한다!!

npm install redux@4.1.2 react-redux

다시 설치.


import reducer from './store';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
...
const store = createStore(reducer);

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

App.js

기존에 App.js에서는 상태값을 Top, Bottom에 전달했는데 이제 그럴 필요가 없다.
다 삭제!!

function App() {

  return (
    <div>    
      <Top />
      <Bottom />
    </div>
  );
}

export default App;

useSelector( )

Top.js

데이터를 가져올때는 useSelector사용!!
store에서 number를 가져온다.

import { useSelector } from 'react-redux';

const Top = () => {
  const { number } = useSelector((store) => store.number);

  return (
    <StyledDiv>
      <h1>Top</h1>
      번호 : {number}
    </StyledDiv>
  );
};

export default Top;

useDispatch( )

Bottom.js

함수를 가져올때는 useDispatch( ) 사용!!
이벤트가 일어날때 dispatcher(사용할 store함수호출)

import { useDispatch } from 'react-redux';
import { decrese, increase } from './../store';

const Bottom = () => {
  const dispatcher = useDispatch();
  return (
    <StyledDiv>
      <h1>Bottom</h1>
      <button onClick={() => dispatcher(increase())}>증가</button>
      <button onClick={() => dispatcher(decrese())}>감소</button>
    </StyledDiv>
  );
};

export default Bottom;

✨ store에 액션 정의 안하면 함수호출할때 직접 넣어줘야함...

store 액션 정의

export const decrese = () => ({ type: 'DECREMENT' });
// 액션정의
<button onClick={() => dispatcher(decrese())}>감소</button>
// 액션정의 안하면 직접 입력
<button onClick={() => dispatcher({ type: 'DECREMENT' })}>감소</button>

이렇게하면 props -> props -> props 안해도 되고
store에 정의된 데이터,함수를 바로 호출해서 사용가능하다!!

데이터 전달

함수를 호출할때 데이터를 전달받기.

store.js

username을 예제로 데이터를 전달받아보자.

기초상태의 username: 'kim'을
증가버튼을 눌렀을때 파라미터 값으로 변경해보기.

// 액션 만들기
export const increase = (username) => ({
  type: 'INCREMENT',
  payload: username,
});
export const decrese = () => ({ type: 'DECREMENT' });

// 상태 만들기(초기상태)
const inistate = {
  username: 'kim',
  number: 1,
};

// reducer:액션의 결과를 걸러내는 친구
const reducer = (state = inistate, action) => {
  switch (action.type) {
    case 'INCREMENT':
      // return되면 그걸 호출한 쪽에서 받는게아니라 return되는 순간 ui가 변경됨
      return { number: state.number + 1, username: action.payload };
    case 'DECREMENT':
      return { number: state.number - 1 };
    default:
      return state;
  }
};
export default reducer;

Top.js

const Top = () => {
  const { number, username } = useSelector((store) => store);

  return (
    <StyledDiv>
      <h1>Top</h1>
      번호 : {number}
      이름 : {username}
    </StyledDiv>
  );
};

Bottom.js

dispatcher(increase('cos'))
increase를 호출할때 변경하고 싶은 username을 전달.

const Bottom = () => {
  const dispatcher = useDispatch();
  return (
    <StyledDiv>
      <h1>Bottom</h1>
      <button onClick={() => dispatcher(increase('cos'))}>증가</button>
      <button onClick={() => dispatcher(decrese())}>감소</button>
    </StyledDiv>
  );
};

0개의 댓글