커서 기반 페이지네이션 구현(무한스크롤)

Do_It·2023년 11월 23일

오늘은 어디~?

목록 보기
5/9

커서 기반 페이지네이션 구현

마이페이지에서 그동안 했던 운동 기록들을 볼 수 있게 구현할 것!
스크롤 형식으로 구현하기로 하였다.

날짜
해당 날짜에 했던 모든 유산소 운동 시간 합
유산소 운동 내역

해당 날짜에 했던 모든 무산소 운동 시간 합
무산소 운동 내역

날짜2
해당 날짜에 했던 모든 유산소 운동 시간 합
유산소 운동 내역

해당 날짜에 했던 모든 무산소 운동 시간 합
무산소 운동 내역

....
이런식으로 스크롤을 구현할 생각이다.

커서기반 페이지네이션

  • 데이터베이스에서 일부 데이터를 검색하고 그 결과를 페이지 단위로 반환하는 기술 중 하나
  • 이전 페이지의 끝 지점에 대한 커서값을 사용하여 다음 페이지의 결과를 가져오는 방식
  • 커서는 일종의 포인터 또는 마커, 현재 페이지에서 마지막 항목의 위치를 나타냄.
  • 장점
    1. 일괄적인 결과 순회 : 커서를 사용하면 클라이언트는 결과를 일괄적으로 가져오고, 특정 페이지로 이동하지 않고 순회할 수 있음
    1. 결과의 일관성 : 새로운 데이터가 추가되거나 삭제되어도 커서를 기반으로 한 페이지네이션은 일관된 결과를 제공
    2. 성능 향상 : 특히 대용량 데이터베이스에서는 페이지 번호를 사용하는 것보다 성능이 향상될 수 있음

커서 기반 페이지네이션 작동 원리

아주 간략한 예시)

쿼리문 작성 후 페이지 사이즈 만큼 페이지 조회(설정하기 나름)

workoutId : 10
생성날짜 : 2022-11-23

workoutId : 9
생성날짜 : 2022-11-20

workoutId : 8
생성날짜 : 2022-11-11

이 때 중요한 것은 cursor를 어떤걸로 잡을것인가이다.
커서 이후의 데이터를 불러와야하기 때문이다.
위와 같은 예시로는 workoutId를 커서로 잡는다면

쿼리문에는 커서보다 작은 데이터들을 불러오도록 하면 된다
커서는 프론트엔드단에서 설정하는 것임!
스크롤해서 다시 요청이 온다면
cursor값에 8이 넣어서 요청이 왔다면

workoutId : 7
생성날짜 : 2022-11-10

workoutId : 6
생성날짜 : 2022-11-06
...

이런식으로 데이터를 조회할 수 있게 되는 것이다.

내가 구현한 것들

@Operation(summary = "모든 운동 목록 가져오기")
    @GetMapping("/all")
    public CommonResponse<?> getAllWorkoutList(
            @AuthenticationPrincipal User user,
            @RequestParam(value = "cursor", required = false,defaultValue = "0") Long cursor,
            @RequestParam(value = "pageSize", defaultValue = "7") int pageSize) {
        return CommonResponse.success(workoutService.getAllWorkoutList(user,cursor,pageSize));
    }

value = "pageSize", defaultValue = "7" 으로 처음 조회시 기본으로 7개 데이터가 조회되도록 만들었음

public List<Map<String,Object>> getAllWorkoutList(User user, Long cursor, int pageSize) {
        PageRequest pageRequest = PageRequest.of(0,pageSize);

        List<Object[]>  workoutList = workoutRepository.findWorkoutWithCardioAndStrength(user,cursor,pageRequest);
        if(workoutList.isEmpty()) return null;

서비스단에서서는 파라미터로 받은 pageSize를 PageRequest 로 만들어서 cursor와 함께 넘겨줌
workoutRepository.findWorkoutWithCardioAndStrength(user,cursor,pageRequest)

이제 workoutRepository에서는

 @Query("SELECT w.id,w.createdAt,w.cardioExTime,ce.exName, ce.km,w.strengthExTime, se.part, se.exName, se.kg, se.rep, se.set " +
            "FROM Workout w " +
            "LEFT JOIN CardioEx ce ON w = ce.workoutId " +
            "LEFT JOIN StrengthEx se ON w = se.workoutId " +
            "WHERE w.usersId = :user " +
            "AND(:cursor = 0 OR w.id < :cursor) "+
            "ORDER BY w.createdAt DESC ")
    List<Object[]> findWorkoutWithCardioAndStrength(
            @Param("user") User user,
            @Param("cursor") Long cursor,
            Pageable pageRequest);

이 쿼리문에 따라서 데이터를 추출해준다.

"AND(:cursor = 0 OR w.id < :cursor) "
커서 초기값 0으로 해놨기 때문에,커서의 값이 0인 경우 7개의 데이터를 보내주는데, 마지막 데이터의 workoutId가 곧 cursor 값이 되고, 요청이 다시 오면 workoutId보다 작은 데이터들을 보내주는 것이다. workoutId가 작을 수 록 이전 운동기록이기 때문!

profile
오늘의 노력이 내일의 성장으로 이어지고 있음을

0개의 댓글