페이징 기능 구현

문한성·2022년 11월 24일
1

공부

목록 보기
1/28
post-thumbnail

Spring Data Jpa와 querydsl용한 페이징

프로젝트를 하는중에 가게추가 요청글을 받는 기능을 구현하였다.
관리자가 요청글을 보는 화면에서 페이징이 필요해서 기존에 했던 더보기버튼을 이용한 페이징보단
기본적인 페이징을 해보지 못하여서 이번기회에 공부하는 마음으로 구현했다.

DTO


먼저 숫자와 다음페이지 이전페이지를 만들기위한 DTO를 만듭니다.
PageRequest객체는 page시작을 0부터 시작하기 때문에 pageNum에 1을 더하고 시작한다.
-view에 뿌릴 값을 만드는것

package ywphsm.ourneighbor.domain.dto;

import lombok.Data;

@Data
public class PageMakeDTO {

    /* 시작 페이지 */
    private int startPage;

    /* 끝 페이지 */
    private int endPage;

    /* 이전 페이지, 다음 페이지 존재유무 */
    private boolean prev, next;

    /*전체 게시물 수*/
    private long total;

    //현재 페이지
    private int pageNum;

    /* 생성자 */
    public PageMakeDTO(int pageNum, int amount, long total) {

        this.total = total;


        /* 마지막 페이지 */
        pageNum++;
        this.pageNum = pageNum;
        this.endPage = (int) (Math.ceil(pageNum / 10.0)) * 10;
        /* 시작 페이지 */
        this.startPage = this.endPage - 9;

        /* 전체 마지막 페이지 */
        int realEnd = (int) (Math.ceil(total * 1.0 / amount));

        /* 전체 마지막 페이지(realend)가 화면에 보이는 마지막페이지(endPage)보다 작은 경우, 보이는 페이지(endPage) 값 조정 */
        if (realEnd < this.endPage) {
            this.endPage = realEnd;
        }

        /* 시작 페이지(startPage)값이 1보다 큰 경우 true */
        this.prev = this.startPage > 1;

        /* 마지막 페이지(endPage)값이 1보다 큰 경우 true */
        this.next = this.endPage < realEnd;

    }
}

Repository

querydsl를 이용하여 가게추가 요청글을 전부 DTO로 바꾸어 List로 받아온다.
count쿼리를 따로 받아 PageImpl에 담아 넘겨준다.
querydsl을 통해서 sql문을 짜는것이 아닌 자바 코드를 짜는 기분을 느낄수 있다.

package ywphsm.ourneighbor.repository.requestAddStore;

import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import ywphsm.ourneighbor.domain.dto.QRequestAddStoreDTO_Detail;
import ywphsm.ourneighbor.domain.dto.RequestAddStoreDTO;
import ywphsm.ourneighbor.domain.member.QMember;
import ywphsm.ourneighbor.domain.store.QRequestAddStore;

import java.util.List;

@RequiredArgsConstructor
public class RequestAddStoreRepositoryImpl implements RequestAddStoreRepositoryCustom{

    private final JPAQueryFactory queryFactory;

    @Override
    public Page<RequestAddStoreDTO.Detail> requestAddStorePage(Pageable pageable) {
        List<RequestAddStoreDTO.Detail> results = queryFactory
                .select(new QRequestAddStoreDTO_Detail(
                        QRequestAddStore.requestAddStore.id,
                        QRequestAddStore.requestAddStore.name,
                        QRequestAddStore.requestAddStore.address.zipcode,
                        QRequestAddStore.requestAddStore.address.roadAddr,
                        QRequestAddStore.requestAddStore.address.numberAddr,
                        QMember.member.email
                ))
                .from(QRequestAddStore.requestAddStore)
                .leftJoin(QRequestAddStore.requestAddStore.member, QMember.member)
                .orderBy(QRequestAddStore.requestAddStore.createdDate.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        int total = queryFactory
                .selectFrom(QRequestAddStore.requestAddStore)
                .fetch().size();

        return new PageImpl<>(results, pageable, total);
    }
}

Service

repository에서 가게추가 요청글 DTO list를 받아서 컨트롤러에 전달한다.

public Page<RequestAddStoreDTO.Detail> pagingRequestAddStore(int page) {
        PageRequest pageRequest = PageRequest.of(page, 10);
        Page<RequestAddStoreDTO.Detail> results = requestAddStoreRepository.requestAddStorePage(pageRequest);
        log.info("reviewMemberDTO={}", results);
        return results;

    }

Controller

html에서 파라미터로 page번호를 받아서 해당 페이지를 view에 뿌려준다.
첫 접근시 파라미터가 없으므로 null체크를 해준다.
가게추가 요청 list와 DTO를 model에 담아서 넘겨준다.

@GetMapping("/admin/request-add-store-list")
    public String requestAddStoreList(Model model, Integer page) {
        if (page == null) {
            page = 0;
        } else {
            page--;
        }
        Page<RequestAddStoreDTO.Detail> requestAddStoreDTOS = requestAddStoreService.pagingRequestAddStore(page);
        List<RequestAddStoreDTO.Detail> content = requestAddStoreDTOS.getContent();

        PageMakeDTO pageMakeDTO = new PageMakeDTO(page, 10, requestAddStoreDTOS.getTotalElements());

        model.addAttribute("count", requestAddStoreDTOS.getTotalElements());
        model.addAttribute("request", content);
        model.addAttribute("pageMake", pageMakeDTO);

        return "store/request_add_store_list";
    }

Html

DTO에 prev,next값이 true면 이전,다음페이지버튼이 나오고, false면 나오지 않는다.
startPage부터 endPage까지의 숫자들이 나열되고 해당 숫자를 클릭하면 파라미터로 숫자가 담겨서 controller로 전달된다.

<div class="pageInfo_wrap">
      <div class="pageInfo_area">
        <ul id="pageInfo" class="pageInfo">
          <!-- 이전페이지 버튼 -->
          <th:block th:if="${pageMake.isPrev()}">
            <li class="pageInfo_btn previous">
              <a th:href="|/admin/request-add-store-list?page=${pageMake.pageNum - 1}|" id="previous">Previous</a>
            </li>
          </th:block>

          <!-- 페이지 번호 -->
          <th:block th:each="num : ${#numbers.sequence(pageMake.getStartPage(),pageMake.getEndPage())}">
            <li class="pageInfo_btn">
              <a th:href="|/admin/request-add-store-list?page=${num}|" th:text="${num}" class="page-number"></a>
            </li>
          </th:block>

          <!-- 다음페이지 버튼 -->
          <th:block th:if="${pageMake.isNext()}">
            <li class="pageInfo_btn next">
              <a th:href="|/admin/request-add-store-list?page=${pageMake.pageNum + 1}|" id="next">Next</a>
            </li>
          </th:block>
        </ul>
      </div>
    </div>

CSS

html이 list형식으로 나열되는걸 css로 정렬해준다.

.pageInfo{
      list-style : none;
      display: inline-block;
      margin: 50px 0 0 100px;
    }
    .pageInfo li{
      float: left;
      font-size: 20px;
      margin-left: 18px;
      padding: 7px;
      font-weight: 500;
    }
    a:link {color:black; text-decoration: none;}
    a:visited {color:black; text-decoration: none;}
    a:hover {color:black; text-decoration: underline;}

profile
기록하고 공유하려고 노력하는 DevOps 엔지니어

0개의 댓글