본문 바로가기

내일배움캠프_개발일지/내일배움캠프_미니프로젝트

내일배움캠프_미니프로젝트 5일차, 게시판을 만들어보자.

 

첫 사진은 팀 소개 메인 페이지의 하단에 구현된 게시판이며, 아래는 게시판에서 게시글 하나를 클릭할 시 열리는 모달창이다. 해당 모달창에는 세부 내용과 수정/삭제/나가기 버튼이 구비되어 있다.

 

 

어떻게 게시판과 페이징을 동시에 잘 어울러지게 구현할 수 있을까.

우선 가정을 하자. 가장 처음, 최초로 이 페이지가 세상에 선보일 때는, 1페이지의 게시글들을 보여주자고.

그리고, 이 게시판은 한 페이지 당 10개의 게시글을 보여줄거야.

내림차순으로 정렬된 게시글들, 즉 가장 최근에 게시된 게시글이 가장 앞 페이지의 가장 위에 등장할거야.

 

 

1. 우선 처음 페이지가 로드될 때, 임의로 페이지의 숫자로 '1' 을 주자.

$(document).ready(function(){
    let page = 1;

    if (localStorage.getItem("page")) {
        page = localStorage.getItem("page"); //string
    }
    board(page, true)
});

필자는 임의로 page 에 1을 주었다. 참고로 localStorage 는 당장은 고려하지 않아도 되는 코드이다.

중요한 것은 board(page, true) 이다.

 

function board (page, loadOrPage) {
    $.ajax({
        type: "POST",
        url: "/getNotices",
        data: {page_give: page},
        success: function (response) {
            const notice_list = response["result"];
            const howManylist = response["pageLength"]
            let pageCount = 0;
            if(howManylist % 10 !== 0) {
                pageCount = Math.floor(howManylist / 10) + 1
            } else {
                pageCount = Math.floor(howManylist / 10)
            }
            let temp = ``;

            for (let i=0; i<notice_list.length; i++) {
                const notice_id = notice_list[i]["notice_id"];
                const title = notice_list[i]["title"];
                const writer = notice_list[i]["writer"];
                const date = notice_list[i]["date"]

                const showDate = date.split(' ')
                const inputDate = `${showDate[3]} ${showDate[2]} ${showDate[1]} ${showDate[4]}`

                temp += `
                        <tr class="notice_tr" onclick="detailOfNotice(${notice_id}, this)">
                            <th scope="row">${notice_id}</th>
                            <td>${title}</td>
                            <td>${writer}</td>
                            <td>${inputDate}</td>
                        </tr>
                        `;
            }
            document.getElementById("table_record").innerHTML = temp;

            if (loadOrPage === true) {
                makePageBtn(pageCount, page);  //총 몇 페이지?
            }

        }
    })
}

위는 실질적으로 백엔드에 현재 페이지(=1) 를 전달하고, 이 페이지에 보여줘야 할 10개의 DB 레코드를 응답받는 구문이다.

추가로, "pageLength" 라는 변수도 reseponse 로 받고 있는데, 해당 변수에는 총 레코드의 갯수가 담겨 있다.

아니, 1페이지니까 1페이지에서 보여줄 레코드만 있으면 되지, 전체 데이터 수는 왜 필요한 건데?

          * 참고로 필자는 한 페이지 당 레코드를 10개 씩만 보여줄 예정이다.

 

 

// paging 버튼 구현
function makePageBtn (pageCount, page) {
    let page_temp = `<li class="page-item">
                        <button type="button" class="btn btn-outline-primary" onclick="changePage(1, this)" style="background-color: white;">
                            <span aria-hidden="true">&laquo;</span>
                        </button>
                    </li>`;
    for (let i=0; i<pageCount; i++) {
        if(i+1 === Number(page)) {
            page_temp += `
                    <li class="page-item">
                        <button disabled=true type="button" class="btn btn-outline-primary" onclick="changePage(${i+1}, this)" style="background-color: white;">
                            ${i+1}
                        </a>
                    </li>
                    `;
        } else {
            page_temp += `
                    <li class="page-item">
                        <button type="button" class="btn btn-outline-primary" onclick="changePage(${i+1}, this)" style="background-color: white;">
                            ${i+1}
                        </a>
                    </li>
                    `;
        }
    }
    page_temp += `
                <li class="page-item" style="margin-left: 1px;">
                    <button type="button" class="btn btn-outline-primary" onclick="changePage(${pageCount}, this)" style="background-color: white;">
                        <span aria-hidden="true">&raquo;</span>
                    </button>
                </li>
                `;
    document.getElementById("inputNotice").innerHTML = page_temp;
}

 

클릭한 페이지에 맞는 DB 레코드 뿐 아니라 전체 레코드의 갯수까지 들고 온 이유는, 페이징 번호를 만들어야 하기 때문이다.

 

우선 board() 구문에서 전체 레코드 갯수를 10으로 나눠 본다. 만약 10으로 딱 나눠 떨어진다면 결과값 만큼 페이지를 만들면 그만일 뿐.

허나, 예를 들어서 전체 레코드의 갯수가 31개라면? 그렇다면 페이지를 3.1개 만들어야 한다는 말인데, 그럴 수는 없지 않은가.

