[React] paging처리된 api에서 데이터 가져오기

김채운·2022년 11월 4일
1

React

목록 보기
13/26

오픈마켓 프로젝트를 진행하던중 장바구니 페이지에서 전체 상품을 가져와야 하는 일이 생겼는데 api가 페이징 처리가 되어 있어서 한번에 데이터를 가져올 방법을 생각해봐야 했다.

const itemCount = useSelector((state) => state.product.count)
const pageNumbers = [];
console.log("아이템카운트", itemCount)
for (let i = 1; i <= Math.ceil(itemCount / 15); i++) {
        pageNumbers.push(i);
  }

 useEffect(() => {  
        pageNumbers.map((p) => {
            return dispatch(getProductDB(p))
        })
    }, [dispatch, pageNumbers])
  • 처음엔 이렇게 코드를 짰는데 무한렌더링이 일어났다. pageNumbers에 배열을 넣어줄 때 i <= Math.ceil(itemCount / 15) 이렇게 범위가 설정되어 있어서 무한렌더링이 일어날거라고는 생각을 못했는데 저 범위만큼 데이터를 가져온다음에 또 저 범위만큼 데이터를 가져오는 식으로 무한 렌더링이 일어났다. 그래서 해결 방법으로 useEffect의 두번째 인자를 빈배열로 만들어 주었고 렌더링은 한번만 일어났지만 첫페이지 데이터만 가져오게 되어서 map으로 돌려준 의미가 없었다.
    그래서 세번째 대안으로 생각한 게 조건식이었다.👇
 useEffect(() => {
        if (pageNumbers.length-1<Math.ceil(itemCount / 15)) {
            pageNumbers.map((p) => {
                return dispatch(getProductDB(p))
            })
        }
    }, [dispatch, itemCount])
  • 근데 이렇게 하니 무한렌더링이 되지 않고 데이터를 페이지 순서대로 가져와지긴 하는데 현재 데이터가 62개라면 62개가 가져와진 후 어떤 값의 변화로 재렌더링이 되었을 때, 62개에 추가로 같은 데이터가 다시 가져와지는 일이 생겼다.
    그래서 조건식을 다시 손 봐서 최종 코드는 👇 이렇게 완성이 되었다.

//ShoppingCart.js
const itemCount = useSelector((state) => state.product.count)
const pageNumbers = [];
console.log("아이템카운트", itemCount)
for (let i = 1; i <= Math.ceil(itemCount / 15); i++) {
        pageNumbers.push(i);
}
pageNumbers.shift()

 useEffect(() => {
        if (itemCount > 1 && productList.length < itemCount) {
            pageNumbers.map((p) => {
                return dispatch(getProductDB(p))
            })
        } else if (itemCount <= 1) {
            dispatch(getProductDB(1))
        }
    }, [dispatch, itemCount])

// product.js (Redux)
const GETPRODUCT = "product/GETPRODUCT";

const initialState = {
    products: [],
    count: 1,
    productOne: [],
    sellerProducts: [],
}

const getProduct = createAction(GETPRODUCT, (products, count) => ({ products, count }))

export const getProductDB = (page) => {
    console.log(page)
    return async function (dispatch) {
        await apis.getProduct(page)
            .then((res) => {
                console.log("상품", res.data)
                dispatch(getProduct(res.data.results, res.data.count))
            })
            .catch((error) => {
                console.log("상품에러", error)
                return;
            })
    }
}

export default handleActions(
 {
        [GETPRODUCT]: (state, action) =>
            produce(state, (draft) => {
                console.log(action.payload)
                draft.products = draft.products.concat(action.payload.products)
                draft.count = action.payload.count
            }),
       
    },
    initialState
);
  • count의 갯수를 1로 초기 설정을 해둔다. 그럼 처음 count의 값은 1이기 때문에 그 값을 통해서 첫번째 페이지의 데이터를 가져온다. 그렇게 되면 dispatch가 getProductDB를 가리키고 action을 일으켜서 밑에 코드에서처럼 reducer를 통해 count숫자가 62개로 업데이트가 된다. 그렇게 되면 itemCount가 62가 되고 pageNumbers는 [1,2,3,4,5]의 배열이 만들어진다. 그럼 저 배열을 통해서 map을 돌려 5개의 페이지의 데이터를 다 가져올 수 있게끔 해주는데 조건식으로는 itemCount가 1보다 큰 조건을 걸어준다 이미 itemCount가 1보다 작거나 같을때 첫번째 페이지의 데이터를 가져올 수 있게 해줬기 때문에 그 이후의 데이터를 가져올 때는 itemCount가 1보다 큰 경우에 map을 돌려서 차례대로 페이지의 데이터를 가져오게끔 했다. 근데 이렇게만 조건식을 걸어두면 렌더링이 될 때마다 62에서 추가로 15개의 데이터가 반복적으로 가져와지기 때문에 &&으로 useSelector를 통해 가져온 productList의 길이가 itemCount보다 작을때까지만 데이터를 가져오게끔 해주는 조건식도 포함을 해줬다. 그럼 62개까지 가져와지면 더이상 데이터가 추가되지 않는다.
  • 여기에서 한가지 문제가 있다면 이미 itemCount의 초기설정이 1로 되어있어서 첫페이지의 데이터를 가져온 상태에서 그 다음 [1,2,3,4,5]의 배열만큼 map을 돌려서 차례대로 데이터를 가져와지게 하는데 그렇게 되면 첫페이지의 데이터를 두 번 가져와지게 된다. 그래서 map으로 돌릴 배열에서 pageNumbers.shift()의 shift()(배열의 맨 앞의 값을 제거)해주는 함수를 통해 1을 제거하고 [2,3,4,5] 페이지의 값만 데이터를 가져오게끔 해줬다.
const [page, setPage] = useState(1)
const [list, setList] = useState([])

useEffect(()=>{
     api.get(`/products/?page=${page}`).then((res) => {
            setList((prev) => prev.concat(res.data.results))//리스트 추가
            setPage(prev => prev + 1)
        }).catch((error) => {
            setMoreData(false)
            return;
        })
})

위처럼 redux가 아닌 app.js에서 데이터를 받아올때마다 page+1을 해줘서 데이터를 가져오는 간단한 방법도 있지만 지금은 작은 규모의 프로젝트여서 상품리스트를 가져올 일이 그렇게 많진 않지만 이게 큰 규모였다면 페이지마다 상품리스트를 가져다 사용할 일이 많다고 생각이 들어서 redux를 사용해서 데이터를 가져오는 방법이 더 낫다고 생각되었다..

0개의 댓글