redux saga 는 redux 에서 API의 데이터를 받아와서 store 에 등록할 때 주로 쓰인다. 물론 saga를 안쓰고 API 의 데이터를 가져와서 저장할 수 있긴 하지만, 둘다 경험해본 결과 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에 변경된 값을 준다.
예로들면 API 통신하여 값을 가지고 와라.
가 되겠다.
파일 위치 : 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()]);
}
rootSaga
에는 lookGet
이라는 함수가 들어간다.lookGet
함수를 살펴보면 takeEvery
를 통해 실행된 action의 Type
이 GET_FIT_DATA
일 경우에 getDataAsync
함수가 실행될 것이다. 라고 쓰였다.getDataAsync
함수에서는 data
라는 변수에 axios.get()
을 통하여 data
에 값이 할당 되기를 기다린다. 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;
saga
에서 만든 getDayaAsync
의 마지막 줄에 작성된 yield put({ type: SAGA_GET_FIT_DATA, data: data.data })
에서 type:SAGA_GET_FIT_DATA
일 경우에 작성할 reducer
함수이다.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);
connect
를 통해 store
와 dispatch
들을 Main 컴포넌트의 props
로 넘겨준다.onClick
함수가 실행된다. 이때 type으로 GET_FIT_DATA
를 넘겨준다.(구분하기 위한 값임.)props
로 넘어온 dispatch
에 들어간 액션함수인 getFitData
를 실행시킨다.
Main.js
의 통신 버튼을 클릭하여 해당 액션(getFitData
)이 일어나고,type
이GET_FIT_DATA
일 때 saga 함수 (getDataAsync
) 가 실행 되어 API를 통해 데이터를 가져오고, 가져온 데이터를reducer
가 읽을 수 있는type
인SAGA_GET_FIT_DATA
로 알 수 있게 하고,store
의fitData
에 가져온data
를 넣어준다.