문제점
1. api call로 불러올 때 해당 데이터 형태의 빈값이 할당됨
2. api로 형태를 전달 후 잠시동안 data crawling 하는 시간이 길어짐
3. 이로 인한 데이터 처리 기간을 delay 시켜줘야함
일단 첫 번 째 redux-saga 구조를 만들어 보자
1. 액션명을 선언해 준다.
export const CRAWLING = "blog/CRAWLING";
export const CRAWLING_SUCESS = "blog/CRAWLING_SUCESS";
export const CRAWLING_FAIL = "blog/CRAWLING_FAIL";
2. 액션명에 맞는 api typescript를 지정해준다.
export const crwalingProduct = createAsyncAction(
CRAWLING,
CRAWLING_SUCESS,
CRAWLING_FAIL
)<CrawlingState, { coupangPartners: coupangProductList }, APIError>();
2-1 CRAWLING(request 요청)일 때는 CrawlingState(형태는 객체 타입에 string[]을 요청해주는 타입을 보냄)
2-2 CRAWLING_SUCESS(response를 제대로 받았을 때) 했을 시 객체 타입에 coupangPartners에 coupangProductList라는 배열타입을 선언
2-3 CRAWLING_FAIL(request 요청 실패시) API ERROR를 나타내줌
액션명과 할당한 typescript 요청값들이 일치! 이제 saga를 본격적으로 만들어보자
function* crawlingProductSaga(
action: ReturnType<typeof crwalingProduct.request>
// request가 성공시 typescript return type을 활용해서 요청한 값을 유츄해냄
) {
try {
// 어드민 로그인시에만 활용을 해야 하기 때문에 yield select를 활용해서 해당 로그인 데이터를 가져옴
const isLoggedIn: UserResponse = yield select(LoginStatusSelector.data);
if (isLoggedIn) {
const ItemName = action.payload;
// 선택한 itemName 값을 action.payload로 전달
const data: AxiosResponse<CrawlingResponse> = yield callWrapperSaga(
blogService.coupangCrawling,
coupangItemName
);
//callWrapperSaga 는 yield를 custom한 값! 그건 차후에 얘기할거임
const data:axios로 받아온 response data를
requestId로 전달
const reqRequestId = data.data.data.requestId;
// 리퀘스트로 받아온 아이디값을 redux-toolkit store에 전달
yield put(addReqRequestId({ requestId: reqRequestId }));
// 리퀘스트 아이디를 받아온 거를 getRequestId에 전달
const { data: resrequestId } = yield callWrapperSaga(
blogService.getRequestId,
reqRequestId
);
// yield put 해주면서 데이터를 요청 성공시 해당 값을 전달
yield put(
crwalingProduct.success({
crawList: {
isFetching: false,
isSuccess: true,
isError: false,
errorMessage: "에러 없음",
list: list,
},
})
}
} catch (response) {
const { data, status } = response as AxiosResponse;
yield put(crwalingProduct.failure({ data, status }));
}
}
이게 초기 모델인데 문제가 뭐냐!
해당 list를 가져올 때 crawling 하는 시간이 길기 때문에 list:[]를 바로 요청하면 빈 배열값이 할당이됨
이제 이 문제를 해결을 해봐야함
여기서 중요한 점은 redux-saga에는 이런 걸 해결하기 위한 redux-saga yield race 라는게 있음!
race 동기적인 처리를 비동기 적으로 바꿔주는 redux-saga에 제공해줌!
하지만 그래도 이거는 실패할 경우에 나오지 않으니까 다시 이거를 반복을 시켜줘야 됨! 여기서 함수 하나를 처리해서 계속 반복을 시켜주게끔 만듬
function* waitUntilListIsReady(
apiFunction: (
requestId: number
) => Promise<AxiosResponse<ProductList>>,
requestId: number
) {
while (true) {
const { data } = yield call(apiFunction, requestId);
if (data.list.length > 0) {
return data.list;
}
yield delay(5000); // 5초간 대기
}
}
waitUntilListIsReady 말그대로 리스트가 준비되기전까지 뺑이 돌리기!
api function으로 api값을 보내고 거기서 request id를 매개변수로 전해준 다음에 data에 array값이 비었을 때 yield delay를 활용해서 다시 뺑이시켜줌
그래서 이부분을 반영해서 해당 코드를 개선!
const { list } = yield race({
list: callWrapperSaga(
waitUntilListIsReady,
blogService.getRequestId,
reqRequestId
),
timeout: delay(50000),
});
if (list) {
yield put(
crwalingProduct.success({
crawList: {
isFetching: false,
isSuccess: true,
isError: false,
errorMessage: "에러 없음",
list: list,
},
})
);
}
이렇게 함으로서 redux-saga에서 해당 list가 텅빈 걸 확인하고 시간을 딜레이 가능해짐! 다만 불러오는 시간이 너무 길어지면 화면에 spinner는 띄워놔야 할듯 싶다.