React에서 Redux, Reducer, 그리고 Redux-Saga를 활용한 상태 관리(Redux-DevTools)
리액트 애플리케이션을 개발하다 보면 상태 관리가 복잡해지는 경우가 많습니다. 이런 상황에서 상태 관리를 보다 체계적으로 관리하기 위해 Redux를 사용할 수 있습니다. 또한, 비동기 작업을 효율적으로 처리하기 위해 Redux-Saga를 활용할 수 있습니다. 이번 글에서는 리덕스(Redux), 리듀서(Reducer), 사가(Saga)에 대해 자세히 알아보겠습니다.
1. Redux란?
Redux는 애플리케이션의 상태를 중앙에서 관리하는 라이브러리입니다. Redux는 상태를 예측 가능하게 관리하며, 다양한 컴포넌트 간에 데이터를 쉽게 공유할 수 있도록 돕습니다.
Redux의 주요 개념은 다음과 같습니다:
- 스토어(Store): 애플리케이션의 상태를 담고 있는 객체입니다.
- 액션(Action): 상태에 변화를 일으키는 객체입니다. 액션은 타입(type)과 페이로드(payload)를 가집니다.
- 리듀서(Reducer): 현재 상태와 액션을 받아 새로운 상태를 반환하는 순수 함수입니다.
- 디스패치(Dispatch): 액션을 스토어에 전달하는 함수입니다.
dispatch는 Redux에서 액션을 발생시키는 함수입니다. 액션은 상태 변경을 위한 지시사항이라고 볼 수 있습니다. 위 코드에서는 직접적으로 보이지 않지만, 컴포넌트에서 다음과 같이 사용될 것입니다:
dispatch(fetchCallbacksRequest({ page: 1, size: 20 }));
2. 리듀서(Reducer)란?
리듀서는 Redux에서 상태 변화를 처리하는 순수 함수입니다. 현재 상태와 액션을 받아서 새로운 상태를 반환합니다.
아래는 createSlice를 사용한 리듀서 예제입니다:
// 콜백 상태를 위한 인터페이스 정의
interface CallbackState {
isCallbackLoading: boolean; // 콜백 데이터 로딩 중 여부
isCallbackSuccess: boolean; // 콜백 데이터 로드 성공 여부
isCallbackError: boolean; // 콜백 데이터 로드 실패 여부
isDeletingCallback: boolean; // 콜백 삭제 중 여부
/*
생략
*/
};
}
// 초기 상태 정의
const initialState: CallbackState = {
// ... (초기값 설정)
};
// 콜백 관련 리듀서 정의
const callbackSlice = createSlice({
name: "callback",
initialState,
reducers: {
// 콜백 데이터 요청 액션
fetchCallbacksRequest: (state, action: PayloadAction<{ page: number, size: number }>) => {
state.isCallbackLoading = true;
// ... (상태 업데이트)
},
// 콜백 데이터 요청 성공 액션
fetchCallbacksSuccess: (state, action: PayloadAction<any>) => {
state.isCallbackLoading = false;
state.callbackData = action.payload;
// ... (상태 업데이트)
},
// 콜백 데이터 요청 실패 액션
fetchCallbacksError: (state, action: PayloadAction<string>) => {
state.isCallbackLoading = false;
state.error = action.payload;
// ... (상태 업데이트)
},
// 콜백 상태 초기화 액션
resetCallbackState: (state) => {
// ... (상태 초기화)
},
// 콜백 삭제 요청 액션
deleteCallbackRequest: (state, action: PayloadAction<any[]>) => {
state.isDeletingCallback = true;
// ... (상태 업데이트)
},
// 콜백 삭제 성공 액션
deleteCallbackSuccess: (state) => {
state.isDeletingCallback = false;
// ... (상태 업데이트)
},
// 콜백 삭제 실패 액션
deleteCallbackError: (state, action: PayloadAction<string>) => {
state.isDeletingCallback = false;
state.deleteCallbackError = action.payload;
},
},
});
// 액션 생성자 내보내기
export const {
fetchCallbacksRequest,
fetchCallbacksSuccess,
fetchCallbacksError,
resetCallbackState,
deleteCallbackError,
deleteCallbackRequest,
deleteCallbackSuccess
} = callbackSlice.actions;
위 코드에서 callbackSlice는 상태와 액션을 정의하고, 리듀서 함수를 자동으로 생성합니다. 각 액션에 대응하는 상태 변화를 관리합니다.
3. Redux-Saga란?
Redux-Saga는 Redux 애플리케이션에서 비동기 작업을 처리하기 위한 미들웨어입니다. Redux-Saga는 제너레이터 함수를 사용하여 비동기 작업을 효율적으로 관리할 수 있게 해줍니다.
Saga의 주요 개념은 다음과 같습니다:
- 사가(Saga): 제너레이터 함수로 작성된 비동기 작업 처리 함수입니다.
- 이펙트(Effect): 사가 내부에서 발생하는 비동기 작업입니다. 예를 들어 call, put 등이 있습니다.
- takeEvery: 특정 액션 타입에 대해 모든 액션을 처리합니다.
- takeLatest: 특정 액션 타입에 대해 마지막 액션만 처리합니다.
아래는 Redux-Saga의 예제입니다:
import { call, put, takeLatest } from 'redux-saga/effects';
import {
fetchCallbacksRequest,
fetchCallbacksSuccess,
fetchCallbacksError,
deleteCallbackError,
deleteCallbackRequest,
deleteCallbackSuccess
} from '../reducers/callback';
import api from './axiosInstance';
// 콜백 데이터를 가져오는 API 호출 함수
const fetchCallbacksAPI = (page: number, size: number) => {
return api.get(`/user/callback?page=${page}&size=${size}`);
}
// fetchCallbacksRequest 액션에 대응하는 사가 함수
function* handleFetchCallbacks(action: ReturnType<typeof fetchCallbacksRequest>) {
try {
const { page, size } = action.payload;
console.log('Fetching callbacks...', { page, size });
const result = yield call(fetchCallbacksAPI, page, size); // API 호출
console.log('API response:', result);
yield put(fetchCallbacksSuccess(result.data)); // 성공 액션 디스패치
} catch (error) {
console.error('Error fetching callbacks:', error);
yield put(fetchCallbacksError(error.response?.data?.message || 'An error occurred while fetching callbacks')); // 실패 액션 디스패치
}
}
// 콜백 데이터를 삭제하는 API 호출 함수
const deleteCallbacksAPI = (callbackIds: any[]) => {
return api.post('/user/callback/delete', callbackIds);
}
// deleteCallbackRequest 액션에 대응하는 사가 함수
function* handleDeleteCallbacks(action: ReturnType<typeof deleteCallbackRequest>) {
try {
yield call(deleteCallbacksAPI, action.payload); // API 호출
yield put(deleteCallbackSuccess()); // 성공 액션 디스패치
// 삭제 후 목록 새로고침
yield put(fetchCallbacksRequest({ page: 0, size: 20 }));
} catch (error) {
yield put(deleteCallbackError(error.response?.data?.message || 'An error occurred while deleting callbacks')); // 실패 액션 디스패치
}
}
// 루트 사가
export default function* callbackSaga() {
yield takeLatest(fetchCallbacksRequest.type, handleFetchCallbacks); // 최신 fetchCallbacksRequest 액션만 처리
yield takeLatest(deleteCallbackRequest.type, handleDeleteCallbacks); // 최신 deleteCallbackRequest 액션만 처리
}
위 코드에서 handleFetchCallbacks와 handleDeleteCallbacks 사가는 비동기 작업(API 호출)을 처리합니다. takeLatest를 사용하여 최신 액션만 처리하도록 설정했습니다.
4. Redux와 Saga의 통합
리덕스와 사가를 통합하여 사용하려면 redux-saga 미들웨어를 설정해야 합니다. 기본적인 설정 과정은 다음과 같습니다:
import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import callbackReducer from './reducers/callback';
import callbackSaga from './sagas/callback';
// 사가 미들웨어 생성
const sagaMiddleware = createSagaMiddleware();
// 스토어 설정
const store = configureStore({
reducer: {
callback: callbackReducer, // 콜백 리듀서 등록
},
middleware: [sagaMiddleware], // 사가 미들웨어 적용
});
// 사가 실행
sagaMiddleware.run(callbackSaga);
export default store;
위 코드에서 configureStore를 사용하여 스토어를 설정하고, createSagaMiddleware로 사가 미들웨어를 생성하여 스토어에 적용합니다. 그런 다음 sagaMiddleware.run을 호출하여 루트 사가를 실행합니다.
이렇게 Redux와 Redux-Saga를 사용하여 React 애플리케이션에서 상태 관리와 비동기 작업을 효율적으로 처리할 수 있습니다. 이를 통해 코드의 가독성과 유지보수성을 높이고, 애플리케이션의 상태를 보다 체계적으로 관리할 수 있습니다.
Redux DevTools
Redux 애플리케이션의 상태를 모니터링하고 디버깅하기 위한 도구입니다. Redux는 애플리케이션 상태를 예측 가능하게 관리하기 위한 상태 관리 라이브러리이며, Redux DevTools는 이를 더 효과적으로 사용하고 디버깅하는 데 도움을 줍니다
상태를 한번에 확인이 가능하며
어떤 것들을 작성했는지도 한번에 알 수 있는 편리한 도구이다.