Redux는 데이터의 상태 관리를 용이하게 만드는 라이브러리 중 하나이다.
조금더 세팅을 간편하게 하고 사용할 수 있는 Recoil도 있지만 현업에서 사용한다고 들었기에 현재 만들고 있는 TypeScript 언어를 사용 중인 프로젝트에 한번 적용하며 공부해 보고자 했고 그 과정을 정리해 보려 한다
Redux를 사용하기 위해 먼저 Redux
와 Redux Toolkit
을 설치한다.
npm install @reduxjs/toolkit react-redux
Redux에서는 상태(state)와 액션(action)을 정의하여 애플리케이션의 상태를 관리한다.
상태는 애플리케이션의 데이터를 저장하고, 액션은 상태를 변경하는 요청이다. 프로젝트에서 상품 상세 페이지에 나타나는 '상품 상세정보 데이터'를 상태관리 하기 위해 아래와 같이 Redux를 사용했다.
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { urlApi } from 'src/api/axiosInstance';
// API로 받아 올 상품 데이터의 타입 정의
interface ProductData {
product_id: number;
created_at: string;
updated_at: string;
image: string;
product_name: string;
price: number;
shipping_method: string;
shipping_fee: number;
stock: string;
products_info: string;
seller: number;
store_name: string;
}
// 상태의 타입 정의
interface ProductState {
productData: ProductData | null; // 상품 데이터
error: null | any; // 오류 메시지
loading: boolean; // 로딩 상태
}
// 초기 상태 정의
const initialProductState: ProductState = {
productData: null,
error: null,
loading: false,
};
액션을 정의하는 데에 사용할 비동기 작업을 처리하기 위해 Redux에서는 createAsyncThunk
함수를 사용한다. 이를 통해 API 호출과 같은 비동기 작업을 쉽게 관리할 수 있다. 아래와 같이 axios로 세팅한 axiosInstance를 가져와 상품 상세정보 데이터를 불러 왔다.
export const getProductData = createAsyncThunk<ProductData, number>(
'/product/getProduct',
async (postId) => {
const res = await urlApi.get(`/products/${postId}/`);
return res.data;
},
);
// 액션 생성자 함수를 사용하여 액션 정의
export const setProductData = createSlice({
name: 'product',
initialState: initialProductState,
reducers: {
// setProductData 액션
setProductData: (state, action: PayloadAction<ProductData | null>) => {
// 상태의 productData를 업데이트
state.productData = action.payload;
},
},
// 비동기 작업에 대한 액션은 extraReducers에 정의
extraReducers: (builder) => {
builder
.addCase(getProductData.pending, (state) => {
// API 요청이 보류 중일 때 상태 업데이트
state.error = null;
state.loading = true;
})
.addCase(
getProductData.fulfilled,
(state, action: PayloadAction<ProductData>) => {
// API 요청이 성공하면 상태 업데이트
state.error = null;
state.loading = false;
state.productData = action.payload;
},
)
.addCase(getProductData.rejected, (state, action: PayloadAction<any>) => {
// API 요청이 실패하면 상태 업데이트
state.error = action.payload;
state.loading = false;
});
},
});
// 액션 생성자 함수 내보내기
export const { setProductData } = productSlice.actions;
// 리듀서 내보내기
export default productSlice.reducer;
이렇게 상태와 액션에 대한 Redux 세팅을 마치고, 상태를 업데이트할 때에는 디스패치(dispatch)를 사용한다. 저장한 '상품 상세정보 데이터'를 각 상품 상세 페이지마다 갱신해 불러오기 위해 다음과 같이 코드를 작성했다.
import { useDispatch, useSelector } from 'react-redux';
dispatch(setProductData(productData));
...
const productId = location.state;
const dispatch = useDispatch<any>();
const productData = useSelector(
(state: RootState) => state.product.productData,
);
const loading = useSelector((state: RootState) => state.product.loading);
useEffect(() => {
dispatch(getProductData(productId));
}, [productId]);
import
문:useDispatch
와 useSelector
훅 불러오기const dispatch = useDispatch();
:const productData = useSelector(...)
const loading = useSelector(...)
:useEffect
:결과적으로 프로젝트에는 상태마다 작성하는 useEffect
의 사용과 코드 수를 조금 더 줄여보고자 React-query를 도입하기로 하면서 React-query와 조금더 잘 맞는 Recoil로 변경하기로 했지만,
다른 데이터들도 Redux로 세팅해 보면서 Redux를 조금이나마 익혀보고 공부해볼 수 있었기에 나중 다시 사용하게 될 때를 위해 기록을 남겨 본다!