그래서 math.floor 로 소숫점을 날리고, 1을 더해주는 것이다. 레코드 갯수가 31개라면, 페이지가 4개 있어야 하기 때문이다.

결과적으로 본다면 소숫점 뒷자리를 올림 처리 한다고 볼 수 있다.

어쨌든 그렇게 몇 개의 페이지 버튼을 만들까 정했다면, makePageBtn() 함수에서 페이지 버튼 갯수만큼 for문을 돌려서 html 태그를 생성해준다.

 

마지막으로, 그렇다면 백엔드 측에서는 정확히 어떻게 해당 페이지에 맞는 10개의 레코드들을 가지고 오는가 확인해보자.

 

@app.route('/getNotices', methods=["POST"])
def home_get():
    page = int(request.form['page_give'])
    count = 10
    start = (page - 1) * count
    limit = start + 10

    notices_list = list(db.notice_board.find({}, {'_id': False}).sort('notice_id', -1))
    notices_list_inpage = notices_list[start:limit]  #  0  :  10(9)  //   10  :  20(19)
    page_length = len(notices_list)

    return jsonify({'msg': 'home 팀 응원 게시판 가져오기 성공!', 'result': notices_list_inpage, 'pageLength': page_length})

우선 전체적인 형태만 보자면, 입력받은 페이지 숫자에 맞춰서 해당 레코드 만큼 잘라내고, 페이지 버튼 갯수를 만들기 위해서 전체 레코드의 갯수가 담긴 변수와 함께 response 되고 있다.

구체적으로 list 를 어떻게 잘라내고 있는지 살펴보자.

 

우선, page 값을 가져와서 int 로 파싱하고는, -1 을 한 다음 10 만큼 곱해준다. 참고로 count = 10 은 페이지 당 레코드 갯수를 의미한다.

예를 들어서 page 가 1이 들어왔다면, 1에서 -1 을 한 다음 10을 곱해도 값은 0이다.

뭔 소리냐, 즉 페이지가 1이면, 레코드 list 의 0번째부터 보여줘야 한다는 의미이다.

그렇다면, 0번째 부터 몇 번째 까지 보여줄 것인가? 0번째 부터 10개를 보여줄 것이다. 따라서 limit 는 0 + 10, 즉 10이다.

 

그런 다음 db에서 list 를 불러 오는데, sort() 메소드를 사용하여 notice_id (고유 아이디값. 등록된 시기가 최근일 수록 높다)

기준으로 내림차순 정렬을 한다. 이렇게 되면 가장 최근에 등록된 레코드가 가장 앞쪽의 인덱스에 위치하게 된다.

이 상태에서 list[start : limit] 를 해준다. 즉 페이지가 1이라면 [0:10] 이 되는데,

이는 '0 < 10 만큼의 인덱스의 list 를 잘라내서 리턴한다' 를 의미한다. 즉, 0번째 부터 9번째 인덱스까지, 총 10개의 리스트가 반환된다.

 

예를 들어서 상상해보자. page에 2가 들어왔다고 생각해보자.

그렇다면, start는 (2-1) * 10 이니까 10이 된다. 그리고 limit 는 10 + 10 이 될테니 20이 된다.

이는 [10 : 20] 을 반영되고, 이는 즉슨 '10 < 20 만큼의 인덱스를 반환한다' 라는 의미가 된다. 즉, 1페이지 바로 다음에 10개를 보여주는 2 페이지가 완성이 된다.

 

 

 

 

마지막으로, html 태그로 생성된 페이지들을 클릭했을 시 어떤 이벤트를 발생시킬 것인지 정리해보자.

function changePage (pageNum, myself) {
    const checkBtn = document.getElementById("inputNotice").querySelectorAll("button")
    for (let i in checkBtn) {
        checkBtn[i].disabled = false;
    }
    myself.disabled = true;

    localStorage.setItem('page', pageNum)
    board(pageNum, false);
}

보시는 바와 같이, changePage 는 makePageBtn() 함수에서 페이지 버튼이 생성됨과 동시에 인자로서 (해당 페이지, 그리고 thsi)

를 받고 있다.

changePage 에서는 우선 클릭된 해당 페이지 버튼을 비활성화 시키고 있으며, 나머지 버튼들은 활성화 시키고 있다.

왜? 예를 들어서 2페이지가 선택된 상태인데, 굳이 또 2페이지를 누르게 할 필요가 없기 때문이다. 페이지를 누를 때마다 값을 들고 올텐데,

그럴 바에야 그냥 비활성화 시켜서 클릭을 막아버리는 것이 편하다.

 

다음으로, 처음 페이지가 로딩되며 DB 레코드 리스트와 레코드 전체 갯수를 response 해왔던 ajax 가 속한 board() 함수를 호출한다.

단, 이때 2번째 매개변수로서 false 값을 준다.

board() 함수를 보면 아시겠지만, 두 번째 인자가 false 일 경우 페이징을 구현하는 함수를 호출하지 않는다.

당연하다. 처음 html 이 로드된 직후일 때야 페이징 버튼도 함께 구현해야 하니 true 값을 주면서 연이어 발동하지만,

이미 페이지 버튼은 로드 된 상태이니, 목록만 새롭게 바꿔주면 되는 것이다.