14.4 react-redux 및 비동기 처리 라이브러리
react-redux는 connect, thunk 등의 함수를 제공합니다. 여기서는 배너 데이터를 가져오는 예제를 통해 비동기 처리 방법을 살펴보겠습니다.
store 설정
비동기 작업은 일반적으로 미들웨어를 사용해야 합니다. configureStore는 이미 redux-thunk를 자동으로 통합하고 있습니다. action에서는 함수를 반환해야 합니다.
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducer/rootReducer';
const store = configureStore({ reducer: rootReducer });
export default store;
action 작성
ACTION_TYPE을 상수로 정의하고, HTTP 요청을 위해 axios를 사용합니다.
import { SET_BANNERS } from './actionTypes';
import axios from 'axios';
export const setBannersAction = (banners) => ({
type: SET_BANNERS,
banners
});
export const fetchBanners = () => async (dispatch) => {
try {
const response = await axios.get('http://api.example.com/banners');
dispatch(setBannersAction(response.data));
} catch (error) {
console.error('배너 데이터 로드 실패:', error);
}
};
reducer 구현
전체적인 상태 관리를 위해 여러 reducer를 합칩니다.
import { combineReducers } from 'redux';
import bannerSlice from './slices/bannerSlice';
const rootReducer = combineReducers({
banner: bannerSlice.reducer
});
export default rootReducer;
개별 reducer: bannerSlice
초기 상태를 설정하고, 다양한 액션 유형에 따라 상태를 업데이트합니다.
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
banners: []
};
const bannerSlice = createSlice({
name: 'banner',
initialState,
reducers: {
setBanners: (state, action) => {
state.banners = action.payload;
}
}
});
export default bannerSlice;
상수 정의
ACTION_TYPE 등을 명확히 하기 위해 상수를 선언합니다.
export const SET_BANNERS = 'SET_BANNERS';
React 애플리케이션에서 store 연결
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
왜 미들웨어가 필요한가?
reducer는 순수 함수여야 하므로 직접 비동기 작업을 수행할 수 없습니다. 따라서 비동기 작업은 미들웨어에서 처리하여 원시 액션을 생성해야 합니다. 이렇게 되면 reducer는 해당 액션을 처리해 상태를 변경하고 UI를 업데이트할 수 있습니다.
Redux Thunk의 단점
- 비동기 작업 처리 시 많은 보일러플레이트 코드가 필요함.
- 액션 생성자는 액션 타입뿐 아니라 비동기 논리도 함께 처리해야 함.
Saga 적용하기
Saga는 복잡한 비동기 작업을 더 효율적으로 처리할 수 있도록 도와줍니다.
import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import bannerSaga from './sagas/bannerSaga';
import rootReducer from './reducer/rootReducer';
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(sagaMiddleware)
});
sagaMiddleware.run(bannerSaga);
export default store;
Saga 내부 로직
Saga는 특정 액션 유형을 감지하고, 이를 기반으로 비동기 작업을 실행합니다.
import { put, takeEvery, call } from 'redux-saga/effects';
import axios from 'axios';
import { FETCH_BANNERS, setBannersAction } from '../actions/bannerActions';
function* fetchBannersSaga() {
try {
const response = yield call(axios.get, 'http://api.example.com/banners');
yield put(setBannersAction(response.data));
} catch (error) {
console.error('사가 에러:', error);
}
}
function* watchFetchBanners() {
yield takeEvery(FETCH_BANNERS, fetchBannersSaga);
}
export default function* bannerSaga() {
yield watchFetchBanners();
}