게시글 페이징

UlBaMe·2023년 2월 10일
0

게시판 만들기 9 - 페이징(onethejay님)

링크를 올려두긴 했는데 복잡해 보이고 이해도 잘 안되고 JPA에서 지원해주는 라이브러리 쓰는 거 같아서 그냥 생짜로 했다.
당연히 똑똑하고 많이 배운 사람들이 만들어준 걸 잘 갖다 쓰는게 성능이든 안정성이든 뭐든 좋을텐데 검색해서 찾는 건 걸리는 시간이 복불복이고 신뢰성도 상대적으로 떨어지고 공식문서는 영어로 된 거 보다보면 너무 힘들때가 많고... 정말 이럴 때 영어 잘해야겠다고 느낀다. 몇몇 프레임워크/라이브러리 번역해주시는 분들께 항상 감사해야...


일단 쭉 해놓고 github의 diff 부분을 보면서 정리해본다.

몇몇 공통 부분 수정은 큰 의미 없는 것 같고(api/board.js->api/post.js 라든가 post에서 쓰는 axios api 분리해서 쓰는 거라든가) 결국 중요한 건 페이징 혹은 페이지네이션 부분 같은데(pagination이 더 맞는 어휘 같다),

현재 페이지, 페이징 사이즈(아래 나타나는 갯수), 리스트 사이즈(화면에 나타나는 게시물 갯수)를 지정해두고 관련 변수들의 크기에 따라 처음으로 불러올 게시물/마지막으로 불러올 게시물을 정해 호출했다.

function fetchBoard(paging) {
  const { currentPage, pagingSize, listSize } = paging;
  const firstPostIndex = (currentPage-1) * pagingSize + 1;
  const lastPostIndex = firstPostIndex+listSize-1;
  return postAxios.get(`?firstPostIndex=${firstPostIndex}&lastPostIndex=${lastPostIndex}`);
}

좀 더 깔끔하게 쓰는 방법도 있을 것 같은데 Axios를 더 공부해야 할까? 어쨌든 잘 날아간다.

@GetMapping(path="/post")
	public List<BoardDto> getBoardList(@RequestParam int firstPostIndex, @RequestParam int lastPostIndex)
			throws Exception{
		return boardService.getBoardList(firstPostIndex, lastPostIndex);
	}

적당히 @RequestParam을 쓴 레스트컨트롤러 안의 메서드로 받아서 넘긴다.
서비스->서비스구현체->매퍼->mybatis xml sql문으로 넘어가면,

<select id="selectBoardList" parameterType="int" resultType="site.bfor0312.bfor0312_BE.board.dto.BoardDto">
		<![CDATA[
			SELECT
				*
			FROM
				(
				SELECT
					row_number() over(order by board_idx desc)
                    	as ROWNUM,
					board_idx,
					title,
					creator_id,
					hit_cnt,
					created_datetime
				FROM t_board
				WHERE deleted_yn = 'N'
				) t_table_filtered
			WHERE
				ROWNUM BETWEEN #{firstPostIndex} and #{lastPostIndex}
		]]>
	</select>

대강 이런 식으로 받아온다. 대충 구현은 했는데 성능 등에서 문제가 없는 쿼리문인지는 잘 모르겠다. board_idx는 pk라 인덱싱은 되어있을 테니 괜찮을 것 같긴 한데...
그리고 오라클과는 다르게 MySQL은 인라인 뷰 테이블에 Alias가 없으면 에러가 떠서 넣어줬다.
특정 파라미터를 받는 것도 param1 param2 이런 식으로도 받을 수 있는 거 같은데 그래도 뭐 코드 보고 바로 이해가 되는게 편하니까.

그리고 페이지의 총 갯수가 있어야 하기 때문에 한 번 더 호출을 해서 t_board에서 삭제되지 않은 게시물의 row 수를 받아오게 해놨다. 이 부분이 조금 불만인데 사용자 입장에서 한 번의 입력을 했는데 백엔드에 두 번 호출한다는게 불편한데 어떻게 바꾸면 좋을지 잘 모르겠다.

게시물 목록 보여주는 뷰 인스턴스 PostList.vue

