paging total Elements가 뻥튀기 됐는데요?!

탄이·2023년 8월 22일
0
post-thumbnail

버그 원인 파악 중 만난 이슈로 이틀을 고민하다 해결한 게 있어 기록으로 남기려고 한다.

버그 발생 상황

  • 쿼리에서 distinct, paging 작업을 하던 것을 java 단에서 처리하려고 수정
  • 쿼리에서 데이터를 가져온 뒤, stream().distinct()까지만 진행된 채로 배포
@Transactional(readOnly = true)
public Page<Response> getHistories(List<Long> brandIds, String startDate, String endDate, PageRequest pageRequest) {
        List<MassMessageType> messageTypes = MassMessageType.getMassMessageTypeList(channelName);
        LocalDateTime startTime = DateUtils.convertKstToUtcStartLocalDateTime(startDate);
        LocalDateTime endTime = DateUtils.convertKstToUtcEndLocalDateTime(endDate);
        
        if (startTime.isAfter(endTime)) {
            throw BaseException.of(ApiErrorCode.BAD_REQUEST);
        }
        
        List<Response> histories = historyRepository.findHistory(brandIds, startTime, endTime, pageRequest)
                .stream()
                .distinct()
                .collect(Collectors.toList());

        Long count = historyRepository.findCountHistories(brandIds, startTime, endTime)
                .orElse(0L);
        return new PageImpl<>(histories, pageRequest, count);
    }

해결 방법을 찾던 중 내가 만난 이슈

  • java단에서 페이징 처리를 추가하면 되는 것으로 판단
  • distinct().limit(pageRequest.getPageSize()) 까지만 추가함
  • 그러고 return new PageImpl<>(histories, pageRequest, count) 로 넘김
    -> 그랬더니 total Elements가 달라졌다!!
// 위는 생략
List<Response> histories = historyRepository.findHistory(brandIds, startTime, endTime, pageRequest)
                .stream()
                .distinct()
                .limit(pageRequest.getPageSize()) //여기만 추가
                .collect(Collectors.toList());

        Long count = historyRepository.findCountHistories(brandIds, startTime, endTime)
                .orElse(0L);
                
         // histories.size() = 15, count = 23
        return new PageImpl<>(histories, pageRequest, count);
        // return 되는 결과 값은 total Elements = 30으로 나왔다!!
    }

무엇이 total Elements를 부풀렸나?

내가 잘못한 것은 크게 두 가지였다.

1) PageRequest를 넘길 때 잘못 넘겼다.

  • 포스트맨으로 디버깅하며, param 설정을 잘못 했다.
    • 1 page를 확인하려면 -1 처리해서 0으로 PageSize()를 세팅해줬어야 했다.
    • param을 1로 보냈으면 2 페이지를 조회하겠다는 뜻이 됐다.

2) stream에서 skip(pageRequest.getOffSet())을 빼먹었다.

  • 페이징 처리의 필수값인 skip()을 뺀 채로 stream이 돌다 보니, total 산출하는 로직에서 기대값과 다르게 나왔다.

skip()과 limit()

  • skip() : 지정한 갯수만큼 '건너 뛰겠다'.
  • limit() : 지정한 갯수만큼만 받겠다.
    즉, 페이징할 때는 skip()은 이번 페이지에서 보여주기 위해 앞에서 몇 개를 건너뛸 것인지를 나타내고
    limit()은 한 페이지에 표기할 데이터 개수를 뜻한다.

그래서 내가 작성했던 값과 코드의 의미는..

2 페이지(page = 1)에 표기될 값을 알고 싶지만 건너뛸 데이터는 없음. 처음부터 15개로 보여줘.

-> 그렇게 뽑힌 데이터가 total을 산출할 때 아래 로직을 타게 된다.

// content.size() = 15, total = 23
public PageImpl(List<T> content, Pageable pageable, long total) {

		super(content, pageable);

		this.total = pageable.toOptional().filter(it -> !content.isEmpty())//
        		//            15 x 1 + 15 > 23 (true)
				.filter(it -> it.getOffset() + it.getPageSize() > total)// 
                //            15 + 15 = 30
				.map(it -> it.getOffset() + content.size())//
				.orElse(total);
	}// this.total = 30

이래서 total Elements가 자꾸 30이 나왔던 것이다!

해결방법

위의 무엇이 부풀렸는지를 고치면 해결 방법이 된다!

1) param 값을 내가 원하는 페이지에 -1 해준다 (1페이지 조회 = 0, 2페이지 조회 = 1 ...)
2) skip()을 추가해준다!

그래서 최종 수정한 값은..

// 코드 일부 생략
	List<Response> histories = historyRepository.findHistory(brandIds, startTime, endTime)
     	           .stream()
        	        .distinct()
            	    .skip(pageRequest.getOffset())
                	.limit(pageRequest.getPageSize())
                	.collect(Collectors.toList());

	Long count = historyRepository.findCountHistories(brandIds, startTime, endTime)
                .orElse(0L);
                
	return new PageImpl<>(histories, pageRequest, count);
}

total이 왜! 뻥튀기 되는지 몰랐는데 드디어 이유를 알게 되서 속이 시원했다ㅎㅎ
그리고 이 기회에 total 산출하는 식을 좀 더 들여다 봐서 재밌었다.

profile
백엔드 개발자의 로그

0개의 댓글