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