메인 프로젝트 (6)메인페이지 - 무한 스크롤

InSeok·2022년 12월 10일
0

프로젝트

목록 보기
6/13

무한 스크롤

  • 유저가 메인 페이지를 스크롤 하는 것만으로 간편하게 새로운 게시물을 확인할 수 있도록 무한스크롤 페이지네이션을 구현하였습니다.
  • 해당 기능은 인스타그램이나 페이스북 같은 SNS에서 많이 보셨을겁니다.
  • 무한 스크롤을 구현하기 위해서 No Offset 방식과 Slice를 사용했습니다

No Offset 방식이란?

  • 기존의 페이징은 offsetlimit을 사용해서 페이징할 범위를 정합니다.
  • 이 방식은 초반에는 효율이 나쁘지않지만 뒤로 갈수록 효율이 급격히 떨어진다.

원인

  • offset + limit 까지의 데이터를 DB에서 전부 다 가지고 온 후에 limit 만큼만 우리에게 반환해주는 방식으로 동작 ⇒ 페이지가 뒤로 갈수록 읽어야 할 행의 개수는 기하급수적으로 늘어남
  • ex) offset이 1000이고 limit이 20이라면 맨 뒤의 20개만 DB에서 가져오는 것이 아니라 1020개 데이터 전부 가져와서 나머지 1000개는 그냥 버리게 된다.

해결

No Offset방식

  • 조회 시작 부분을 인덱스로 빠르게 찾아서 매번 첫페이지만 읽도록 하는 방식
SELECT *
FROM Post
WHERE 조건문
AND id < 마지막 조회 id
ORDER BY id DESC
LIMIT 페이지 사이즈
  • 조건절에 들어가는 id는 클러스터링 인덱스이기 때문에 매우 빠르게 조회 가능
    • 모든 페이지를 첫 페이지를 조회하듯이 일정한 개수를 가져올 수 있다.

Slice - Spring Data Jpa

  • Slice 형태로 응답을 반환 할때, last 라는 속성을 통해 클라이언트에게 남은 페이지가 있는지 알려 줄 수 있다.
public List<Tuple> findList(Long lastPostId,Long memberId, Pageable pageable) {

   return  jpaQueryFactory
            .select(post,likes.id,post.postComments.size())
            .from(post)
            .leftJoin(post.member,member).fetchJoin()
            .leftJoin(post.likes,likes)
             .on(likes.member.id.eq(memberId))
            .where(ltPostId(lastPostId)) //no -offset 페이징 처리
            .limit(pageable.getPageSize()+1) //마지막 페이지인지 여부 확인하기위해 +1 해서 조회
            .orderBy(post.id.desc()) // 최신순
            .fetch();

}

// no -offset 방식 처리 메서드
    private BooleanExpression ltPostId(Long postId) {
        return isEmpty(postId) ? null : post.id.lt(postId);
    }
  • No Offset으로 처음 조회할때는 몇번째 id 부터 조회하는지 알 수 없기 때문에 null 값을 넘겨줘야 한다.
    • where 절에 null을 반환하면, 내림차순으로 페이지 사이즈 만큼 조회가 된다.
  • 클라이언트 측에서는 반환된 데이터 중 마지막 데이터의 id를 기준으로 '마지막 조회 id'를 알아낸 뒤, 이후 요청에 포함해서 서버에 전송해주면 된다.
private Slice<PostResponse> checkLastPage(List<PostResponse> postResponses, Pageable pageable) {

    boolean hasNext = false;

    //조회 결과 개수가 요청한 페이지 사이즈보다 크면 뒤에 더 있음, next = true
    if (postResponses.size() > pageable.getPageSize()) {
        hasNext = true;
        // 확인용으로 추가한데이터 remove(findList에서 limit에 +1해서 조회한 데이터)
        postResponses.remove(pageable.getPageSize());
    }

log.info("Slice PostResponse size = {}", postResponses.size());

    return new SliceImpl<>(postResponses, pageable, hasNext);
}
  • 만약 지금 페이지가 마지막 페이지가 아니라면 요청으로 들어온 pageablepageSize보다 resultssize가 더 클 것이다. 하지만 만약 현재 페이지가 마지막이라면 +1해서 조회했더라도 result의 size가 더 크지는 않을 것이다.
  • last 페이지가 아닐경우, +1해서 조회한 데이터 삭제

참조 : https://devjem.tistory.com/74

profile
백엔드 개발자

0개의 댓글