inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

호돌맨의 요절복통 개발쇼 (SpringBoot, Vue.JS, AWS)

게시글 조회 5 - 페이징 처리 (QueryDSL)

Count 쿼리 발생에 대해 궁금합니다.

557

전병준

작성한 질문수 10

0

안녕하세요 호돌맨님 ! 질문이 있습니다 !!

Pageable 을 사용해서 페이징 구현 시 Full Scan 을 하기 위해 Count 쿼리가 발생하는걸 확인했는데, Pageable 에서는 페이징 처리를 위해 총 데이터의 개수를 파악해야 하기 때문이니까 Count 쿼리가 필요한거고 Querydsl 은 직접 limit 값, offset 값을 지정하기 때문에 Full Scan 할 필요가 없으니 Count 쿼리가 필요없는게 맞을까요 ?

vue.js aws spring-boot jpa spring-security

답변 2

1

인프런 AI 인턴

안녕하세요 전병준님! Count 쿼리에 대한 질문이 있군요.

일반적으로 페이징 처리를 위해 총 데이터의 개수 파악이 필요합니다. Pageable을 사용해서 페이징을 구현할 때, 총 데이터 개수를 파악하기 위해 Count 쿼리가 발생하게 됩니다. 따라서 Pageable을 사용하면 Count 쿼리가 필요한 것입니다.

하지만 Querydsl을 사용하면 직접 limit 값과 offset 값을 지정할 수 있기 때문에, Full Scan 할 필요 없이 데이터를 찾을 수 있습니다. 이 경우 Count 쿼리가 필요하지 않을 수 있습니다.

즉, Pageable을 사용할 때는 Count 쿼리가 필요하고, Querydsl을 사용할 때는 Count 쿼리가 필요하지 않을 수 있다는 것이 맞습니다. 페이지 처리 방식에 따라 Count 쿼리의 필요 여부가 달라집니다.

더 자세한 내용이나 궁금한 점이 있으시면 언제든지 물어보세요! 좋은 하루 되세요~요.

0

호돌맨

안녕하세요. 호돌맨입니다.

Full Scan 여부는 별로 상관 없어 보이는데요.
페이지네이션 처리를 하려면 전체 데이터 개수를 알아야합니다.
그렇기 때문에 QueryDSL을 이용해 데이터를 가져오실때 별도로 totalCount 쿼리를 통해
아이템, 전체 개수를 반환 해야합니다.

Pageable을 이용하면 이러한 과정을 좀 더 쉽게 해주는것 뿐입니다.

감사합니다.

0

전병준

Hibernate: 
    select
        post0_.id as id1_2_0_,
        user1_.user_id as user_id1_4_1_,
        post0_.create_at as create_a2_2_0_,
        post0_.modified_at as modified3_2_0_,
        post0_.content as content4_2_0_,
        post0_.is_deleted as is_delet5_2_0_,
        post0_.title as title6_2_0_,
        post0_.user_id as user_id7_2_0_,
        user1_.email as email2_4_1_,
        user1_.name as name3_4_1_,
        user1_.oauth_id as oauth_id4_4_1_,
        user1_.picture as picture5_4_1_,
        user1_.role as role6_4_1_ 
    from
        post post0_ 
    inner join
        users user1_ 
            on post0_.user_id=user1_.user_id 
    order by
        post0_.id desc limit ?,
        ?

저도 그렇게 생각하는데 Pageable 을 사용하지 않고 .fetct() 로 List<Post> 를 반환하니 count 쿼리가 따로 나가지 않아서요.. 그래서 찾아보니 fetch() 로 조회하면 count 쿼리가 따로 나가질 않으니 필요하다면 애플리케이션 로직에서 처리하던가, count 쿼리를 따로 호출해야한다고 하더라고요. 근데 count 쿼리가 나가질 않는데 어떻게 페이지네이션이 동작하는지 그걸 모르겠습니다..

0

호돌맨

Pageable로 호출한 부분 코드를 보여주실 수 있을까여?

0

전병준

// Controller
@GetMapping("/posts")
  public ResponseEntity<PostMultiReadResponse> readAll(@ModelAttribute PageSearch pageSearch) {
    PostMultiReadResponse response = postService.readAll(pageSearch);

    return ResponseEntity.ok(response);
  }

