앞서 작성한 글에서 게시글 작성하기를 마쳤으므로 이제 작성한 게시글을 DB에서 불러와 나열해서 보여줘야 한다.

↓ <게시글 작성 구현> 정리글 

https://sungeun97.tistory.com/36?category=1127827 

 

 

먼저 백엔드 파트를 구현하기 위해 server>routes>template.js로 이동하여 다음 코드를 작성한다. 

// template.js

...

router.post("/getTemplates", (req, res) => {
    
    Template.find()
	    .exec((err, templates) => {
        	if(err) {return res.status(400).json({ success: false, err })}
                res.status(200).json({ success: true, templates })
	    })
});

...

 

 

그런 다음 클라이언트 파트를 위해 LandingPage.js로 이동해서 다음 코드를 작성한다. 

주석 처리되어 있는 Filter와 Search 부분에 대한 내용은 다음 게시글에서 정리할 예정이다. 

// LandingPage.js

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Col, Card, Row } from 'antd';


const { Meta } = Card;

function LandingPage() {
    const [Templates, setTemplates] = useState([])
    
    useEffect(() => {
        axios.post('/api/template/getTemplates')
            .then(response => {
                if(response.data.success) {
                    setTemplates(response.data.templates)
                } else {
                    alert('Failed to fetch template data')
                }
            })
    }, [])

    
    const renderCards = Templates.map((template, index) => {
        return <Col lg={6} md={8} xs={24} key={index}>
            <Card 
                hoverable={true}
                cover
            > 
                <Meta
                    title={template.title}
                    description={template.writer.name}
                />
            </Card>
        </Col>
    })
    
    return (
        <div style ={{ width: '75%', margin:'3rem auto' }}>
            <div style ={{ textAlign: 'center' }}>
                <h2>모든 속지</h2>
            </div>

            {/* Filter */}
            
            
            {/* Search */}
            
            
            {Templates.length === 0 ?
                <div style ={{ display: 'flex', height:'300px', justifyContent: 'center', alignItems: 'center' }}>
                    <h2>No post yet...</h2>
                </div> :
                <div>
                    <Row gutter={[16, 16]}>
                        {renderCards}
                    </Row>
                </div>
            }
            
            <br /><br />
            
            <div style ={{ display: 'flex', justifyContent: 'center' }}>
            	<button onClick={onLoadMore}>Load More</button>
            </div>
            
        </div>
    )
}

export default LandingPage

 

 

실행시켜보면 작성한 게시글이 Landing Page에 뜨는 것을 확인할 수 있다. 

LandingPage - 게시글 목록

 

 

이제 게시글 작성 시에 등록했던 이미지를 띄우기 위해 client > src > components > utils 폴더에 ImageSlider.js 파일을 생성해서 다음 코드를 작성한다. 

// ImageSlider.js


import React from 'react'
import { Carousel } from 'antd';

function ImageSlider(props) {
    return (
        <div>
            <Carousel autoplay>
                {props.images.map((image, index) => (
                    <div key={index}>
                        <img style={{ width:'100%', height:'150px' }} 
                        src={`http://localhost:5000/${image}`} alt="templateImage" />
                    </div>
                ))}
            </Carousel>
        </div>
    )
}

export default ImageSlider

 

 

그리고 다시 LandingPage.js로 돌아와 다음 코드를 추가한다. 

// LandingPage.js


...

import ImageSlider from '../../utils/ImageSlider';

...

function LandingPage() {
   ...
    
    const renderCards = Templates.map((template, index) => {
        return <Col lg={6} md={8} xs={24} key={index}>
            <Card 
                hoverable={true}
                cover = { <ImageSlider images={template.images} /> }
            > 
                <Meta
                    title={template.title}
                    description={template.writer.name}
                />
            </Card>
        </Col>
    })
    
    ...
}

export default LandingPage

 

 

다시 실행시켜보면 게시글 목록에 이미지 슬라이더가 추가되어 등록한 이미지도 같이 뜨는 것을 확인할 수 있다. 

LandingPage - ImageSlider

 

 

 

마지막으로 이번 포스팅에서는 Load More 버튼 기능으로 마무리하고, 다음 포스팅에서 게시글 필터링 및 검색 기능에 대해 정리할 것이다. 

