무한 스크롤
- 유저가 메인 페이지를 스크롤 하는 것만으로 간편하게 새로운 게시물을 확인할 수 있도록 무한스크롤 페이지네이션을 구현하였습니다.
- 해당 기능은 인스타그램이나 페이스북 같은 SNS에서 많이 보셨을겁니다.
- 무한 스크롤을 구현하기 위해서
No Offset
방식과 Slice
를 사용했습니다
No Offset 방식이란?
- 기존의 페이징은
offset
과 limit
을 사용해서 페이징할 범위를 정합니다.
- 이 방식은 초반에는 효율이 나쁘지않지만 뒤로 갈수록 효율이 급격히 떨어진다.
원인
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))
.limit(pageable.getPageSize()+1)
.orderBy(post.id.desc())
.fetch();
}
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;
if (postResponses.size() > pageable.getPageSize()) {
hasNext = true;
postResponses.remove(pageable.getPageSize());
}
log.info("Slice PostResponse size = {}", postResponses.size());
return new SliceImpl<>(postResponses, pageable, hasNext);
}
- 만약 지금 페이지가 마지막 페이지가 아니라면 요청으로 들어온
pageable
의 pageSize
보다 results
의 size
가 더 클 것이다. 하지만 만약 현재 페이지가 마지막이라면 +1해서 조회했더라도 result의 size가 더 크지는 않을 것이다.
- last 페이지가 아닐경우, +1해서 조회한 데이터 삭제
참조 : https://devjem.tistory.com/74