// Service
@Transactional
  public PostMultiReadResponse readAll(PageSearch pageSearch) {
    List<Post> offset = postRepository.readAll(pageSearch);
//    List<Post> noOffset = postRepository.readNoOffset(pageSearch);

    List<PostSingleReadResponse> response = offset.stream()
        .map(post -> PostSingleReadResponse.builder()
            .id(post.getId())
            .title(post.getTitle())
            .content(post.getContent())
            .createdDate(post.getCreateAt())
            .modifiedDate(post.getModifiedAt())
            .writer(WriterResponse.builder()
                .name(post.getUser().getName())
                .email(post.getUser().getEmail())
                .picture(post.getUser().getPicture())
                .build())
            .build())
        .collect(Collectors.toList());

    return PostMultiReadResponse
        .builder()
        .postsResponse(response)
        .build();
  }

// Repository
@Override
  public List<Post> readAll(PageSearch pageSearch) {
    return jpaQueryFactory.selectFrom(QPost.post)
        .join(QPost.post.user)
        .fetchJoin()
        .limit(pageSearch.getSize())
        .offset(pageSearch.getOffset())
        .orderBy(QPost.post.id.desc())
        .fetch();
  }

// Service Test
  @Test
  @DisplayName("게시글 전체 조회 API 페이징 성능을 테스트합니다.")
  void readAll() {
    // given
    PageSearch pageSearch = new PageSearch(1, 20);

    // when
    long startTime = System.currentTimeMillis();
    List<Post> response = postRepository.readAll(pageSearch);
    long endTime = System.currentTimeMillis();
    long pagingTime = endTime - startTime;
    System.out.println("반환 시간: " + pagingTime + "ms");

    // then
    assertThat(response).isNotNull();
  }

넵 차례대로 Controller, Service, Repository, Test 코드입니다.

테스트 코드 실행 결과는 아래와 같습니다.

Hibernate: 
    select
        post0_.id as id1_2_0_,
        user1_.user_id as user_id1_4_1_,
        post0_.create_at as create_a2_2_0_,
        post0_.modified_at as modified3_2_0_,
        post0_.content as content4_2_0_,
        post0_.is_deleted as is_delet5_2_0_,
        post0_.title as title6_2_0_,
        post0_.user_id as user_id7_2_0_,
        user1_.email as email2_4_1_,
        user1_.name as name3_4_1_,
        user1_.oauth_id as oauth_id4_4_1_,
        user1_.picture as picture5_4_1_,
        user1_.role as role6_4_1_ 
    from
        post post0_ 
    inner join
        users user1_ 
            on post0_.user_id=user1_.user_id 
    order by
        post0_.id desc limit ?,
        ?

반환 시간: 177ms

Deprecated 관련 사항들

0

100

2

깃헙 collaboator 초대 관련

0

87

1

강의 듣다가 도커 이미지 생성시 각각도 가능하나 그렇게 사용하는데가 많은지 모르겠다라는 말을 듣고 남김니다

0

157

2

logout 후에 login 페이지 이동은 어디서 시켜주는건가요?

0

231

1

다중 데이터를 삭제 할 때

0

270

2

querydsl Q class 이슈

0

414

2

Windows WSL Vue 설정

2

246

1

Dip, @transactional

0

187

1

[vite] http proxy error: /auth/login

0

1043

2

로그인 하고 나서 GET요청으로 메인페이지 요청

0

234

2

GitHub Collaborator 초대 관련

0

256

2

Window에서 Vue.js 설정

0

321

2

(솔루션 수정)'tsyringe' Error: TypeInfo not known for "클래스명"

0

713

2

collaboator로 초대받을 수 있을까요??

0

283

2

SecurityMockContext 로부터 유저 정보를 가져오기

0

261

1

given 부분이 길어질 때 어떻게 처리하면 좋을까요?

0

318

1

섹션9 프론트의 코드를 보고싶습니다,,,

0

423

1

Spring Security - defaultSuccessUrl 질문

0

625

1

강의 화면이 나오지 않습니다. 음성과 자막만 나와요

0

302

1

JPAQueryFactory(em)의 객체 생성자 오류에 대해서 질문이 있습니다ㅜㅜ

0

690

2

ExceptionHandler가 AccessDeniedHandler(Http403Handler)를 먹어버리는 현상

0

1179

2

섹션10 언제 나오나요?

0

484

1

CommentService에서 Repository를 호출하지 않는데도

0

342

1

Editor....를 활용한 패턴에 질문있습니다.

0

494

1