본문 바로가기
React

React로 가성비 Pagination 만들기 (with. react-js-pagination)

by 흑시바 2023. 2. 12.

토이 프로젝트 중 React + Spring 조합으로 개발하는 과정에서 Pagination이 필요해서 구현하다가

직접 만든 거 말고 기존에 있는 라이브러리를 사용해 보는 건 어떨까🤔 라는 생각이 들어서 찾아보게 되었다.

그렇게 react-js-pagination라는 라이브러리를 활용하여 Pagination을 구현하였고 관련 내용을 공유하고자 한다.

 

🐕 0. 준비

React 프로젝트 Terminal에 하기 명령을 입력하여 react-js-pagination 라이브러리를 받는다.

 

npm i react-js-pagination

 

🐕 1. Spring 서버 페이징 처리

Controller

@GetMapping("/api/study")
public ResponseEntity<Pagination> getPublicStudyList(@PageableDefault Pageable pageable) {
    log.debug("[getPublicStudyList] call ");

    Pagination<List<StudyDto>> response = studyService.getPublicStudyList(pageable);

    return new ResponseEntity<>(response, HttpStatus.OK);
}

 

1. 컨트롤러에 Pageable 파라미터를 받는다. 

 

Pageable를 파라미터로 받게 되면 클라이언트에서 조회에 필요한 만큼을 쿼리 파라미터로 요청할 수 있게 된다.

( size = 목록 개수, page = 페이지 번호, sort = 정렬 기준 )

@PageableDefault 어노테이션이 있으면, 기본으로 page = 0, size=10가 설정된다.

Service

@Transactional(readOnly = true)
public Pagination<List<StudyDto>> getPublicStudyList(Pageable pageable) {
    Page<Study> studyPage = studyRepository.findAllBySecretAndIsUse(false, IsUse.Y, pageable);
        
    List<StudyDto> data = studyPage.getContent().stream()
            .map(StudyDto::entityToDto)
            .collect(Collectors.toList());
                
    return new Pagination<>(studyPage, data);
}

Repository

Page<Study> findAllBySecretAndIsUse(Boolean secret, IsUse isUse, Pageable pageable);

 

2.  Repository로 Pageable을 전달하여 페이징 처리된 개수만큼의 데이터를 가져온다.

 

JpaRepository가 상속된 Repository에서 메서드 파라미터로 Pageable을 전달하면 자동으로 페이징 처리를 해준다.

이때 반드시 반환 타입을 Page <Entity>로 해야 한다.

Dto

@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Pagination<T> {

    private PageInfo page;
    private T data;

    public Pagination(Page page, T data) {
        this.page = new PageInfo(page);
        this.data = data;
    }

    @Getter
    @ToString
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class PageInfo {

        private long totalElements;
        private int totalPages;
        private int size;
        private int number;
        private boolean first;
        private boolean last;

        public PageInfo(Page page) {
            this.totalElements = page.getTotalElements();
            this.totalPages = page.getTotalPages();
            this.size = page.getSize();
            this.number = page.getNumber();
            this.first = page.isFirst();
            this.last = page.isLast();
        }
    }
}

 

3. Dto로 페이징 정보와 결과 값을 담아서 전달한다.

 

DB로부터 가지고 온 결과 Page에는 페이징에 필요한 많은 정보들을 담고 있다.

이를 바탕으로 구현에 필요한 정보들을 Dto에 담아서 반환한다.

 

필드 명 설명
totalElements 전체 필드 수
totalPages totalElements / size
size 페이지 당 조회 수
number 현재 페이지 번호 (0부터 시작)
first 첫 번째 여부
last 마지막 여부

 

🐕 2. React

Api

export const fetchPublicStudyList = ({ page, size, sort }) =>
  client.get(`/api/study?page=${page}&size=${size}&sort=${sort}`);

page, size, sort를 설정해서 서버에 요청하면 Pagination과 동일한 형태의 값을 받아오게 된다.

SCSS

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
}

ul {
  list-style: none;
  padding: 0;
}

ul.pagination li {
  display: inline-block;
  width: 40px;
  height: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1rem;
}

ul.pagination li:first-child {
}

ul.pagination li:last-child {
}

ul.pagination li a {
  text-decoration: none;
  color: #22b8cf;
  font-size: 1rem;
}

ul.pagination li.active a {
  color: white;
}

ul.pagination li.active {
  border-radius: 40px;
  background-color: #22b8cf;
}

ul.pagination li a:hover,
ul.pagination li a.active {
}

.page-selection {
}

Component

<Pagination
  activePage={page.number + 1}
  itemsCountPerPage={10}
  totalItemsCount={page.totalElements}
  pageRangeDisplayed={5}
  firstPageText={
    <KeyboardDoubleArrowLeft
      style={{ fontSize: 25, display: 'flex', color: '#22b8cf' }}
    />
  }
  prevPageText={
    <ArrowBackIos
      style={{ fontSize: 15, display: 'flex', color: '#22b8cf' }}
    />
  }
  nextPageText={
    <ArrowForwardIos
      style={{ fontSize: 15, display: 'flex', color: '#22b8cf' }}
    />
  }
  lastPageText={
    <KeyboardDoubleArrowRight
      style={{ fontSize: 25, display: 'flex', color: '#22b8cf' }}
    />
  }
  onChange={(e) => getPublicStudyList(e - 1)}
/>

 

서버에서 가지고 온 페이징 정보를 바탕으로 속성 값들을 설정한다.

 

라이브러리는 다양한 속성을 가지고 있지만, 그중 사용했던 속성에 대해서만 설명하자면 다음과 같다.

(전체 속성에 대한 설명은 하단의 REFERENCE를 참고 바람)

 

속성 명 설명
activePage
활성화 된 페이지 (Pagination = number)
itemsCountPerPage
페이지당 화면에 보여줄 리스트 수 (Pagination = size)
totalItemsCount
전체 개수 (Pagination = totalElements)
pageRangeDisplayed
보여줄 페이지 범위 (5를 설정하면 1~5 버튼이 보인다)
firstPageText
가장 이전 페이지로 가는 Text (String, React Element 타입 지원)
prevPageText
이전 페이지로 가는 Text (String, React Element 타입 지원)
nextPageText
다음 페이지로 가는 Text (String, React Element 타입 지원)
lastPageText
가장 마지막 페이지로 가는 Text (String, React Element 타입 지원)
onChange
페이지가 바뀔 때 이벤트 (e는 현재 페이지 번호를 가지고 있다.)

 

PageText 속성은 React Element도 지원하기 때문에, Material Icon의 UI를 사용하면 더 예쁘게 꾸밀 수 있다.

한 번 scss + Material Icon를 활용해서 각 프로젝트에 맞는 개성 있는 페이징을 구현해 보자! 👍

 

REFERENCE

https://www.npmjs.com/package/react-js-pagination

 

react-js-pagination

Simple, easy to use component for pagination. Compatible with bootstrap paginator stylesheets. Latest version: 3.0.3, last published: 3 years ago. Start using react-js-pagination in your project by running `npm i react-js-pagination`. There are 109 other p

www.npmjs.com

https://mui.com/material-ui/material-icons/

 

Material Icons - Material UI

2,100+ ready-to-use React Material Icons from the official website.

mui.com

https://github.com/wwwaiser/react-js-pagination

 

GitHub - wwwaiser/react-js-pagination

Contribute to wwwaiser/react-js-pagination development by creating an account on GitHub.

github.com

 

댓글