-
무한스크롤 도입Health-Genie 2024. 3. 2. 11:31728x90
기존에는 트레이너 프로필 조회하는 기능을 offset을 통한 페이징 처리를 하였습니다.
하지만 데이터 양이 많을때 offset 방식의 문제점은 아래와 같이 존재 하였습니다
- 맨뒤의 페이지는 모든 데이터를 조회한 후에 마지막 페이지를 조회하기 때문에 뒤로 갈수록 조회가 느려집니다.
thalals님의 자료를 보면 특정 페이지의 데이터를 읽을 때와 맨 뒤의 페이지를 읽을 때 성능 차이를 알 수 있습니다.
offset 방식 [ 7만건의 데이터 중 ]
- 첫번째 페이지를 읽을 때 : 0.738
- 마지막 페이지를 읽을 때 : 2785.614 -> 0.984 [ 모든 데이터 조회 -> 특정 페이지 조회 ]
no offset 방식 [ 7만건의 데이터 중 ]
- 첫번째 페이지를 읽을 때 : 0.032
- 마지막 페이지를 읽을 때 : 0.044 -> 0.041보시다 시피 no offset 같은 경우에는 첫번째 페이지와 마지막 페이지의 성능차이가 크게 없으며, 페이지가 늘어도 동일한 성능을 보장합니다.
그렇다면 no offset이 뭔가?
말 그대로 offset을 사용하지 않고 페이지네이션을 진행한다는 말입니다.
[ offset - 몇번째 부터 시작할것인가? ]
즉, 어디서 부터 시작할지를 offset을 사용하지 않고 판단한다는 것입니다.
어떻게 사용하는가?
offset을 대신해 탐색을 해준 위치로 이동시켜줄 무언가만 있으면 됩니다.
[ 아래 저의 코드에서는 lastIndex를 통해서 지정해주었습니다 ]
select * from table where idx > 0 limit 100;
이런식으로 간단하게 볼 수 있습니다.
코드 보기
기존에 offset을 통한 페이징 처리에서 현재 무한스크롤로의 변화를 보여드리겠습니다.
controller
[ 변경 전 ]
@GetMapping public ResponseEntity<Result> getAllProfile(@RequestParam(required = false, defaultValue = "0") int page) { List<ProfileResponseDto> response = profileService.getAllProfile(page, 10); return ResponseEntity.ok(Result.of(response)); }
[ 변경 후 ]
@GetMapping public ResponseEntity<Result> getAllProfile(@RequestParam(value = "lastIndex", required = false) Long lastIndex) { List<ProfileResponseDto> response = profileService.getAllProfile(lastIndex); return ResponseEntity.ok(Result.of(response)); }
controller 같은 경우는 parameter로 받는 데이터가 offset & limit 에서 lastIndex로의 변화에 집중하면 될거 같습니다.
service
[ 변경 전 ]
@Transactional(readOnly = true) public List<ProfileResponseDto> getAllProfile(int page, int size) { List<TrainerInfo> profiles = trainerQueryRepository.findAllProfiles(page, size); return ProfileResponseDto.of(profiles); }
[ 변경 후 ]
@Transactional(readOnly = true) public List<ProfileResponseDto> getAllProfile(Long lastIndex) { Long maxId = lastIndex; if (maxId == null) { maxId = trainerQueryRepository.findMaxId().orElse(0L); } List<TrainerInfo> profiles = trainerQueryRepository.findAllProfilesSortByLatest(maxId); return ProfileResponseDto.of(profiles); }
repository ( querydsl 작성 )
[ 변경 전 ]
public List<TrainerInfo> findAllProfiles(int page, int size) { return query .selectFrom(trainerInfo) .orderBy(trainerInfo.id.desc()) .offset(page) .limit(size) .fetch(); }
[ 변경 후 ]
public List<TrainerInfo> findAllProfilesSortByLatest(Long lastIndex) { return query .selectFrom(trainerInfo) .where(trainerInfo.id.loe(lastIndex)) .orderBy(trainerInfo.id.desc()) .limit(10) .fetch(); } public Optional<Long> findMaxId() { return Optional.ofNullable(query .select(trainerInfo.id.max()) .from(trainerInfo) .fetchOne()); }
기존에 비해 만일 lastindex가 null이 나오는 경우를 막기위해서 findMaxId()를 추가적으로 넣어주었습니다.
참고
https://medium.com/@hee98.09.14/무한-스크롤-기능-구현하기-with-no-offset-a580bf2addc5
무한 스크롤 기능 구현하기 (with No Offset)
OFFSET 방식의 페이지 처리 문제점
medium.com
'Health-Genie' 카테고리의 다른 글
[exception caching] 성능 향상을 위한 exception 처리 (0) 2024.03.21 [Health-Genie] orElse() vs orElseGet() 차이 (2) 2024.03.06 [모니터링] AWS - cloud watch 설정 (1) 2024.01.30 [CICD] 프로젝트 빌드 (2) 2024.01.25 [CICD] github actions CD ssh 연결안됨 (0) 2024.01.25