NODE.JS

아마존 E-commerce 클론 -30) Pagination적용하기

dodop 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나 다른 페이지에도 추가할 수 있다.