23.03.05 pageable 사용

Kuno17·2023년 3월 7일
0

TIL/WIL

목록 보기
24/38
post-thumbnail

프로젝트에 pageable 적용

리스트 형태로 DTO를 반환하는 서비스에 pageable을 적용하려 한다.

  • 처음 페이징에대한 개념이 모호해서 이를 적용하는대 상당한 혼선을 격었다.
    내가 생각한 페이징 처리와 실제 동작하는 페이징 처리는 달랐다.
    • 나 : 리포지토리에서 불러올떄 Page 타입으로 불러와서 Page 타입으로 반환해야한다!
    • 실제 : 페이지 타입을 적용하려는 부분에만 Page타입으로 찾아서 pageable을 적용하기만 하면된다.

우선은 Paging이란 무엇인지 다시 생각해보자

JPA Paging 이란?

DB에 저장된 Entity들을 페이지로 나누는 것을 말한다.
예를들어 저장된 데이터가 100개라면 한번에 10개씩만 주고 나머지는 다음 파트에서 넘겨주는 방법이다.

그래서 어떻게 사용하나?

기본적으로는 repository애서 findAll()메서드의 parameter에 pageable 또는 Pageable의 구현체인 PageRequest를 넣어주면 된다.

다만 필자가 찾은 코드들은 간단하게 Controller에서 Pageable의 page와 size를 RequestParam으로 PageRequest를 받아 적용하는 방법이다.

PageRequest란?

몇 페이지, 한 페이지의 사이즈, Sorting 방법(Option)을 가지고,Repository에 Paging을 요청할 때 사용하는 것을 말한다.!

PageRequest의 구조는?

위와 같이 인터페이스인 Pageable과 Serializable을 implements하는 AbstractPageRequest라는 추상 클래스가 있다.
그리고 PageRequest class는 이 AbstractPageRequest를 상속한다.

아래 코드는 PageRequest를 구현할때 사용하는 메서드이다.
Sorting의 필요 유무에 따라 아래 3가지 생성자중 하나를 선택해 사용한다.

	public static PageRequest of(int page, int size) {
		return of(page, size, Sort.unsorted());
	}
    
    public static PageRequest of(int page, int size, Sort sort) {
		return new PageRequest(page, size, sort);
	}
    
    public static PageRequest of(int page, int size, Direction direction, String... properties) {
		return of(page, size, Sort.by(direction, properties));
	}

글로 작성하니 무슨 말인가 하는 생각이 들지만 코드로 확인한다면 비교적 쉽게 이해가 가능하다.


프로젝트 적용

프로젝트에서 Pin이라는 게시글을 불러올때 정렬을 한 상태로 불러온다.
( 3월/6일 수정내용 )
JPA Paging을 적용후 속도개선과 코드 정리를 위해서 QueryDsl내부에서 모두 처리 후 반환하는 방법으로 변경.
다만 개념적인 부분이 동일하게 적용된점은 알 수 있다.

Repository

현재 이 코드는 QueryDsl을 통해서 정렬을 완료한 상태이며 내부에서 Page와 size 정보를 이용해서 페이징 처리를 끝낸 후 반환한다.

코드는 다음과 같다.

    @Override
    public Slice<PinResponseDto> findAllByOrderByCreatedAtDesc(Pageable pageable){

        QueryResults<Pin> results = queryFactory
                .select(pin)
                .from(pin)
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize()+1)
                .orderBy(pin.createdAt.desc())
                .fetchResults();

        // DTO로 반환
        List<PinResponseDto> contents = new ArrayList<>();
        List<CommentResponseDto> commentResponseDtos = new ArrayList<>();
        for(Pin pin : results.getResults()){
            List<Comment> comments = pin.getComments();
            for(Comment comment : comments){
                commentResponseDtos.add(CommentResponseDto.from(comment));
            }
            contents.add(new PinResponseDto(pin, commentResponseDtos));
        }

        // 다음 페이지 여부 판단하는 로직
        boolean hasNext = false;
        if (contents.size() > pageable.getPageSize()) {
            contents.remove(pageable.getPageSize());
            hasNext = true;
        }
        return new SliceImpl<>(contents, pageable, hasNext);
    }

Controller

    @GetMapping("/pins")
    public Slice<PinResponseDto> getPostList(@RequestParam(value = "page") int page,
        @RequestParam(value = "size") int size){
        Slice<PinResponseDto> resultList = pinService.getPintList(page, size);
        return resultList;
    }

바로 처음 혼동한 부분이 컨트롤러에서 반환타입 부분이었다.
당연히 Page<PinResponsDto>로 반환타입을 작성해야 하는줄 알앗다..
페이징 처리를 해주는것일 뿐 반환타입을 변경할 필요는 없다!!!

Service

    @Transactional
    public Slice<PinResponseDto> getPintList(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Slice<PinResponseDto> pins = pinRepository.findAllByOrderByCreatedAtDesc(pageable);
        return pins;
    }

이제 리포지토리에서 Paging처리를 완료한 후에 바로 반환할 수 있다.

profile
자바 스터디 정리 - 하단 홈 버튼 참조.

0개의 댓글