📚 PHP 중급 - 3주차: 게시판 CRUD 구현 (기본) - 03 페이징 처리
2025. 7. 23. 08:21ㆍ프로그램/PHP 중급
📚 PHP 중급 - 3주차: 게시판 CRUD 구현 (기본) - 03 페이징 처리

안녕하세요! 지난 시간에 #PHP #게시판의 #CRUD 기능을 구현해 보았죠. 오늘은 #글 목록을 효율적으로 관리하고 사용자 경험을 향상시키는 데 필수적인 #페이징_처리(Pagination)를 자세히 다뤄볼 거예요. #페이징 처리는 #데이터가 많을 때 웹페이지의 #성능을 최적화하는 중요한 방법입니다.

1. 페이징 처리의 필요성
#게시판에 #글이 수천, 수만 개 쌓인다고 상상해 보세요. 모든 #글을 한 번에 불러와서 웹페이지에 표시한다면 다음과 같은 문제가 발생할 수 있어요.
- 느린 로딩 속도: #데이터베이스에서 많은 #데이터를 가져오고, #웹페이지에 렌더링하는 데 오랜 시간이 걸립니다. 이는 사용자에게 좋지 않은 경험을 줍니다.
- 서버 부하 증가: 한 번에 모든 #데이터를 처리하느라 #서버에 과부하가 걸릴 수 있습니다.
- 사용자 경험 저하: 사용자가 스크롤을 끝없이 내려야 하므로 원하는 #정보를 찾기 어렵고 불편합니다.
#페이징_처리는 이러한 문제를 해결하고, #데이터를 적절한 단위로 나누어 표시함으로써 #웹페이지의 #성능과 #사용자_경험을 동시에 개선합니다.

2. 페이징 처리의 핵심 원리
#페이징_처리의 기본 원리는 다음과 같아요.
- 총 #게시물_수 파악: #데이터베이스에 있는 전체 #게시물 수를 먼저 알아냅니다.
- 한 페이지당 #게시물_수 설정: 한 페이지에 몇 개의 #글을 보여줄지 정합니다. (예: 10개, 20개)
- 총 #페이지_수 계산: 총 #게시물 수를 페이지당 #게시물 수로 나누어 전체 #페이지 수를 계산합니다.
- 현재 #페이지 번호 파악: 사용자가 보고자 하는 #페이지 번호를 #URL 파라미터(예: list.php?page=2) 등을 통해 가져옵니다.
- #LIMIT과 #OFFSET 사용: #SQL 쿼리에서 #LIMIT과 #OFFSET을 사용하여 현재 #페이지에 해당하는 #데이터만 #데이터베이스에서 가져옵니다.

