react-shop-web/front & back
더보기 버튼 만들기
tjdvyzl
2023. 1. 28. 11:29
SKIP이란?
어디서 부터 데이터를 가져오는지에 대한 위치 ex) 처음엔 0부터 시작. LIMIT이 6이라면 다음 번엔 0+6부터
LIMIT이란?
처음 데이터를 가져올 때 와 더보기 버튼을 눌러서 가져올 때 얼마나 많은 데이터를 한 번에 가져오는지 지정
더보기 버튼 onClick Function 구현 & SKIP과 LIMIT을 위한 STATE 구현 &
Route 수정(1) & AXIOS 중복 코드 제거 및 loadMoreHandler function 구현(1)
import axios from 'axios';
import React, { useEffect, useState } from 'react'
import {Icon, Col, Row, Card, Carousel} from 'antd'
import Meta from 'antd/lib/card/Meta';
import ImageSlider from '../../utils/ImageSlider'
function LandingPage() {
const [Products, setProducts] = useState([])
const [Skip, setSkip] = useState(0)
const [Limit, SetLimit] = useState(8)
useEffect(() => {
let body = {
skip:Skip,
limit:Limit
}
getProducts(body)
},[])
const loadMoreHandler = () => {
let new_skip = Skip + Limit
let body = {
skip:new_skip,
limit: Limit,
// 더보기 버튼을 눌렀을 때 가는 request라는 정보를 넣어줌
loadMore:true
}
getProducts(body)
setSkip(new_skip)
}
const getProducts = (body) => {
axios.post('/api/product/products', body)
.then(response => {
if(response.data.success){
setProducts(response.data.productInfo)
}else {
alert('상품들을 갖고오는데 실패')
}
})
}
const renderCards = Products.map((product, index) => {
return (
<Col lg={6} md={8} xs={24} key={index}>
<Card
cover={<ImageSlider images={product.images} />}>
<Meta
title={product.title}
description={`${product.price}`}
/>
</Card>
</Col>
)
})
return (
<div style={{ width: '75%', margin: '3rem auto' }}>
<div style={{textAlign: 'center'}}>
<h2>Let's Travel Anywhere <Icon type="rocket"/></h2>
</div>
{/* Filter */}
{/* Search */}
<Row gutter={[16, 16]}>
{renderCards}
</Row>
<div style={{display:'flex', justifyContent: 'center'}}>
<button onClick={loadMoreHandler}>더보기</button>
</div>
</div>
)
}
export default LandingPage
server/product 에서 products 라우트 부분
router.post('/products', (req, res) => {
// product collection에 들어있는 모든 상품 정보를 갖고오기
let limit = req.body.limit ? parseInt(req.body.limit) : 20;
let skip = req.body.skip ? parseInt(req.body.skip) : 0;
// 밑에 처럼 .skip과 .limit에 skip값과 limit값을 넣어주면 skip값부터 최대 skip + limit값 까지만 갖고오라는 의미
Product.find()
.populate("writer")
.skip(skip)
.limit(limit)
.exec(( err, productInfo ) => {
if (err) return res.status(400).json({ success: false, err })
return res.status(200).json({success: true, productInfo})
})
})
처음에 웹이 렌더링 되고 useEffect를 통해 skip과 limit는 초기에 지정해준 값으로 이미지들을 불러온다.
그리고 더보기 클릭을 눌렀을 때 onClick 이벤트가 발생한다.
newSkip 값에는 기존의 skip값과 limit값의 합이 들어간다. 이렇게 되면 두번째엔 8이 들어감으로써 8개만큼 이미지를 스킵하고 9번째 이미지부터 DB에서 불러오도록 구현 할 수 있다.
여기까지 구현했다면 랜딩 페이지에는 이미지가 초기에 설정한 limit값인 8개만큼 랜딩된다.
하지만 더보기 버튼을 눌렀을 땐 기존에 있던 이미지들은 사라지고 새로 더 갖고오는 이미지 2개만 랜딩되게 된다.
이 부분을 고쳐보자.
Spread Operator를 사용해서 가져온 데이터들을 현재 상품 STATE에 추가하기
& Route 수정(2) & Post Size 정하기
이유는 간단하다. getProducts를 처음에 호출했을 땐 이미지 8개를 갖고와서 setProducts를 통해 Products state를 설정해줬다.
문제는 호출을 한 번 더 했을 때 new_skip값에 기존의 skip값 0과 limit값 8을 합한 값이 들어가서 8개 이미지를 스킵한다. 그래서 9번째 이미지부터 limit값 8까지 최대 8개의 이미지를 갖고오니까 2개를 갖고 오게된다. 그리고 갖고 온 2개 이미지를 Products state로 설정해줌으로써 랜딩되는 이미지는 2개밖에 안 나오는 것이다.
이것을 해결하기 위해 스프레드 오퍼레이터를 사용하자.
import axios from 'axios';
import React, { useEffect, useState } from 'react'
import {Icon, Col, Row, Card, Carousel} from 'antd'
import Meta from 'antd/lib/card/Meta';
import ImageSlider from '../../utils/ImageSlider'
function LandingPage() {
const [Products, setProducts] = useState([])
const [Skip, setSkip] = useState(0)
const [Limit, SetLimit] = useState(8)
const [PostSize, setPostSize] = useState(0)
useEffect(() => {
let body = {
skip:Skip,
limit:Limit
}
getProducts(body)
},[])
const loadMoreHandler = () => {
let new_skip = Skip + Limit
let body = {
skip:new_skip,
limit: Limit,
// 더보기 버튼을 눌렀을 때 가는 request라는 정보를 넣어줌
loadMore:true
}
getProducts(body)
setSkip(new_skip)
}
const getProducts = (body) => {
axios.post('/api/product/products', body)
.then(response => {
if(response.data.success){
if (body.loadMore) {
setProducts([...Products, ...response.data.productInfo])
} else {
setProducts(response.data.productInfo)
}
setPostSize(response.data.postSize)
}else {
alert('상품들을 갖고오는데 실패')
}
})
}
const renderCards = Products.map((product, index) => {
return (
<Col lg={6} md={8} xs={24} key={index}>
<Card
cover={<ImageSlider images={product.images} />}>
<Meta
title={product.title}
description={`${product.price}`}
/>
</Card>
</Col>
)
})
return (
<div style={{ width: '75%', margin: '3rem auto' }}>
<div style={{textAlign: 'center'}}>
<h2>Let's Travel Anywhere <Icon type="rocket"/></h2>
</div>
{/* Filter */}
{/* Search */}
<Row gutter={[16, 16]}>
{renderCards}
</Row>
{PostSize >= Limit &&
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button onClick={loadMoreHandler}>더보기</button>
</div>
}
</div>
)
}
export default LandingPage
router.post('/products', (req, res) => {
// product collection에 들어있는 모든 상품 정보를 갖고오기
let limit = req.body.limit ? parseInt(req.body.limit) : 20;
let skip = req.body.skip ? parseInt(req.body.skip) : 0;
// 밑에 처럼 .skip과 .limit에 skip값과 limit값을 넣어주면 skip값부터 최대 skip + limit값 까지만 갖고오라는 의미
Product.find()
.populate("writer")
.skip(skip)
.limit(limit)
.exec(( err, productInfo ) => {
if (err) return res.status(400).json({ success: false, err })
return res.status(200).json({success: true, productInfo, postSize: productInfo.length})
})
})
db로부터 처음에 find함수를 이용하여 모든 이미지를 갖고 왔을 때 postSize의 크기를 지정해줌으로 써
전체 이미지 개수를 알 수 있다. 즉, 현재 limit값이 이 전체 이미지 개수보다 작다면 앞으로 가져 올 이미지가 남아 있다는 의미이므로 더보기 버튼이 보일 수 있도록 구현한다.