redux-saga 흐름

김영준·2020년 11월 21일
3

react

목록 보기
4/4

redux-saga의 흐름 정리

redux saga 는 redux 에서 API의 데이터를 받아와서 store 에 등록할 때 주로 쓰인다. 물론 saga를 안쓰고 API 의 데이터를 가져와서 저장할 수 있긴 하지만, 둘다 경험해본 결과 saga 를 쓰는 것이 코드가 더 깔끔하게 정리되고, 속도가 더 빠르다.

saga의 흐름을 정리해보자.

기존 redux 의 흐름을 정리하면 다음과 같다.

1. View 에서 액션이 일어난다.
2. dispatch 에서 action이 일어난다.
3. action이 일어서 reducer 함수가 실행된다. (기존 state의 값을 참조해서 데이터 처리)
4. reducer 함수의 실행결과 store에 새로운 값을 저장한다.
5. store 의 state에 subscribe 하고 있던 UI에 변경된 값을 준다.

redux-saga의 흐름을 정리하면 다음과 같다.

1. View 에서 액션이 일어난다.
2. dispatch 에서 action이 일어나게 된다.
3. action에 의한 reducer 함수가 실행되기 전에 middleware가 작동한다.
4. middleware 에서 명령내린 일을 수행하고 난뒤, reducer 함수를 실행한다.
5. reducer 의 실행결과 store에 새로운 값을 저장한다.
6. store의 state에 subscribe 하고 있던 UI에 변경된 값을 준다.

redux saga의 큰 차이점은 reducer 에 명령 하기 전에 어떤 행동을 처리해줄지 판단하는 것.

예로들면 API 통신하여 값을 가지고 와라. 가 되겠다.

이제부터 redux-saga를 이용하여 API 통신하여 값을 store 에 넣는 과정을 살펴볼것이다.

파일 위치 : src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './redux/module';
// import logger from 'redux-logger';
import rootSaga from './redux/saga/sagas.js';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);

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

index.js 파일에서 Provider에 store를 넣어주고 <App>을 render 한다.
1. store에 rootReducer를 넣어주고, redux의 자체함수인 applyMiddleware를 넣어준다.
2. applyMiddleware안에는 createSagaMiddleware()로 만들어진 sagaMiddleware가 들어간다.
3. 그리고store선언 밑에 sagaMiddleware.run(rootSaga)를 실행한다.
3-1. rootSaga 는 saga함수를 모아놓은 것이다.

이제 rootSaga를 살펴볼 것이다.

파일 위치 : src/redux/saga/saga.js

import axios from 'axios';
import { all, takeEvery, put } from 'redux-saga/effects';
import {
  GET_FIT_DATA,
  SAGA_GET_FIT_DATA,
} from '../Actions';


export function* lookGet() {
  yield takeEvery(GET_FIT_DATA, getDataAsync);
}

export function* getDataAsync() {
  let data = yield axios.get('https://test.api');
  yield put({ type: SAGA_GET_FIT_DATA, data: data.data });
}


export default function* rootSaga() {
  yield all([lookGet()]);
}
  1. rootSaga 에는 lookGet 이라는 함수가 들어간다.
  2. lookGet 함수를 살펴보면 takeEvery를 통해 실행된 action의 TypeGET_FIT_DATA일 경우에 getDataAsync 함수가 실행될 것이다. 라고 쓰였다.
  3. 그리고, getDataAsync 함수에서는 data 라는 변수에 axios.get() 을 통하여 data에 값이 할당 되기를 기다린다.
  4. 그리고, put을 통해 값을 reducer 함수에 전해준다. (put함수는 dispatch 하는것과 같다고 생각하면 된다.)

파일위치 : src/redux/module/fitdata.js

import { GET_FIT_DATA, SAGA_GET_FIT_DATA } from '../Actions';
import { initState } from './InitState';
// initState = {
//    fitData: {},
//    auth: {}
//     }

export const getFitData = (data) => ({type: GET_FIT_DATA,});

const FitData = (state = initState, action) => {
  switch (action.type) {
    case SAGA_GET_FIT_DATA:
      return {
        fitData: action.data,
      };
    default:
      return state;
  }
};

export default FitData;
  1. saga에서 만든 getDayaAsync의 마지막 줄에 작성된 yield put({ type: SAGA_GET_FIT_DATA, data: data.data }) 에서 type:SAGA_GET_FIT_DATA일 경우에 작성할 reducer 함수이다.
  2. reducer함수에는 yield put({ type: SAGA_GET_FIT_DATA, data: data.data }) 으로 들어온 값을 store로 넣어준다.

즉, type 만 있는 액션을 호출했을뿐인데 api 통신을 하여 가져온 데이터를 store에 넣어주게 되었다!!
이게 중요한 컨셉이다.

액션을 view의 어디에서 호출했는지 설명이 없어서 추가설명함.

파일위치 : src/page/Main.js

import React from 'react';
import './index.css';
import { getFitData } from '../redux/module/FitData';
import { connect } from 'react-redux';

const Main = (props) => {
  const onClick = (type) => {
    switch (type) {
      case GET_FIT_DATA:
        return props.getData();
      default:
        return;
    }
  };

  return (
    <div className='main'>
      <button
        onClick={() => {
          onClick(GET_FIT_DATA);
        }}
      >
        통신
      </button>
      </div>
    </div>
  );
};

const mapStateTpProps = (store) => ({ store: store });

const mapDispatchToProps = (dispatch) => ({
  getData: () => {
    dispatch(getFitData());
  }
});

export default connect(mapStateTpProps, mapDispatchToProps)(Main);
  1. connect를 통해 storedispatch 들을 Main 컴포넌트의 props로 넘겨준다.
  2. button 을 클릭 하게 되면 onClick함수가 실행된다. 이때 type으로 GET_FIT_DATA를 넘겨준다.(구분하기 위한 값임.)
  3. 그리고, props로 넘어온 dispatch에 들어간 액션함수인 getFitData를 실행시킨다.

종합 설명

Main.js의 통신 버튼을 클릭하여 해당 액션(getFitData)이 일어나고,typeGET_FIT_DATA일 때 saga 함수 (getDataAsync) 가 실행 되어 API를 통해 데이터를 가져오고, 가져온 데이터를 reducer가 읽을 수 있는 typeSAGA_GET_FIT_DATA로 알 수 있게 하고, storefitData에 가져온 data를 넣어준다.

profile
프론트엔드 개발자 김영준 입니다. 디테일함을 키우고 있습니다

0개의 댓글