์ค๋ ํฌ์คํธ๋ Redux-Toolkit ๊ณผ Redux-saga๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ๋ก API๋ฅผ ๋ฐ์์๋ณด์ ํ์ ์คํฌ๋ฆฝํธ์ ํจ๊ป !
์ฐ์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ฝ๋๋ฅผ ๋ณด๊ธฐ์ ์์ Saga์ ๋ํด ์์๋ณด์
Redux-Saga ์ ๋ํ์ฌ
redux-saga ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ด๋ ์ดํํธ, ์๋ฅผ ๋ค๋ฉด ๋ฐ์ดํฐ fetching์ด๋ ๋ธ๋ผ์ฐ์ ์บ์์ ์ ๊ทผํ๋ ์์ํ์ง ์์ ๋น๋๊ธฐ ๋์๋ค์, ๋ ์ฝ๊ณ ์ข๊ฒ ๋ง๋๋ ๊ฒ์ ๋ชฉ์ ์ผ๋กํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
saga๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ด๋ ์ดํํธ๋ง์ ๋ด๋นํ๋ ๋ณ๋์ ์ฐ๋ ๋๋ผ๊ณ ๋ณด๋ฉด ๋๊ณ , redux-saga๋ ๋ฏธ๋ค์จ์ด์ด๊ธฐ ๋๋ฌธ์ saga๋ผ๋ ์ฐ๋ ๋๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ redux ์ก์ ์ ํตํด ์คํ๋๊ณ , ๋ฉ์ถ๊ณ , ์ทจ์ํ ์ ์๊ฒ ํ๋ค. ๋ ๋ชจ๋ redux ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ์ ์ ๊ทผํ ์ ์๊ณ redux ์ก์ ๋ dispatch ํ ์ ์๊ฒ ํ๋ค.
saga๋ ๋น๋๊ธฐ ํ๋ฆ์ ์ฝ๊ฒ ์ฝ๊ณ , ์ฐ๊ณ , ํ ์คํธ ํ ์ ์๊ฒ ๋์์ฃผ๋ ES6์ Generator๋ฅผ ์ฌ์ฉํ๋ค. Generator๋ฅผ ์ฌ์ฉํจ์ผ๋ก์จ, ๋น๋๊ธฐ ํ๋ฆ์ ํ์ค ๋๊ธฐ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์ฒ๋ผ ๋ณด์ด๊ฒ ๋๋ค. (async/await์ ๋น์ทํ๋ฐ, ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค)
redux-thunk์ ๋ค๋ฅด๊ฒ ์ฝ๋ฐฑ ์ง์ฅ์ ๋น ์ง์ง ์๊ณ ๋น๋๊ธฐ ํ๋ฆ๋ค์ ์ฝ๊ฒ ํ ์คํธํ ์ ์๊ฒ ํด์ฃผ๋ฉฐ ์ก์ ๋ค์ ์์ํ๊ฒ ์ ์ง ํ ์ ์๋ค.
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ์์๋ณด์
์ฐ์ redux-toolkit์ ์์ฑํด๋ณด์
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
//initialState
export const initialState: Description = {
animal: [],
isLoading: false,
error: null,
};
export const slice = createSlice({
name: 'animal',
initialState,
reducers: {
getDataSuccess: (state, action: PayloadAction<DescriptionParams>) => {
state.isLoading = true;
state.animal.length = 0;
const newState = state.animal.concat(action.payload);
state.animal = newState;
},
getDataFailure: (state, { payload: error }) => {
state.isLoading = false;
state.error = error;
},
getData: (state, action: PayloadAction<ParamType>) => {
state.isLoading = false;
},
},
});
export const animal = slice.name;
export const animalReducer = slice.reducer;
export const animalAction = slice.actions;
์ฐ์ createSlice๋ฅผ ์ฌ์ฉํ ๊ฒ ์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ก์ ์ ํ์ ์ ์ง์ ํด์ฃผ๊ฑฐ๋ ์ก์ ์์ฑํจ์๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ์ผ์ ํ์ง ์์๋ ๋๋ค
initialState๋ฅผ ์ ์ธํ ๋ค Slice์์ ๋ฃ๊ณ reducer: {} ์์ ๋ฆฌ๋์๋ค์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค. name์ ์ก์ ์ ํ์ ์ด๋ค.
์ฌ๊ธฐ์ ์ก์ ์ Payload ํ์ ์ PayloadAction์ผ๋ก ์ง์ ํด์ฃผ์๋๋ฐ ๋ฆฌ๋์์์ ์ก์ ์ ํ์ ์ ์ ์ธ ํ ๋ ์ฌ์ฉํ ๊ธฐ๋ณธ ์ ํ์ PayloadAction <PayloadType> ์ด๋ค
interface๋ก ํ์ ์ง์ ์ ํด๋ณด๋ฉด
// ๋ฐ์์ค๋ api Data์ type
interface DescriptionParams {
age: number;
careAddr: string;
careNm: string;
careTel: string;
chargeNm: string;
colorCd: string;
desertionNo: number;
filename: string;
happenDt: number;
happenPlace: string;
}
// state์ ํ์
interface Description {
animal: DescriptionParams[];
isLoading: boolean;
error: null;
}
// api์ param ํ์
interface ParamType {
city: number;
kind: number;
}
๋ฐ์์ค๋ api์ ๋ฐ์ดํฐ๊ฐ ๊ฐ์ฒด๋ฐฐ์ด ํ์์ด๊ธฐ ๋๋ฌธ์ api ๋ฐ์ดํฐ์ interface๋ฅผ initialState์ animal ์ด๋ผ๋ state๋ ๊ทธ interface๋ฅผ
ํ์ ์ผ๋ก ๊ฐ์ง๋ค
์ด์ Saga์ ์ฝ๋๋ฅผ ์ดํด๋ณด๊ธฐ ์ ์ ๋ช๊ฐ์ง ์ดํด๋ณด์
yield ๋ ?
Promise ๊ฐ ๋ฏธ๋ค์จ์ด์ yield ๋ ๋, ๋ฏธ๋ค์จ์ด๋ Promise ๊ฐ ๋๋ ๋ ๊น์ง Saga ๋ฅผ ์ผ์์ ์ง ์ํค๋ ๊ฒ
์ฐ์ ์ ์ผ ์๋์ animalSaga ํจ์๋ถํฐ ์ดํด๋ณด์ ์ด ํ๋ก์ ํธ์์๋ ์ด๋ ํ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด apiํธ์ถ์ ์ํ param๊ณผ ํจ๊ป getData๋ผ๋ ์ก์ ์ด dispatch ๋๊ฒํ์๋ค.
์๋์ ๊ฐ์ ํ์์ผ๋ก dispatch ๋๋ค
const param = {
city: 411400,
kind: 210020,
};
dispatch(getData(param));
์ด์ saga์ ์ฝ๋๋ฅผ ์ดํด๋ณด์
import { animalAction } from './animal';
import { call, put, takeEvery } from '@redux-saga/core/effects';
import * as API from '../service/get-data';
interface DescriptionParams {
age: number;
careAddr: string;
careNm: string;
careTel: string;
chargeNm: string;
colorCd: string;
desertionNo: number;
filename: string;
happenDt: number;
happenPlace: string;
}
interface ParamType {
city: number;
kind: number;
}
// get Saga
export function* getDataSaga(action: { payload: ParamType }) {
const { getDataSuccess, getDataFailure } = animalAction;
const param = action.payload;
try {
const response: DescriptionParams = yield call(API.getAnimal, param);
// call์ ๋ฏธ๋ค์จ์ด์๊ฒ ํจ์์ ์ธ์๋ค์ ์คํํ๋ผ๋ ๋ช
๋ น
yield put(getDataSuccess(response));
// put์ dispatch ๋ฅผ ๋ปํ๋ค.
} catch (err) {
yield put(getDataFailure(err));
}
}
// Main Saga
export function* animalSaga() {
const { getData } = animalAction;
yield takeEvery(getData, getDataSaga);
}
์์์ exportํ animalAction์ getData๋ฅผ ์ ์ธํ๊ณ takeEvery์ ๋ฃ์ด์ค๋ค
์ฌ๊ธฐ์ takeEvery ๋ ์ฌ๋ฌ๊ฐ์ fetchData ์ธ์คํด์ค๋ฅผ ๋์์ ์์ํ๊ฒํ๋ค.
getData๋ผ๋ ์ก์ ์ด ํธ์ถ์ด ๋๋ค๋ฉด getDataSaga๋ผ๋ ํจ์๋ฅผ take ํ๋ค ๋ผ๊ณ ๋ณด๋ฉด๋๊ฒ ๋ค !
์ด์ getDataSaga์์๋ animalAction์์ getDataSuccess์ getDataFailure ์ด๋ผ๋ ์ก์ ๋๊ฐ๋ฅผ ๋ฐ์์ค๊ณ api์ param๋ ๋ฐ์์ try {} ์์์ param๊ณผ ํจ๊ป api๋ฅผ call (ํจ์ ์คํ) ํ๊ณ ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์์์ง๋ฉด getDataSuccess ์ก์ ์ put(dispatch ํ๋๊ฒ) ํ๊ณ ์๋ฌ๊ฐ ์๊ธธ ๊ฒฝ์ฐ์๋ getDataFailure๋ฅผ put ํ๊ฒ๋๋ค
์ด๊ฒ์ด saga์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ผ๊ณ ๋ณด๋ฉด ๋๊ฒ ๋ค
์ด์ rootReducer๋ฅผ ์ดํด๋ณด์
import { animalReducer } from './animal';
import { combineReducers } from 'redux';
import { all } from 'redux-saga/effects';
import { animalSaga } from './saga';
// animalReducer ๋ฅผ rootReducer ๋ก ํฉ์ณ ๋ด๋ณด๋
const rootReducer = combineReducers({
animalReducer,
});
export function* rootSaga() {
// all ์ ์ฌ๋ฌ ์ฌ๊ฐ๋ฅผ ๋์์ ์คํ์์ผ์ค๋ค. ํ์ฌ๋ animalSaga ํ๋.
yield all([animalSaga()]);
}
export type ReducerType = ReturnType<typeof rootReducer>;
export default rootReducer;
rootReducer์ combineReducer๋ก ๋ฆฌ๋์ ํ์ผ์์ exportํ animalReducer๋ฅผ ๋ด์์ค๋ค
rootSagaํจ์๋ animalSaga๋ผ๋ Sagaํจ์๋ฅผ ์คํ์์ผ์ค๋ค
์ด ํจ์๊ฐ ์คํ ๋์ด์ ธ ์์ด์ผ ๋ฏธ๋ค์จ์ด๊ฐ ์ ์์ ์ผ๋ก ์๋ํ๋ค
๋ง์ง๋ง์ผ๋ก ReturnType์ด๋ผ๋ type์ ์ง์ ํด ์ฃผ์๋๋ฐ TypeScript ํ๊ฒฝ์์ ๋ฆฌ๋์ค๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํ์์ ์ผ๋ก ํด์ฃผ์ด์ผ ํ๋ค.
์ด์ ๋ useSelector๋ฅผ ์ด์ฉํ์ฌ state๋ฅผ ๋ฐ์์ฌ ๋ ํ์ ์ ์ง์ ํด์ฃผ์ง ์๋๋ค๋ฉด ์ ์์ ์ผ๋ก state๋ฅผ ๋ฐ์์ค์ง ๋ชปํ๋ค
์๋์ ๊ฐ์ด ํ๋ฉด๋๋ค.
const { animal } = useSelector<ReducerType, Description>(
(state) => state.animalReducer
);
index ํ์ผ
import App from './App';
import React from 'react';
import ReactDOM from 'react-dom';
import GlobalStyle from './assets/styles/global-styles';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer, { rootSaga } from './modules/rootReducer';
import { Provider } from 'react-redux';
import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: rootReducer,
middleware: [sagaMiddleware],
});
// saga๋ฅผ ์คํ
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={store}>
<GlobalStyle />
<App />
</Provider>,
document.getElementById('root')
);
์ด๋ ๊ฒ Redux-toolkit๊ณผ Saga๋ฅผ TypeScript ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๋จํ๊ฒ ์์๋ณด์๋ค
๋ ํ์ตํด์ ๋ฅ์ํ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ํด๋ณด์
reference
https://im-developer.tistory.com/195
'Redux' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Redux Toolkit ์ ๋ํ์ฌ ์์๋ณด๊ธฐ (0) | 2021.06.07 |
|---|---|
| ๋ฆฌ๋์ค์์ Hooks ์ฌ์ฉํ๊ธฐ (0) | 2021.05.25 |
| ๋ฆฌ๋์ค ์์ ํ๊ฒฝ ์ค์ ๋ฐ ์ฌ์ฉ๋ฐฉ๋ฒ (1) | 2021.05.24 |
| ๋ฆฌ๋์ค๋ ๋ฌด์์ธ๊ฐ ? (0) | 2021.05.20 |