React와 Node.js를 사용한 웹 애플리케이션 개발

React와 Node.js를 사용한 웹 애플리케이션(게시판) 개발 강좌(27)

atomicdev 2024. 9. 22. 21:50
728x90

페이지네이션 기능 구현: 대량의 게시글 처리를 위한 서버 및 클라이언트 페이지네이션

이번 강의에서는 대량의 게시글을 효율적으로 처리하기 위한 페이지네이션(pagination) 기능을 구현합니다. 페이지네이션을 사용하면 모든 데이터를 한 번에 로드하지 않고, 페이지 단위로 나누어 클라이언트에 전달할 수 있습니다. 이는 성능을 최적화하고, 사용자 경험을 개선하는 데 매우 유용합니다.

페이지네이션 기능


1. 페이지네이션 개요

페이지네이션은 대량의 데이터를 페이지 단위로 나누어 서버클라이언트에서 각각 처리하는 기법입니다.

1.1 페이지네이션 흐름

  1. 클라이언트 요청: 사용자는 페이지 버튼을 클릭하여 원하는 페이지의 데이터를 요청합니다.
  2. 서버에서 데이터 처리: 서버는 해당 페이지에 해당하는 데이터만 클라이언트에 전달합니다.
  3. 결과 출력: 클라이언트는 서버에서 받은 데이터를 화면에 렌더링합니다.

2. 서버에서 페이지네이션 처리

2.1 API에서 페이지네이션 구현

서버에서는 클라이언트로부터 **페이지 번호(page)**와 **페이지당 항목 수(limit)**를 받아 해당 페이지의 데이터를 반환합니다.

// server.js
app.get('/api/posts', (req, res) => {
  const page = parseInt(req.query.page) || 1; // 기본값은 1
  const limit = parseInt(req.query.limit) || 10; // 기본값은 10
  const offset = (page - 1) * limit; // 페이지의 시작점 계산

  const query = `
    SELECT * FROM posts 
    LIMIT ? OFFSET ?
  `;
  db.query(query, [limit, offset], (err, results) => {
    if (err) {
      return res.status(500).send('Error fetching posts');
    }
    res.json(results);
  });
});

2.2 총 게시글 수 반환

클라이언트에서 총 페이지 수를 계산하기 위해 전체 게시글의 수를 알아야 합니다. 이를 위해 게시글의 총 개수를 반환하는 API를 추가할 수 있습니다.

app.get('/api/posts/count', (req, res) => {
  const query = 'SELECT COUNT(*) AS total FROM posts';
  db.query(query, (err, results) => {
    if (err) {
      return res.status(500).send('Error fetching post count');
    }
    res.json({ total: results[0].total });
  });
});

3. 클라이언트에서 페이지네이션 처리

3.1 페이지네이션 컴포넌트 구현

React에서 페이지네이션 버튼을 구현하여 사용자가 페이지를 선택할 수 있도록 합니다.

// src/components/Pagination.js
import React from 'react';

function Pagination({ totalPosts, postsPerPage, currentPage, paginate }) {
  const pageNumbers = [];

  for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
    pageNumbers.push(i);
  }

  return (
    <nav>
      <ul className="pagination">
        {pageNumbers.map(number => (
          <li key={number} className="page-item">
            <a onClick={() => paginate(number)} href="#" className="page-link">
              {number}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}

export default Pagination;

3.2 페이지네이션을 적용한 게시글 목록

페이지네이션을 적용하여 게시글을 페이지 단위로 불러오고 렌더링하는 코드를 구현합니다.

// src/components/PostList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Pagination from './Pagination';

function PostList() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage] = useState(10);
  const [totalPosts, setTotalPosts] = useState(0);

  useEffect(() => {
    const fetchPosts = async () => {
      setLoading(true);

      // 게시글 총 개수 가져오기
      const countRes = await axios.get('/api/posts/count');
      setTotalPosts(countRes.data.total);

      // 현재 페이지의 게시글 가져오기
      const res = await axios.get(`/api/posts?page=${currentPage}&limit=${postsPerPage}`);
      setPosts(res.data);
      setLoading(false);
    };

    fetchPosts();
  }, [currentPage]);

  const paginate = (pageNumber) => setCurrentPage(pageNumber);

  return (
    <div>
      <h1>게시글 목록</h1>
      {loading ? (
        <p>로딩 중...</p>
      ) : (
        <ul>
          {posts.map(post => (
            <li key={post.id}>
              <h3>{post.title}</h3>
              <p>{post.content}</p>
            </li>
          ))}
        </ul>
      )}
      <Pagination
        totalPosts={totalPosts}
        postsPerPage={postsPerPage}
        currentPage={currentPage}
        paginate={paginate}
      />
    </div>
  );
}

export default PostList;

3.3 코드 설명

  • currentPage: 현재 페이지 번호를 저장하고, 페이지 버튼을 클릭할 때 이 값을 변경합니다.
  • postsPerPage: 한 페이지에 표시할 게시글 수를 정의합니다.
  • fetchPosts: 페이지가 변경될 때마다 서버에서 해당 페이지의 게시글을 가져옵니다.
  • Pagination 컴포넌트: 페이지 번호 버튼을 생성하고, 각 페이지로 이동할 수 있는 기능을 제공합니다.

4. 페이지네이션 기능의 UX 최적화

4.1 이전 및 다음 페이지 버튼 추가

이전 페이지 및 다음 페이지로 이동할 수 있는 버튼을 추가하여 사용자 경험을 향상시킬 수 있습니다.

// Pagination.js
return (
  <nav>
    <ul className="pagination">
      <li className="page-item">
        <a onClick={() => paginate(currentPage - 1)} href="#" className="page-link">
          이전
        </a>
      </li>
      {pageNumbers.map(number => (
        <li key={number} className={`page-item ${currentPage === number ? 'active' : ''}`}>
          <a onClick={() => paginate(number)} href="#" className="page-link">
            {number}
          </a>
        </li>
      ))}
      <li className="page-item">
        <a onClick={() => paginate(currentPage + 1)} href="#" className="page-link">
          다음
        </a>
      </li>
    </ul>
  </nav>
);

4.2 페이지네이션 상태 유지

사용자가 페이지를 변경할 때 URL 쿼리 파라미터로 페이지 상태를 유지할 수 있습니다. 이를 통해 사용자가 브라우저의 뒤로 가기 버튼을 눌러도 이전에 보던 페이지를 유지할 수 있습니다.

import { useHistory } from 'react-router-dom';

const history = useHistory();

const paginate = (pageNumber) => {
  setCurrentPage(pageNumber);
  history.push(`?page=${pageNumber}`);
};

5. 전체 페이지네이션 흐름 요약

  1. 클라이언트 요청: 사용자가 페이지 버튼을 클릭하면, 해당 페이지에 해당하는 게시글을 서버로 요청합니다.
  2. 서버에서 데이터 반환: 서버는 데이터베이스에서 해당 페이지의 게시글을 가져와 클라이언트에 전달합니다.
  3. 결과 출력: 클라이언트는 받은 게시글 데이터를 화면에 표시하며, 페이지네이션 컴포넌트를 통해 페이지를 이동할 수 있습니다.

결론

이번 강의에서는 대량의 게시글을 효율적으로 처리하기 위한 페이지네이션 기능을 구현하는 방법을 배웠습니다. 서버에서 데이터베이스 쿼리로 데이터 페이지별로 가져오는 방식클라이언트에서 페이지네이션 컴포넌트를 사용해 페이지를 이동하는 방식을 이해하고, 성능 최적화를 위한 UX 개선 방법도 다루었습니다.

728x90