ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -30) Pagination적용하기
    NODE.JS 2021. 5. 30. 18:32

    여기까지 이슈가 생겼다면 해당 강의 한번 보기!

    먼저 페이지사이즈를 정하고 정해진 수만큼 보여지도록 설정한다. 

     

    기능을 추가하기 위해서 백엔드와 프론트엔드 모두 다음과 같은 내용을 추가해준다. 

    먼저 backend(productRouter.js)

    
    productRouter.get('/' ,expressAsyncHandler(async(req, res)=>{
        const pageSize  = 3;
        const page = Number(req.query.pageNumber)||1;
    
        const seller = req.query.seller || ''
        const name = req.query.name || ''
        const category = req.query.category || ''
        const min = req.query.min && Number(req.query.min) !== 0? Number(req.query.min):0
        const max = req.query.max && Number(req.query.max) !== 0? Number(req.query.max):0
        const rating = req.query.rating && Number(req.query.rating) !== 0? Number(req.query.rating):0
        const order = req.query.order || ''
    
        const sellerFilter = seller? {seller} :{};
        const nameFilter = name? {name:{$regex:name, $options:'i'}} :{};
        const categoryFilter = category? {category} :{};
        const priceFilter = min && max ? {price: {$gte:min, $lte:max}}:{};
        const ratingFilter = rating ? {rating: {$gte:rating}}:{};
        const sortOrder = order==='lowest'? {price:1} : 
                          order==='highest'? {price:-1} :
                          order==='toprated'? {rating:-1} :
                          {_id:-1};
    
        const count = await Product.count({...sellerFilter, ...nameFilter, ...categoryFilter, ...priceFilter, ...ratingFilter})
        const products = await Product.find({...sellerFilter, ...nameFilter, ...categoryFilter, ...priceFilter, ...ratingFilter}).populate('seller', 'seller.name seller.logo').sort(sortOrder).skip(pageSize*(page-1)).limit(pageSize);
        res.send({products, page, pages:Math.ceil(count /pageSize)});
    }));
    

     

    frontend

    productactions.js

        dispatch({
            type:PRODUCT_LIST_REQUEST
        });
        try{
            const {data} = await Axios.get(`/api/products?pageNumber=${pageNumber}&seller=${seller}&name=${name}&category=${category}&min=${min}&max=${max}&rating=${rating}&order=${order}`);
            dispatch({type:PRODUCT_LIST_SUCCESS, payload:data});
        }catch(error){
            dispatch({type:PRODUCT_LIST_FAIL, payload:error.message});
        }
    }
    
    export const detailsProduct = (productId) =>async(dispatch)=>{
        dispatch({
            type:PRODUCT_DETAILS_REQUEST, payload:productId
        });
        try{
            const {data} = await Axios.get(`/api/products/${productId}`);
            dispatch({type:PRODUCT_DETAILS_SUCCESS, payload:data})
    
        }catch(error){
            dispatch({type:PRODUCT_DETAILS_FAIL, payload:error.response && error.response.data.message? error.response.data.message:error.message})
        }
    
    }
    

     

     

    productReducers

    export const productListReducer = (state = { loading:true, products: []}, action)=>{
        switch(action.type){
            case PRODUCT_LIST_REQUEST:
                return {loading: true};
            case PRODUCT_LIST_SUCCESS:
                return {loading:false, products:action.payload.products, pages:action.payload.pages, page:action.payload.page};
            case PRODUCT_LIST_FAIL:
                return {loading:false, error:action.payload};
            default:
                return state;
        }
    }

     

     

    searchscreen에 페이지를 추가해준다. 

    	const {name = 'all', category='all', min=0, max=0, rating=0, order='newest', pageNumber=1} = useParams();
        
        const productList = useSelector(state => state.productList)
        
        const {loading, error, products, page, pages} = productList
            useEffect(() => {
            dispatch(listProducts({pageNumber ,name:name!=='all'? name: '', category:category !=='all'? category:'', min, max,rating,order}))
        }, [category, dispatch,name,min, max,rating,order, pageNumber])
        
        const getFilterUrl = (filter) =>{
            const filterPage = filter.page ||pageNumber;
            const filterCategory = filter.category ||category;
            const filterName = filter.name ||name;
            const filterMin = filter.min? filter.min :filter.min===0? 0:min;
            const filterMax = filter.max? filter.max :filter.max===0? 0:max;
            const filterRating = filter.rating||rating;
            const sortOrder = filter.order||order;
            return `/search/category/${filterCategory}/name/${filterName}/min/${filterMin}/max/${filterMax}/rating/${filterRating}/order/${sortOrder}/pageNumber/${filterPage}`
        }
        
        ...
                            (<>
                            {products.length===0 &&  <MessageBox>No Product Found</MessageBox>}
                            <div className="row center">
                                {products.map(product=>(
                                    <Product key={product._id}  product = {product}/> 
                                ))}
                            </div>
                            <div className="row center pagination">//여기에 추가
                                {
                                    [...Array(pages).keys()].map(x=>(
                                        <Link key = {x+1} to={getFilterUrl({page:x+1})}>{x+1}</Link>
                                    ))
                                }
                            </div>
                        </>)

     

     

     

    app.js수정

                  <Route path="/search/category/:category/name/:name/min/:min/max/:max/rating/:rating/order/:order/pageNumber/:pageNumber" component={SearchScreen} exact></Route>
    

     

     

     

    페이지가 뜬다

     

    스타일을 주자

    /* Pagenation */
    
    .pagination a{
      padding:1rem;
      margin:0.5rem;
      border-radius: 0.5rem;
      border:0.1rem #a4a4a4 solid;
      font-family: Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    
    }

     

     

    현재있는 페이지는 스타일을 다르게 주도록 해보자. 

                            <div className="row center pagination">
                                {
                                    [...Array(pages).keys()].map(x=>(
                                        <Link classname={x+1===page? 'active':''} key = {x+1} to={getFilterUrl({page:x+1})}>{x+1}</Link>
                                    ))
                                }
                            </div>

     

    .pagination a.active{
      font-weight: bold;
    }

    페이지마다 잘 나온다.

     

    이제 products에도 적용시켜보자. 

    productListScreen.js

     

    import { useParams } from 'react-router';
    import {Link} from 'react-router-dom'
    
        const {pageNumber=1} = useParams();
        //page, pages추가
        const {loading, error, products, page, pages}= productList;
    
    
        useEffect(() => {
            if(successCreate){
                dispatch({
                    type:PRODUCT_CREATE_RESET
                })
                props.history.push(`/product/${createdProduct._id}/edit`)
            }
            if(successDelete){
                dispatch({type:PRODUCT_DELETE_RESET})
            }
            dispatch(listProducts({pageNumber, seller:sellerMode? userInfo._id :''}))
        }, [createdProduct, dispatch, props.history, successCreate, successDelete,sellerMode, pageNumber])//pageNumber 바뀌면 다시 실행하기
        
        
        
                    </table>//테이블 끝나는지점에 추가
                <div className="row center pagination">
                    {
                        [...Array(pages).keys()].map((x)=>(
                            <Link to={`/productlist/pageNumber/${x+1}`} className={x + 1 === page ? 'active' : ''} key = {x+1} >{x+1}</Link>
                        ))
                    }
                </div>
                </>
    

     

    app.js에 루트 추가

                  <AdminRoute path="/productlist/pageNumber/:pageNumber" component={ProductListScreen} exact></AdminRoute>
    

     

     

     

     

     

     

     

    추가하고싶다면 order나 다른 페이지에도 추가할 수 있다. 

     

     

     

     

     

     

     

Designed by Tistory.