현재 Load More 버튼 버튼이 보이긴 하지만 onClick 함수를 따로 작성하지 않아 버튼을 클릭해도 아무 기능이 없다. 

 

여기서 하고자하는 것은, Load More 버튼을 눌렀을 때 더 많은 템플릿 게시글들을 불러오는 것이다. 

 

이때, MongoDB의 skip()과 limit() 메소드를 사용한다.

skip()출력할 데이터의 시작 부분을 설정하고, limit() 출력할 데이터의 갯수를 제한한다.

 

skip()과 limit()에 대한 구체적 예를 들어 설명하자면, skip(0)일 경우에는 데이터를 처음부터 가져오게 되고, 만약 이미 데이터를 n개 가져온 상태라면 skip(n)해서 그 뒤의 데이터부터 가져오면 되는 것이다. 

그리고 Load More 버튼을 눌렀을 때 데이터를 4개씩만 가져오고 싶다면 limit(4)로 해서 한 번에 가져올 수 있는 데이터의 양을 4개로 제한할 수 있다. 

 

그렇기 때문에 skip()과 limit()은 Pagination 할 때 많이 사용된다.

 

 

본격적으로 Load More 버튼의 기능을 구현하기 위해 LandingPage.js에 다음 코드를 작성하면 된다.

// LandingPage.js


...

function LandingPage() {
   ...
   
   const [Skip, setSkip] = useState(0) //데이터를 처음부터 가져와야 하기 때문에 initial value = 0
   const [Limit, setLimit] = useState(4) //데이터를 4개씩 가져오기 위해 Limit = 4
   const [PostSize, setPostSize] = useState(0)
   
   ...
   
   useEffect(() => {
        const variables = {
            skip: Skip,
            limit: Limit,
        }

        getTemplates(variables)
    }, [])


    //useEffect에 있던 axios 부분이 거의 동일하게 onLoadMore에서도 사용되기 때문에
    //getTemplates에 axios를 가져와 여러 곳에서 사용할 수 있게 만듦
    const getTemplates = (variables) => { 
        axios.post('/api/template/getTemplates', variables)
            .then(response => {
                if(response.data.success) {
                    setTemplates([...Templates, ...response.data.templates])
                    setPostSize(response.data.postSize)
                } else {
                    alert('Failed to fetch template data')
                }
            })
    }
   
   const onLoadMore = () => {
        let skip = Skip + Limit;

        const variables = {
            skip: skip,
            limit: Limit
        }

        getTemplates(variables)
        setSkip(skip)
    }
   
   ...
   
   return (
        <div style ={{ width: '75%', margin:'3rem auto' }}>
            <div style ={{ textAlign: 'center' }}>
                <h2>모든 속지</h2>
            </div>

            ...
            
            {PostSize >= Limit &&
                <div style ={{ display: 'flex', justifyContent: 'center' }}>
                    <button onClick={onLoadMore}>Load More</button>
                </div>
            }
            
        </div>
    )
   
}

export default LandingPage

 

 

그리고나서 MongoDB에서 게시글을 불러오는 부분을 수정하기 위해 server > routes > template.js 로 이동해서 다음 코드를 작성한다. 

// template.js

...

router.post("/getTemplates", (req, res) => {
    let order = req.body.order ? req.body.order : "desc";
    let sortBy = req.body.sortBy ? req.body.sortBy : -1;
    let limit = req.body.limit ? parseInt(req.body.limit) : 20;
    let skip = parseInt(req.body.skip);
    
    Template.find()
    	.populate("writer")
        .sort([[sortBy, order]])
        .skip(skip)
        .limit(limit)
	.exec((err, templates) => {
	    if(err) {return res.status(400).json({ success: false, err })}
	    res.status(200).json({ success: true, templates, postSize: templates.length })
	})
});

...

 

 

 

이제 게시글을 추가로 작성하여 5개 이상으로 만든 다음, Load More 버튼을 눌러보면 처음에 4개만 보여지던 게시글에서 추가로 더 불러진 것을 확인할 수 있다.

LandingPage - Load More

 

 

+ Recent posts