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값이 이 전체 이미지 개수보다 작다면 앞으로 가져 올 이미지가 남아 있다는 의미이므로 더보기 버튼이 보일 수 있도록 구현한다.