3. 페이징 처리 코드 구현 (list.php 수정)
지난번 list.php 코드에 #페이징_처리 #로직을 추가하여 수정해 볼게요.
PHP
<?php
// list.php
include 'db_config.php'; // 데이터베이스 연결 설정 포함
// 1. 페이징을 위한 설정값 정의
$posts_per_page = 10; // 한 페이지에 보여줄 게시글 수
// 2. 현재 페이지 번호 파악
// URL 쿼리 파라미터 'page'에서 현재 페이지 번호를 가져오며, 없으면 1로 설정
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// 3. OFFSET 계산: 데이터베이스에서 몇 번째 레코드부터 가져올지 결정
// (현재 페이지 번호 - 1) * 페이지당 게시글 수
$offset = ($current_page - 1) * $posts_per_page;
try {
// 4. 총 게시글 수 가져오기
$total_posts_stmt = $conn->query("SELECT COUNT(*) FROM board");
$total_posts = $total_posts_stmt->fetchColumn(); // 전체 게시글 수를 숫자로 가져옴
// 5. 총 페이지 수 계산
// ceil() 함수를 사용하여 소수점 이하를 올림 (예: 20.1개면 21페이지 필요)
$total_pages = ceil($total_posts / $posts_per_page);
// 6. 현재 페이지에 해당하는 게시글 목록 가져오기
// ORDER BY created_at DESC: 최신 글부터 정렬
// LIMIT :offset, :limit: offset부터 limit 개수만큼의 게시글 가져오기
$stmt = $conn->prepare("SELECT id, title, author, created_at FROM board ORDER BY created_at DESC LIMIT :offset, :limit");
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT); // OFFSET 바인딩 (정수형)
$stmt->bindParam(':limit', $posts_per_page, PDO::PARAM_INT); // LIMIT 바인딩 (정수형)
$stmt->execute();
$posts = $stmt->fetchAll(); // 모든 결과를 배열로 가져옴
} catch(PDOException $e) {
echo "오류: " . $e->getMessage();
$posts = []; // 오류 발생 시 게시글 배열을 비워둠
$total_pages = 1; // 오류 시 총 페이지 수를 1로 설정
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시판 글 목록</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { width: 80%; border-collapse: collapse; margin: 20px auto; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.pagination { text-align: center; margin-top: 20px; }
.pagination a { margin: 0 5px; padding: 5px 10px; text-decoration: none; border: 1px solid #ddd; color: #333; }
.pagination a.active, .pagination a:hover { background-color: #007bff; color: white; border-color: #007bff; }
</style>
</head>
<body>
<h1>게시판 글 목록</h1>
<p><a href="write.php">새 글 작성</a></p>
<table>
<thead>
<tr>
<th>ID</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
</tr>
</thead>
<tbody>
<?php if (count($posts) > 0): ?>
<?php foreach ($posts as $post): ?>
<tr>
<td><?php echo htmlspecialchars($post['id']); ?></td>
<td><a href="view.php?id=<?php echo htmlspecialchars($post['id']); ?>"><?php echo htmlspecialchars($post['title']); ?></a></td>
<td><?php echo htmlspecialchars($post['author']); ?></td>
<td><?php echo htmlspecialchars($post['created_at']); ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4">아직 작성된 글이 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<div class="pagination">
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<a href="?page=<?php echo $i; ?>" class="<?php echo ($i == $current_page) ? 'active' : ''; ?>"><?php echo $i; ?></a>
<?php endfor; ?>
</div>
</body>
</html>

4. 코드 설명 및 보충
- $posts_per_page: 한 페이지에 몇 개의 #게시글을 보여줄지 설정하는 변수예요. 필요에 따라 조절할 수 있습니다.
- $current_page: 현재 사용자가 요청한 #페이지 번호를 $_GET['page']로 가져와요. #URL에 ?page=숫자 형태로 전달됩니다. (int)로 형 변환하여 #보안을 강화하고 비정상적인 값을 막습니다.
- $offset: #SQL의 #LIMIT 절에서 사용할 시작 위치를 계산해요. 예를 들어 2페이지를 요청하고 페이지당 10개의 #글을 보여준다면, (2-1) * 10 = 10이 되어 11번째 #글부터 10개를 가져오게 됩니다.
- COUNT(*): #데이터베이스 쿼리에서 #게시판의 #총_글_수(#total_posts)를 가져와요. #PDO의 fetchColumn()을 사용하면 첫 번째 컬럼의 값(여기서는 COUNT(*))을 바로 얻을 수 있습니다.
- ceil(): #PHP의 ceil() 함수는 소수점 이하를 무조건 올림 해요. 예를 들어 총 #글이 21개이고 페이지당 10개라면, 21 / 10 = 2.1이 되고, ceil(2.1)은 3이 되어 총 3페이지가 필요하다고 정확히 계산됩니다.
- LIMIT :offset, :limit: #SQL에서 특정 범위의 #데이터만 가져오는 구문입니다. OFFSET으로 시작 위치를, LIMIT으로 가져올 개수를 지정해요. #준비된_구문(Prepared Statement)을 사용해 #SQL_인젝션 공격을 방지하고 PDO::PARAM_INT로 정수형임을 명시하여 #보안을 강화합니다.
- #페이지네이션_링크: for 루프를 사용하여 1부터 $total_pages까지의 #페이지_링크를 동적으로 생성합니다. 현재 #페이지에는 active 클래스를 추가하여 시각적으로 강조했어요.
이 #페이징_처리 #로직을 적용하면 #게시판이 아무리 커져도 #웹페이지는 빠르고 효율적으로 #글 목록을 보여줄 수 있을 거예요. 다음 주차에는 #게시판의 다른 유용한 기능들을 추가해 보겠습니다!
게시판 CRUD 페이징 PHP 데이터베이스 SQL 성능 사용자경험
'프로그램 > PHP 중급' 카테고리의 다른 글
| 📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 02 $_FILES 슈퍼 글로벌 변수 (0) | 2025.07.25 |
|---|---|
| 📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 01 HTML 폼을 이용한 파일 업로드 (0) | 2025.07.24 |
| 📚 PHP 중급 - 3주차: 게시판 CRUD 구현 (기본) - 02 글 목록, 글 작성, 글 보기, 글 수정, 글 삭제 (0) | 2025.07.22 |
| 📚 PHP 중급 - 3주차: 게시판 CRUD 구현 (기본) - 01 게시판 테이블 설계 (0) | 2025.07.20 |
| 📚 PHP 중급 - 2주차: 회원가입 및 로그인 시스템 구현 - 04 로그아웃 및 세션 관리 (0) | 2025.07.19 |