<template>
  <div class="board-list">
    <table class="w3-table-all" summary="연습용 자유 게시판">
      <thead>
        <tr>
          <th v-for="header, idx in tableHeader" :key="idx" scope="col" v-bind:id="header" > {{header}} </th>
        </tr>
      </thead>
      <!-- <tfoot>
      </tfoot> -->
      <tbody>
      <tr v-for="(row, idx) in postList" :key="idx" scope="row">
          <td v-for="(key, idx) in row" :key="idx">
            <div v-if="key==row.title">
              <router-link :to="`/board/post/${row.boardIdx}`">
                {{ key }}
              </router-link>
            </div>
            <div v-else>
              {{ key }}
            </div>
          </td>
      </tr>
      </tbody>
    </table>
    <div 
      class="pagination w3-bar w3-padding-16 w3-small"
    >
      <span class="pg">
        <p class="first w3-button w3-border" @click="changePage(1)">{{'<<'}}</p>
        <p
          class="prev w3-button w3-border"
          @click="changePage(paging.currentPage-1)"
          disabled=true
        >{{'<'}}</p>
        <template v-for="(number, index) in pageNumbers" :key="index" >
            <template v-if="paging.currentPage===number">
                <strong class="w3-button w3-border w3-green">{{ number }}</strong>
            </template>
            <template v-else>
                <p class="w3-button w3-border" @click="changePage(number)">{{ number }}</p>
            </template>
        </template>
        <p class="next w3-button w3-border" @click="changePage(paging.currentPage+1)">{{'>'}}</p>
        <p class="last w3-button w3-border" @click="changePage(paging.lastPagingNumber)">{{'>>'}}</p>
      </span>
    </div>
  </div>
</template>

<script>
import {fetchBoard, fetchBoardSize } from '@/api/post';
import _ from 'lodash';

export default {
  data() {
    return {
      tableHeader: {},
      postList: {},
      paging: {
        currentPage: 1,
        pagingSize: 10,
        listSize: 10,
        lastPagingNumber: undefined,
      },
      pageNumbers: [],
    }
  },
  mounted() {
    this.GetList();
    this.initPaging();
  },
  methods: {
    async GetList() {
        const {data:rawPostList} = await fetchBoard(this.paging);

        const filteredPostList = rawPostList.map(
          post => _.pickBy(post, (value) => { return !_.isNil(value) })
        );
        this.postList = filteredPostList;

        this.initHeader(Object.keys(filteredPostList[0]));
      },
      changePage(number){
        if(number<1) return;
        if(number>this.paging.lastPagingNumber) number = this.paging.lastPagingNumber;
        this.paging.currentPage=number;
        this.GetList();
        this.initPaging();
      },
      initHeader(tableHeader) {
        this.tableHeader=tableHeader;
      },
      async initPaging(){
        const {data} = await fetchBoardSize();

        let boardSize = data.boardSize;
        this.paging.lastPagingNumber = parseInt(boardSize/this.paging.pagingSize)+1;

        let startPageNumber = parseInt(((this.paging.currentPage-1)/this.paging.pagingSize))*this.paging.pagingSize+1;
        
        this.pageNumbers = Array(this.paging.pagingSize).fill(0).map(
          (zero, index) => zero+index+startPageNumber
        ).filter(number => number <= this.paging.lastPagingNumber);
      }
  },
}

</script>

changePage가 조금 지저분한데... 입력 받는 것도 그렇고 안에서 GetList나 initPaging 호출하는 것도 좀 그렇고... watch로 currentPage 변화 감시해서 불러주는 게 나을 것 같은데 나중에 해야겠다.
-> 해놨음. immediate 줘서 mounted()도 없애버림.

  watch: {
    paging: {
       deep: true,
       immediate: true,
      handler() {
            this.GetList();
            this.initPaging();
      },
    },
  },

** 지금 소스를 보니 PostList 컴포넌트에 Paging이 들어가 있어서 확인해서 PostListPage.vue 쪽으로 분리 해야할듯.


좀 읽어봐야 할 링크

https://memostack.tistory.com/244
https://dev-gorany.tistory.com/16
https://devlog-wjdrbs96.tistory.com/182
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html

0개의 댓글