본문 바로가기

개발/Node.js

nodejs를 이용한 페이징 처리 (DB 연결) Step3

Step1 : https://datobi.tistory.com/28?category=948178

Step2 : https://datobi.tistory.com/29?category=948178

 

Step3에서는 DB를 연결 했다. 코드가 많이 바뀌진 않았다.

수정한 코드는 db/index.js와 view/index.ejs, routes/index.js 이다 

 

그리고 DB 데이터 몇가지를 추가하였다. DB는 postgre를 사용하였다.

더보기를 누르면 해당 데이터를 추가 할 수 있다.

 

더보기


CREATE TABLE "student" (
"idx" INTEGER NOT NULL,
"name" VARCHAR NOT NULL,
"age" INTEGER NOT NULL,
"grade" CHAR(1) NOT NULL,
"address" VARCHAR NOT NULL,
PRIMARY KEY ("idx")
);


INSERT INTO "student" VALUES (1, '김똘똘', 10, 'B', '서울시');
INSERT INTO "student" VALUES (2, '이장님', 9, 'A', '서울시');
INSERT INTO "student" VALUES (3, '홍길동', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (4, '윤뽀뽀', 10, 'A', '제주시');
INSERT INTO "student" VALUES (5, '박박', 9, 'D', '경기도');
INSERT INTO "student" VALUES (6, '김소희', 8, 'A', '서울시');
INSERT INTO "student" VALUES (7, '지선생', 10, 'A', '경기도');
INSERT INTO "student" VALUES (8, '장기하', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (9, '원빈', 10, 'D', '서울시');
INSERT INTO "student" VALUES (10, '조인성', 10, 'C', '경기도');
INSERT INTO "student" VALUES (11, '산다라박', 11, 'B', '서울시');
INSERT INTO "student" VALUES (12, '김갑생', 9, 'C', '서울시');
INSERT INTO "student" VALUES (13, '김장', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (14, '조여정', 11, 'D', '서울시');
INSERT INTO "student" VALUES (15, '이솜', 10, 'C', '경기도');
INSERT INTO "student" VALUES (16, '김별', 9, 'A', '경기도');
INSERT INTO "student" VALUES (17, '박막례', 10, 'B', '경기도');
INSERT INTO "student" VALUES (18, '조갑생', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (19, '이하루', 10, 'D', '서울시');
INSERT INTO "student" VALUES (20, '김영광', 10, 'C', '경기도');
INSERT INTO "student" VALUES (21, '이택조', 11, 'B', '서울시');
INSERT INTO "student" VALUES (22, '최준', 9, 'C', '서울시');
INSERT INTO "student" VALUES (23, '김해준', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (24, '이은지', 11, 'D', '서울시');
INSERT INTO "student" VALUES (25, '안영미', 10, 'C', '경기도');
INSERT INTO "student" VALUES (26, '김소희', 8, 'A', '서울시');
INSERT INTO "student" VALUES (27, '지선생', 10, 'A', '경기도');
INSERT INTO "student" VALUES (28, '장기하', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (29, '원빈', 10, 'D', '서울시');
INSERT INTO "student" VALUES (30, '조인성', 10, 'C', '경기도');
INSERT INTO "student" VALUES (31, '산다라박', 11, 'B', '서울시');
INSERT INTO "student" VALUES (32, '김갑생', 9, 'C', '서울시');
INSERT INTO "student" VALUES (33, '김장', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (34, '조여정', 11, 'D', '서울시');
INSERT INTO "student" VALUES (35, '이솜', 10, 'C', '경기도');
INSERT INTO "student" VALUES (36, '김별', 9, 'A', '경기도');
INSERT INTO "student" VALUES (37, '박막례', 10, 'B', '경기도');
INSERT INTO "student" VALUES (38, '조갑생', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (39, '이하루', 10, 'D', '서울시');
INSERT INTO "student" VALUES (40, '김영광', 10, 'C', '경기도');
INSERT INTO "student" VALUES (41, '이택조', 11, 'B', '서울시');
INSERT INTO "student" VALUES (42, '최준', 9, 'C', '서울시');
INSERT INTO "student" VALUES (43, '김해준', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (44, '이은지', 11, 'D', '서울시');
INSERT INTO "student" VALUES (45, '안영미', 10, 'C', '경기도');
INSERT INTO "student" VALUES (46, '김소희', 8, 'A', '서울시');
INSERT INTO "student" VALUES (47, '지선생', 10, 'A', '경기도');
INSERT INTO "student" VALUES (48, '장기하', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (49, '원빈', 10, 'D', '서울시');
INSERT INTO "student" VALUES (50, '조인성', 10, 'C', '경기도');
INSERT INTO "student" VALUES (51, '산다라박', 11, 'B', '서울시');
INSERT INTO "student" VALUES (52, '김갑생', 9, 'C', '서울시');
INSERT INTO "student" VALUES (53, '김장', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (54, '조여정', 11, 'D', '서울시');
INSERT INTO "student" VALUES (55, '이솜', 10, 'C', '경기도');
INSERT INTO "student" VALUES (56, '김별', 9, 'A', '경기도');
INSERT INTO "student" VALUES (57, '박막례', 10, 'B', '경기도');
INSERT INTO "student" VALUES (58, '조갑생', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (59, '이하루', 10, 'D', '서울시');
INSERT INTO "student" VALUES (60, '김영광', 10, 'C', '경기도');
INSERT INTO "student" VALUES (61, '이택조', 11, 'B', '서울시');
INSERT INTO "student" VALUES (62, '최준', 9, 'C', '서울시');
INSERT INTO "student" VALUES (63, '김해준', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (64, '이은지', 11, 'D', '서울시');
INSERT INTO "student" VALUES (65, '안영미', 10, 'C', '경기도');
INSERT INTO "student" VALUES (66, '김소희', 8, 'A', '서울시');
INSERT INTO "student" VALUES (67, '지선생', 10, 'A', '경기도');
INSERT INTO "student" VALUES (68, '장기하', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (69, '원빈', 10, 'D', '서울시');
INSERT INTO "student" VALUES (70, '조인성', 10, 'C', '경기도');
INSERT INTO "student" VALUES (71, '산다라박', 11, 'B', '서울시');
INSERT INTO "student" VALUES (72, '김갑생', 9, 'C', '서울시');
INSERT INTO "student" VALUES (73, '김장', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (74, '조여정', 11, 'D', '서울시');
INSERT INTO "student" VALUES (75, '이솜', 10, 'C', '경기도');
INSERT INTO "student" VALUES (76, '김별', 9, 'A', '경기도');
INSERT INTO "student" VALUES (77, '박막례', 10, 'B', '경기도');
INSERT INTO "student" VALUES (78, '조갑생', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (79, '이하루', 10, 'D', '서울시');
INSERT INTO "student" VALUES (80, '김영광', 10, 'C', '경기도');
INSERT INTO "student" VALUES (81, '이택조', 11, 'B', '서울시');
INSERT INTO "student" VALUES (82, '최준', 9, 'C', '서울시');
INSERT INTO "student" VALUES (83, '김해준', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (84, '이은지', 11, 'D', '서울시');
INSERT INTO "student" VALUES (85, '안영미', 10, 'C', '경기도');
INSERT INTO "student" VALUES (86, '김소희', 8, 'A', '서울시');
INSERT INTO "student" VALUES (87, '지선생', 10, 'A', '경기도');
INSERT INTO "student" VALUES (88, '장기하', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (89, '원빈', 10, 'D', '서울시');
INSERT INTO "student" VALUES (90, '조인성', 10, 'C', '경기도');
INSERT INTO "student" VALUES (91, '산다라박', 11, 'B', '서울시');
INSERT INTO "student" VALUES (92, '김갑생', 9, 'C', '서울시');
INSERT INTO "student" VALUES (93, '김장', 12, 'A', '삼척시');
INSERT INTO "student" VALUES (94, '조여정', 11, 'D', '서울시');
INSERT INTO "student" VALUES (95, '이솜', 10, 'C', '경기도');
INSERT INTO "student" VALUES (96, '김별', 9, 'A', '경기도');
INSERT INTO "student" VALUES (97, '박막례', 10, 'B', '경기도');
INSERT INTO "student" VALUES (98, '조갑생', 11, 'C', '강릉시');
INSERT INTO "student" VALUES (99, '이하루', 10, 'D', '서울시');
INSERT INTO "student" VALUES (100, '김영광', 10, 'C', '경기도');
INSERT INTO "student" VALUES (101, '김국희', 11, 'B', '경기도');

 

view/index.ejs

<!DOCTYPE html>
<html>
  <head>
    <title>index</title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
  </head>
  <body>
    <h3>
      currentPage : <%= paging.currentPage %><br>
      currentSet : <%= paging.currentSet %><br>
      startPage : <%= paging.startPage %><br>
      endPage : <%= paging.endPage %><br>
    </h3>
    <div class="container">
      <table class="table">
        <thead>
          <tr>
            <th scope="col">idx</th>
            <th scope="col">name</th>
            <th scope="col">age</th>
            <th scope="col">grade</th>
          </tr>
        </thead>
        <tbody>
          <!-- 게시글-->
          <% for(let i=0; i<data.length; i++){ %> 
            <tr>
              <th scope="row"><%= data[i].idx%></th>
              <td><%= data[i].name %></td>
              <td><%= data[i].age %></td>
              <td><%= data[i].grade %></td>
          </tr>
          <% } %>  
        </tbody>
      </table>
      
      <nav aria-label="Page navigation example">
        <ul class="pagination justify-content-center">
          <!-- 뒤로가기 버튼 (1보다 작으면 무조건 뒤로가기 생성)-->
          <% if(paging.currentSet > 1) {%>
          <li class="page-item">
            <a class="page-link" href="/?page=<%=paging.startPage-1%>" aria-label="Previous">
              <span aria-hidden="true">&laquo;</span>
            </a>
          </li>
          <% } %>

          <!--숫자 버튼-->
          <% for(let i=paging.startPage; i<=paging.endPage; i++){
            if(i > paging.totalPage){break;}  
            
            if(i == paging.currentPage){ %>
              <tr>
                <td>    
                  <li class="page-item active"><a class="page-link" href='/?page=<%=i%>'><%=i%></a></li>
                </td>
              </tr>
            <% } else {%> 
            <tr>
              <td>    
                <li class="page-item"><a class="page-link" href='/?page=<%=i%>'><%=i%></a></li>
              </td>
            </tr>
          <% } 
          } %> 

          <!--앞으로 버튼 (현재 세트가 토탈보다 작으면 생성)-->
          <% if(paging.currentSet < paging.totalSet) {%>
            <li class="page-item">
              <a class="page-link" href="/?page=<%=paging.endPage+1%>" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
              </a>
            </li>
          <% } %>

        </ul>
      </nav>
    </div>
  </body>
</html>

 

- 게시글들을 data란 변수에 담아

[
  { idx: 1, name: '김똘똘', age: 10, grade: 'B' },
  { idx: 2, name: '이장님', age: 9, grade: 'A' },
  { idx: 3, name: '홍길동\r\n', age: 11, grade: 'C' },

...

]

이러한 JSON 형태로 가져와서 반복문을 통해 각각의 컬럼에 맞는 값들을 넣어주었다.

 

- 숫자버튼을 출력할 때, 현재 페이지 번호 인지 판별하여, 현재 페이지번호일 때 active라는 클래스를 달아주었다.

 

 

routes/index.js

var express = require('express');
var router = express.Router();
let db = require('../db/index')

router.get('/', async function(req, res, next) {
  let currentPage = req.query.page; // 현재 보여지는 페이지
  if(!currentPage) currentPage = 1 // page 파라미터 값을 넘겨주지 않을 시, 1페이지로 설정
  let pagePerSize = 10 // 한 페이지에 보여질 게시물 수
  let pageBtnSize = 5 // 보여질 페이지 버튼의 개수 
  
  // 총 게시물 수
  let totalPageCnt = await db.getStudentCount()
  if (totalPageCnt < 0){
    totalPageCnt = 1
  }
  
  let totalPage = Math.ceil(totalPageCnt / pagePerSize) // 전체 페이지 수
  let totalSet = Math.ceil(totalPage / pageBtnSize) // 전체 세트 수
  let currentSet = Math.ceil(currentPage / pageBtnSize) // 현재 버튼 세트 번호 
  let startPage = ((currentSet-1) * pageBtnSize) + 1  // 시작 페이지 번호
  let endPage = (startPage + pageBtnSize) - 1 // 끝 페이지 번호
  let startPost = '' // db

  // DB에 들어갈 offset 설정
  if(currentPage < 0){
    startPost = 0
  } else {
    startPost = (currentPage - 1) * pagePerSize // 시작 게시글 번호
  }

  let data = await db.getStudentInfo(startPost, pagePerSize)
  // console.log(data)

  let paging = { // ejs로 전송하기위해 객체화
    'startPage' : startPage,
    'endPage' : endPage,
    'currentSet' : currentSet,
    'totalSet' : totalSet,
    'totalPage' : totalPage,
    'currentPage' : currentPage,
  }

  res.render('index', {'paging': paging, 'data': data})
});

module.exports = router;

 

- totalPageCnt를 db에서 가져왔다. 0인경우 에러처리를 해주었다.

 

 

페이지에 맞는 게시물을 db에서 가져올 때, 

SELECT *
FROM STUDENT
LIMIT 10 /* 가져올 게시물 수 */
OFFSET 0 /*가져올 게시물의 첫번째 idx*/

이러한 쿼리를 쓴다. limt은 가져올 게시물의 갯수이고 offset은 어디부터 가져올건지를 설정해 줄 수 있다.

그래서 LIMIT은 pagePerSize로, OFFSET은 startPost로 설정해두었다.

 

 

db/index.js

let db = require('./config')

// student 개수를 반환하는 메서드
async function getStudentCount() {
    let query = 'SELECT COUNT(*) FROM student' 
    let result = await db.query(query) 
    let data = result.rows[0].count

    return data
}

// student row들을 리턴하는 메서드
async function getStudentInfo(startPost, pagePerSize) {
    let query = `
    SELECT *
    FROM STUDENT
    LIMIT ${pagePerSize}
    OFFSET ${startPost}
    `
    let data = [] // 배열 만듦
    let { rows } = await db.query(query)

    for(let row of rows){ // 배열안에 각각의 객체 삽입
        let node = {
            'idx' : row['idx'],
            'name' : row['name'],
            'age' : row['age'],
            'grade' : row['grade'],
        }
        data.push(node)
    }
    
    return data
}


module.exports = {
    getStudentCount,
    getStudentInfo
}

 

db/index.js에는 Student의 총 갯수를 가져오는 getStudentCount()와 

해당 페이지의 게시물을 가져오는 getStudentInfo를 선언하였다. 

getStudentInfo 메서드에서 data에 JSON 형태로 값들을 담아 반환하였다.

 

 

결과화면

 

 

 

 

 

 

 

소스코드 : https://github.com/datoybi/blog-posting/tree/main/paging-step3

 

GitHub - datoybi/blog-posting: code examples posted on my blog

code examples posted on my blog. Contribute to datoybi/blog-posting development by creating an account on GitHub.

github.com