저번시간엔 마이페이지에서 개인정보 수정 및 비밀번호 변경 기능을 구현했었습니다.
"이번시간에는 Pagination 기능을 구현해보겠습니다"
<목차>
1. Pagination 이란?
2. Pagination 에 사용될 값 계산
3. 구현
< 1. Pagination 이란? >
게시글이 10개라면 한페이지에 게시글을 전부 보일수도 있을테지만...
게시글이 100개라면? 여러 페이지로 분할 시켜서 10개씩 보여줘야 할것입니다.
이런 페이지 분할을 할수있는 기능을 페이지네이션 이라고 합니다.
예시로 구글에 검색을 한 후 가장 하단으로 내리면 pagination 기능을 보실수 있습니다.
<2. Pagination 에 사용될 값 계산 >
게시글을 페이지네이션 하려면 총 4개의 변수가 필요합니다.
- 전체 게시글수
- 전체 페이지수
- 페이지당 보이는 게시글 수(필자의 경우엔 8개로 설정)
- 시작점 인덱스
-전체 게시글수
: mysqli_num_rows 를 쓰면 쿼리로 가져온 결과에 몇개의 행이 있는지 출력이 되게 됩니다.
저희는 mysqli_num_rows($result) 함수를 써서 전체 게시글이 몇개인지를 가져왔습니다.
- 전체 페이지수
:전체 페이지수는 (전체 게시글수) / (페이지당 보이는 게시글 수) = 전체 페이지 수 가 됩니다.
※소수점은 올림처리
예를들어 SQL 로 가지고온 행의 수가 21개고 , 페이지당 8개씩 보여주고싶으면
21 / 8 = 2.625 >> 올림처리 하면 3이 됩니다.
즉 21개의 게시글일때는 최대 페이지가 3까지 있다는 말입니다.
- 페이지당 보여질 게시글 수
: 이건 사용자의 선택입니다.
저는 이번경우엔 한페이지당 게시글이 최대 8개까지 보여지도록 만들어 봤습니다.
-시작점 인덱스
: 만약 8개씩 가져올건데 sql 쿼리문으로 가져온 결과값이 20개입니다. 그렇다면...
0~7 번째 게시글이 [1]번 페이지를 차지.
8~15 번째 게시글이 [2]번 페이지를 차지.
16~20 번째 게시글이 [3]번 페이지를 차지.
이런식으로 페이지마다 시작하는 인덱스가 0 , 8 ,16 으로 다르게 됩니다.
이럴경우엔 계산식은 "시작점 인덱스 = 페이지당보이는게시글수*(현재페이지-1) " 이 됩니다.
실제로 현재페이지가 1이라고 가정을하고 계산을 해보면 1페이지에선 시작점 인덱스의 값이 0.
2페이지에선 8 , 3페이지 에선 16 이 됨을 알수 있습니다.
< 3. 구현 >
결과는 보시다시피 화면 하단에 pagination 기능이 구현되었습니다.
page.php |
<위에서 언급한 변수 구현>
1. sql 쿼리문으로 검색을 통해 가져온 게시글의 결과를 $result 함수에다가 넣어줍니다.
2. 페이지 관련 변수를 만들어줍니다. (올림처리는 ceil 함수를 사용했습니다.)
3. 이후 위에서 쓰였던 sql 쿼리문 마지막에 limit 을 추가하여 $result 에서 시작점 인덱스부터 8개씩 가져와줍니다.
(밑에 구현코드에서 시작점 인덱스는 $limit_num 현재페이지는 $current_page)
<구현 코드>
$sql = "select * from postinfo where postdate BETWEEN '".$startdate."' AND '".$enddate."' and ".$stand." like '%".$search."%' order by ".$sort; //검색기능을 포함한 sql 구문생성
$result = mysqli_query($dbcon,$sql);
$totalnum=mysqli_num_rows($result); //전체 게시글수
$totalpage=ceil($totalnum/8);//전체 페이지 수
$page_viewed=8; //페이지당 게시글수
$limit_num=$page_viewed*($current_page-1); //limit a,b 에서 a 역할을 할 변수설정
$sql = "select * from postinfo where postdate BETWEEN '".$startdate."' AND '".$enddate."' and ".$stand." like '%".$search."%' order by ".$sort." limit ".$limit_num.",".$page_viewed;
$result = mysqli_query($dbcon,$sql);
$row=mysqli_fetch_array($result);
이제 페이지의 하이퍼링크를 만들어줍니다.
<?php //페이지네이션 하이퍼링크를 만드는 코드
for($startpage=1;$startpage<=$totalpage;$startpage=$startpage+1){
?><a href="#" onclick="submitForm(<?php echo $startpage; ?>);">[<?php echo $startpage; ?>]</a><?php
} ?>
하이퍼 링크를 클릭하면 이벤트핸들러가 발동하여 현재페이지에 form 요청을 하게됩니다.
이 form 요청은 제가 검색을 했을때 제출하는 form 요청에다가 pageidx 값을 추가 한 것입니다.
이렇게되면 하이퍼링크를 클릭해서 다른페이지로 리다이렉션 되도 저희가 검색한 범위 내에서 결과가 나오게됩니다.
왜냐하면 하이퍼링크를 클릭하여 리다이렉션 되도 전에 있던 POST 요청을 그대로 pageidx만 추가해서 보내줄테니깐요!
onlcik="submitForm()" 이라는 함수는 밑에 코드대로 정의하여 <head> 태그에 넣어주었습니다.
<function submitForm()>
<script>
function submitForm(startpage) {
var form = document.createElement('form');
form.method = 'POST';
form.action = '/test.php';
var inputStand = document.createElement('input');
inputStand.type = 'hidden';
inputStand.name = 'stand';
inputStand.value = '<?php echo $stand; ?>';
form.appendChild(inputStand);
var inputStartDate = document.createElement('input');
inputStartDate.type = 'hidden';
inputStartDate.name = 'startdate';
inputStartDate.value = '<?php echo $startdate; ?>';
form.appendChild(inputStartDate);
var inputEndDate = document.createElement('input');
inputEndDate.type = 'hidden';
inputEndDate.name = 'enddate';
inputEndDate.value = '<?php echo $enddate; ?>';
form.appendChild(inputEndDate);
var inputSearch = document.createElement('input');
inputSearch.type = 'hidden';
inputSearch.name = 'search';
inputSearch.value = '<?php echo $search; ?>';
form.appendChild(inputSearch);
var inputPageidx = document.createElement('input');
inputPageidx.type = 'hidden';
inputPageidx.name = 'pageidx';
inputPageidx.value = startpage;
form.appendChild(inputPageidx);
var inputSort = document.createElement('input');
inputSort.type = 'hidden';
inputSort.name = 'sort';
inputSort.value = '<?php echo $sort; ?>';
form.appendChild(inputSort);
document.body.appendChild(form);
form.submit();
}
</script>
전체코드
<?php
include 'dbcon.php';
session_start();
if(!($_SESSION['loggedin'])){ // 점프해서 page.php 로 오는것을 방지.
header('Location: /index.php');
exit();
}
$current_page= isset($_POST['pageidx']) ? $_POST['pageidx']:1; //기본적으로 페이지는 1페이지로 시작
// 검색 조건과 검색어 가져오기
$stand = isset($_POST['stand']) ? $_POST['stand'] : 'post_name'; // 기본적으로 제목으로 검색
$search = isset($_POST['search']) ? $_POST['search'] : '';
$startdate = isset($_POST['startdate']) ? $_POST['startdate'] : '2024-01-01';// 날짜 설정 안할시 자동으로 min 값
$enddate = isset($_POST['enddate']) ? $_POST['enddate'] : '2025-12-29';// 날짜 설정 안할시 자동으로 max 값
$sort = isset($_POST['sort']) ? $_POST['sort'] : 1;// 기본적으로 제목으로 정렬
?>
<!DOCTYPE html>
<html lang="en">
<head>
<style> /*a 태그 스타일시트*/
a:link {
color : black;
text-decoration-line: none;
}
a:hover{
text-decoration-line: underline;
}
</style>
<script>
function submitForm(startpage) {
var form = document.createElement('form');
form.method = 'POST';
form.action = '/test.php';
var inputStand = document.createElement('input');
inputStand.type = 'hidden';
inputStand.name = 'stand';
inputStand.value = '<?php echo $stand; ?>';
form.appendChild(inputStand);
var inputStartDate = document.createElement('input');
inputStartDate.type = 'hidden';
inputStartDate.name = 'startdate';
inputStartDate.value = '<?php echo $startdate; ?>';
form.appendChild(inputStartDate);
var inputEndDate = document.createElement('input');
inputEndDate.type = 'hidden';
inputEndDate.name = 'enddate';
inputEndDate.value = '<?php echo $enddate; ?>';
form.appendChild(inputEndDate);
var inputSearch = document.createElement('input');
inputSearch.type = 'hidden';
inputSearch.name = 'search';
inputSearch.value = '<?php echo $search; ?>';
form.appendChild(inputSearch);
var inputPageidx = document.createElement('input');
inputPageidx.type = 'hidden';
inputPageidx.name = 'pageidx';
inputPageidx.value = startpage;
form.appendChild(inputPageidx);
var inputSort = document.createElement('input');
inputSort.type = 'hidden';
inputSort.name = 'sort';
inputSort.value = '<?php echo $sort; ?>';
form.appendChild(inputSort);
document.body.appendChild(form);
form.submit();
}
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="holygrail.css" rel="stylesheet" type = "text/css">
<link href="div.css" rel="stylesheet" type = "text/css">
<title>감자대학교 메인페이지</title>
</head>
<body>
<div class="wrapper">
<header>
<div style="text-align:center ; color: #B86824; font-size:20px ; font-weight:semi bold;transform:translate(0px,10px)">GamJa National University</div>
<div style="text-align: right ;font-weight:bold;transform:translate(-20px,-10px)"><a style="color:purple" href = "mypage.php">마이페이지</a></div>
</header>
<nav>
카테고리
<form action="logout.php" method="POST"><div style="position:absolute;bottom:0px;left:10px;width:20%">
<input style="height:30px" type = submit class="submit-btn" value="Logout"></form></div>
<form action="post_write.php" method="POST"><div style="position:absolute;top:170px;left:10px;width:20%">
<input style="height:40px" type = submit class="submit-btn" value="게시글 작성"></form></div>
</nav>
<main>
<div>
<form style="text-align:left" method="POST">
<select name="stand">
<option value="post_name">제목</option>
<option value="postwriter">작성자</option>
<option value="contents">내용</option>
</select>
<input style="border: solid 1.5px black;width:25%;transform:translate(0px,-1px)" type="text" name ="search" placeholder = "검색" maxlength='30' >
<input style="border: solid 1.5px black" type = submit value="검색">
<select name="sort">
<option value="post_name">제목순</option>
<option value="postdate desc">최신순</option>
<option value="postdate asc">오래된순</option>
<option value="liked">좋아요순</option>
</select>
<input name="startdate" type="datetime-local" max="2025-12-29" min="2024-01-01" value="2024-01-01T00:00">
<input name="enddate" type="datetime-local" max="2025-12-29" min="2024-01-01" value="2025-12-30T00:00">
</form>
</div>
<br><hr style="border: solid 1px #696969;height:1px;width:100%">
<table style=border-collapse:collapse;width:100%;>
<tr style="border-bottom:2px solid black;">
<td width="50%">글 제목</td><td width="30%">작성자</td><td style="text-align:center" width="5%" >liked</td><td style="text-align:center" width="5%" >view</td><td style="text-align:center" width="20%" >날짜</td>
</tr>
<?php // postinfo 테이블 에서 게시글 내용 8개 가져오는 코드 시작.
$sql = "select * from postinfo where postdate BETWEEN '".$startdate."' AND '".$enddate."' and ".$stand." like '%".$search."%' order by ".$sort; //검색기능을 포함한 sql 구문생성
$result = mysqli_query($dbcon,$sql);
$totalnum=mysqli_num_rows($result); //전체 게시글수
$totalpage=ceil($totalnum/8);//전체 페이지 수
$page_viewed=8; //페이지당 게시글수
$limit_num=$page_viewed*($current_page-1); //limit a,b 에서 a 역할을 할 변수설정
$sql = "select * from postinfo where postdate BETWEEN '".$startdate."' AND '".$enddate."' and ".$stand." like '%".$search."%' order by ".$sort." limit ".$limit_num.",".$page_viewed;
$result = mysqli_query($dbcon,$sql);
$row=mysqli_fetch_array($result);
for($count=1;isset($row);$count=$count+1){ //게시글을 보여주는 코드 ?>
<tr height="45px" style="border-bottom: 1px solid #696969">
<td width="50%"><a href="post_read.php?idx=<?php echo $row['idx']; ?>"><?php echo $row['post_name']; ?></a></td>
<td width="20%"><?php echo $row['postwriter']; ?></td>
<td style="text-align:center" width="5%" height="5%" ><?php echo $row['liked']; ?></td>
<td style="text-align:center" width="5%" height="5%" ><?php echo $row['view']; ?></td>
<td style="text-align:center" width="20%" height="5%" ><?php echo $row['postdate']; ?></td>
</tr><?php $row=mysqli_fetch_array($result);} ?> </table>
<?php //페이지네이션 하이퍼링크를 만드는 코드
for($startpage=1;$startpage<=$totalpage;$startpage=$startpage+1){
?><a href="#" onclick="submitForm(<?php echo $startpage; ?>);">[<?php echo $startpage; ?>]</a><?php
} ?>
</main>
</body>
</html>
+보완할 점
솔직히 form 요청으로 전에 있던 POST 요청을 그대로 보내게되면 페이지를 바꿀때마다 서버에 부하가 심하게 올것같다는 생각이 들었습니다. 또한 POST 요청이 많이 오가다 보니 어디선가는 SQL 인젝션이나 Dom based XSS 취약점이 발생할수도 있겠다 라는 생각에 보안적인 측면에서 되게 무식하게 코딩을 하였다 생각이 되었습니다.
제가 자바스크립트를 능숙하게 사용할 줄 알았더라면 좀 더 고급지게 Pagination 기능을 구현할 수 있을거 같았습니다.
그래서 앞으로는 틈틈이 자바스크립트 공부를 충분히 하여 올해가 끝나기 전까지 이 페이지를 다시 보강할 예정입니다.
곧 시작할 모바일 앱 개발 또한 신경써야 하지만.. 올해 말 까지는 꼭 자바스크립트를 이용해서 좀 더 효율적인 페이지를 만드는 것을 목표로 잡게되었습니다.
그리고 CSS 도 지금처럼 무식하게 tranform:translate 사용하는것이 아닌 , flex 같은것들을 사용하여 보다 더 체계적으로 할 수 있도록 해야겠다고 다짐하게 되었습니다.
'웹개발(PHP-Mysql)' 카테고리의 다른 글
[웹 개발-PHP] 조회수 & 좋아요 기능 구현 (1) | 2024.07.21 |
---|---|
[웹 개발-PHP] 파일 업로드 기능 구현(+확장자 체크) (0) | 2024.07.20 |
[웹 개발] 마이페이지(비밀번호 변경 & 개인정보 수정) (0) | 2024.07.16 |
[웹 개발] 검색 및 정렬 기능 구현 (0) | 2024.07.16 |
[웹 개발] CRUD(생성,읽기,수정,삭제) 페이지 만들기 (0) | 2024.07.15 |