tlov

스프링 부트를 이용한 페이징 구현 본문

개발

스프링 부트를 이용한 페이징 구현

nowitzki 2024. 9. 27. 03:45

네이버 쇼핑 속 페이징 기능

 

네이버 쇼핑이나 지메일을 들어가 보면 이러한 페이지 목록들을 확인할 수 있다. 이러한 기능을 구현하는 기법을 페이징이라고 한다. 정확하게 말하면 전체 데이터에서 사용자가 요청한 일부 데이터를 원하는 정렬 방식으로 보여주는 것이다.

 

페이징이 없으면 우리는 대용량의 데이터를 모두 받아서 하나의 웹 페이지에 다 표현해야 한다. 네이버 쇼핑에서 모니터를 검색하면 약 500만 개의 모니터 구매처가 나오는데 이를 하나의 웹 페이지 모두 불러온다면 웹 페이지를 불러오는 시간도 늘어날뿐더러 그만큼 네트워크 트래픽도 증가한다. 또한, 많은 양의 모니터를 보여주다 보니 스크롤 길이도 상당할 것이다. 그렇기에 우리는 페이징 기법으로 웹 페이지에 전체 데이터 중 일부만을 가져와 사용자에게 보여주는 것이다.

 

기본적으로 페이징 기능은 스프링 부트에서 구현하려면 복잡한 과정을 거쳐야한다.

 

url 속 페이징 파라미터가 보인다.

 

네이버 쇼핑에서 아무거나 검색 후에 2페이지로 넘어가면 이 사진처럼 pagingIndex=2와 pagingSize=40과 같은 쿼리스트링이 함께 넘어가는데, 스프링 부트에서는 이렇게 넘어온 페이징 정보와 전체 데이터 정보 등을 활용해 가며 직접 구현하는 방법을 사용한다. 이렇게 하기 위해서는 전체 데이터 개수를 가져와 전체 페이지 개수를 계산하고, 현재 페이지에서 보여줄 데이터를 가져오고, 현재 페이지가 첫 번째 페이지인지 마지막 페이지인지 등등.. 많은 일들을 해야 한다. 이 과정에서 데이터 요청과 데이터 개수 요청으로 2번의 데이터베이스 접근도 무시할 수 없다.

 

그래서 Spring 프레임워크에서는 Spring Data JPA의 Page와 Pageable을 이용하여 많이 페이징을 구현한다.

 

Pageable

Pageable은 페이지 번호, 페이지 크기, 정렬 정보들을 담은 인터페이스이다. 클라이언트로부터의 페이지 요청을 담당하며 해당 인터페이스를 통해 Spring Data JPA 리포지토리로 DB에 접근 시 자동으로 limit 조건을 붙여 데이터들을 가져온다. 인터페이스이기 때문에 구현체로 PageRequest 클래스를 사용하며 PageRequest.of(page, size, sort) 메서드로 생성할 수 있다.

 

Page

Pageable로 요청할 페이지 정보를 넘겨 가져온 데이터는 Page라는 객체에 감싸져 반환된다. 반환된 Page 객체에는 전체 데이터 개수, 페이지 개수, 한 페이지당 데이터 개수, 페이지 번호 등 페이지를 관리하는데 필요한 정보들이 함께 반환되기 때문에 쉽게 페이징 정보들을 관리할 수 있다.

 

 

구현

개인적으로 연습하고 있는 블로그 프로젝트에 한번 페이징을 적용시켜 보았다.

 

먼저, 페이징이 제대로 되는지를 확인하기 위해서 스프링 테스트 프레임워크를 통해 더미 데이터 300개를 데이터베이스에 추가해 주었다. 해당 내용은 점프 투 스프링부트를 참고하였다!!

 

Test Code

 

그리고, 실질적으로 데이터베이스를 조작하는 Jpa Repository에 Page로 감싼 PostEntity를 반환하도록 새로운 메서드를 정의하였다.

Repository

 

다음은 Service 코드이다. 사용자가 누른 페이지 번호를 통해 들어오는 페이지 번호는 1부터 시작하지만, 실제 페이징 처리를 위해서는 0부터 시작하도록 해야 하므로 넘겨받은 pageable 객체에서 page 번호를 -1 해주며 size 변수에 하나의 페이지에서 읽을 크기를 받아온다.

 

업데이트된 해당 정보들을 바탕으로 다시 PageRequest.of로 새로운 Pageable 객체를 만든다. 만들면서 정렬 기준도 'createdTime'을 내림차순으로 정렬하도록 했다. 그래야 최신글부터 보이기 때문이다. 이제, Repository에 이전에 만들었던 findAll 메서드로 Page에 담긴 PostEntity 목록들을 가져온다.

Service

 

간단하게 제목과 작성일만 보여주도록 하였고 넘겨받은 Page<PostEntity>를 Page<PostListDTO>로 변환한다. 

DTO

 

이렇게 구현한 것을 바탕으로 Controller를 다음과 같이 구성하고 실행하였다.

Controller

 

페이지를 읽어와서 보여주기만 했지 아직 페이지 정보들을 활용하지 않아서 밑에 페이지를 선택하는 내비게이션은 없지만 정상적으로 작동하는 모습을 볼 수 있다.

/post?page=4

 

url/post?page=4 이렇게 URL을 변경하여도 한 페이지에 10개를 역순으로 정확히 출력하고 있다. 이제 아래 페이지를 쉽게 이동할 수 있도록 내비게이션 바를 만들어 볼 차례이다. 나는 표시되는 페이지 번호와 url에 넘겨지는 페이지 번호 모두 1부터 시작하도록 하고 서버에서 처리할 때만 모두 0부터 시작하도록 하였다. 그래서, 제가 page=1을 요청했다면 실제 Page 객체 속 getNumber()는 0이 된다.

html

 

조금 복잡하게 Thymeleaf가 설계된 것 같다. 내가 원하는 nav는 1부터 5까지는 1-5까지의 페이지 링크를 보여주고, 그다음부터는 6-10, 11-15가 나타나도록 했다. 1-5까지 요청은 실제로 getNumber() 시 0-4까지 이므로 이를 원하는 5로 나누면 0, 6-10은 1, 11-15는 2가 된다. 그래서 시작 번호는 1-5 페이지를 제외하고는 나눈 값에 * 5를 해주면 되고 끝 번호는 나눈 값에 + 1을 하고 * 5를 해주면 된다. 단, 끝 번호가 전체 페이지 개수를 넘어가면 전체 페이지 개수가 끝 번호가 되도록 하였다. 그렇게 하면 끝 번호는 1-5일 때는 0-4로 나누고 + 1 * 5니까 5가 항상 나오고, 6-10은 10, 11-15는 15가 항상 나오게 된다.

첫 페이지
마지막 페이지

 

이로써 페이징을 이해하고 구현까지 해보았다. 근데, 페이지 링크를 만드는 과정이 조금 아쉽게 느껴지며 더 좋은 방법이 분명 많을 것이다. 계속해서 공부해 보아야겠다!!

 

'개발' 카테고리의 다른 글

MVC 패턴  (3) 2024.10.15
동작 파라미터화 코드 전달하기  (6) 2024.10.10
BeanFactory와 ApplicationContext, Configuration  (0) 2024.10.04
JWT 토큰을 이용한 로그인 방법 이해  (5) 2024.09.15