강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

항상배고픔님의 프로필 이미지
항상배고픔

작성한 질문수

코드로 배우는 React 19 with 스프링부트 API서버

TanStack Query (React Query) 설정

수업 외 질문

해결된 질문

작성

·

23

·

수정됨

0

안녕하세요, 강사님께서 제공해주신 프로젝트를 활용하여 부하 테스트를 진행하며 실습 중인 학생입니다.

 

페이징 기능 사용 시 count 쿼리가 자동으로 실행되어 많은 리소스를 필요로 한다는 내용은 인지하고 있었습니다. 실제로 테스트를 진행해보니 이 때문에 CPU 사용량이 급증하는 현상을 직접 체감하게 되었습니다.

 

현재 사용하고 있는 PageResponseDTO의 빌더 생성자를 보면 totalCount가 사실 상 필수로 요구됩니다. 이로 인해 '결국 count 쿼리는 무조건 같이 필요한 것 아닌가?'라는 의문이 생겼습니다.

왜냐하면 다음 페이지, 이전 페이지 같은 기능은 Slice를 사용하면 될 것 같은데 페이지 이동 같은 경우에는 어떻게 처리하는 지 궁금합니다.


더 나아가 데이터가 방대하게 많아지는 경우 페이징 처리는 일반적으로 어떻게 접근하고 해결하는지 조언을 구하고 싶습니다.


귀찮게 해서 죄송합니다 ㅠㅠ

답변 2

1

구멍가게코딩단님의 프로필 이미지
구멍가게코딩단
지식공유자

좋은 질문 감사합니다.

 

페이징 처리를 위한 카운트 쿼리(이하 카운트쿼리) 의 근본적인 문제는 거의 동일한 쿼리를 한 번 더 실행한다는 사실입니다. 그리고 이 과정에서 잘못하면 테이블 전체에 대한 스캔이 일어나게 됩니다(실제로 공격시에도 이상한 단어 검색을 유도하기도 합니다.).

 

하지만 매번 달라지는 검색 조건 때문에 카운트 쿼리를 피할 수도 없습니다.

그런데 곰곰히 생각해 보면 강의에서도 언급했듯이 최근의 웹은 마지막 페이지는 노출하지 않는다는 특징이 있습니다. -- 그러면 마지막 페이지까지는 필요없게 됩니다.

 

쿼리가 2번 필요하다고 생각들 하지만 사실 페이징 처리에 필요한 양은 (1페이지에 10개씩 화면에 페이지 번호가 10개 출력한다고 가정) 100 + 1 개 이상인지 입니다. 1개가 추가되는 이유는 마지막 페이지 번호 뒤에 붙는 '다음' 페이지로 이동하는 링크 때문입니다.

 

만일 현재 페이지 3 페이지이고 101개의 데이터가 존재한다면? 페이징 처리가 온전히 가능해 집니다. 유사하게 11페이지라면 201개의 데이터만 있다면 됩니다. 201개 중에서 최종적으로 101개의 데이터만 반환된다면 화면에 페이징 처리가 가능합니다.

 

한번의 쿼리를 통해 101개의 데이터를 조회하므로 실제 데이터와 추가적인 카운트 쿼리를 없애서 성능향상을 기대할 수 있습니다. 특히 검색의 경우는 1번 쿼리를 실행하는것과 2번 실행하는 것은 차이가 엄청납니다). 이 방식 역시 페이지 번호가 1000번 이상 넘어가면 성능이 저하될 수 있긴 합니다(때문에 은행이나 카드회사의 내역이 최근 몇 달로 제한되는 것입니다). 최근에 유행하는 방법들(캐싱이나 NoSQL 등)의 방법들도 있겠지만 근본적인 문제는 결국 많은 데이터를 여러 번 스캔하는 것입니다.

 

Slice는 사실 JDBC의 Movable Cursor와 유사한 개념입니다. ResultSet에서 커서를 이동시키는 방식이라.. 페이지 번호가 높아지면 커서의 이동시에 메모리 사용등에서 치명적인 성능 저하가 일어나기 때문에 제가 일하던 곳에서는 사용하지 않도록 했던 적이 있습니다.

 

데이터가 방대한 경우에는 인덱스 외에도 검색하는 데이터의 양을 줄일 수 있는 방법들(최근 100만건 내에서만 쿼리를 수행하거나 날짜로 제한) 을 이용하거나 테이블 파티셔닝과 같이 데이터를 줄여서 처리할 수 있는 방법을 고민하기도 합니다. 검색의 경우 성능 저하가 심해지기 때문에 색인 엔진(검색 엔진)의 도입을 고려하는 것도 필요하구요.

 

 

 

 

 

 

0

안녕하세요, 인프런 AI 인턴입니다. 항상배고픔님.

페이징 처리를 고민하시면서 성능 최적화에 대해 고민 중이신 것 같습니다. count 쿼리에 대한 리소스 사용 문제와 페이지 전환에 대한 부분에 대해 아래와 같이 설명드리겠습니다.

count 쿼리 및 totalCount 처리

  1. count 쿼리의 필요성: totalCount 값을 얻기 위해서는 현재 데이터베이스의 전체 레코드 수를 알아야 하므로, count 쿼리는 본질적으로 필요합니다. 하지만 이 쿼리는 많은 리소스를 소비할 수 있으며, 데이터베이스에 대한 부하를 증가시킬 수 있습니다.

  2. 대안적인 접근:

    • 캐싱: 결과를 캐싱하여 순간적인 데이터 변화가 큰 문제가 되지 않을 경우, 종종 사용하는 방법입니다.
    • 실시간 대체: 실시간으로 정확한 totalCount가 필요하지 않을 경우, 불완전한 데이터 또는 예측 데이터를 활용하여 성능을 개선할 수 있습니다.

Slice 와 페이지 이동

  • Slice 사용의 장점: Slice를 사용하는 경우, 이전 또는 다음 페이지가 존재하는지에 대한 여부만 확인할 수 있기 때문에 totalCount에 대한 의존성을 줄일 수 있습니다. 이 방식은 성능적으로 유리하나, 전체 페이지 수에 대한 정보가 없기 때문에 페이지 직접 이동 기능은 어렵습니다.

  • 페이지 이동 처리:

    1. 경량화된 페이지 이동: totalPages의 확보 없이 페이지 이동 자체는 처리할 수 있으나, 특정 페이지로의 직접 이동은 어려움이 존재합니다.
    2. 전체 페이지 로딩: totalCount에 의존하지 않고 특정 범위나 구현 전략을 통해 페이징된 데이터셋을 제공.

페이징 성능 최적화

  1. 인덱싱 및 최적화:

    • 데이터베이스 레벨에서의 인덱싱 및 효과적인 쿼리 최적화는 필수적입니다.
  2. 데이터 분산 및 파티셔닝:

    • 데이터를 여러 파티션으로 나누는 것은 성능 향상에 큰 기여를 할 수 있습니다.
  3. 타임 스탬프 기반 페이징:

    • 타임 스탬프를 기준으로 하는 방식은, offset 기반이 아닌 where 조건을 통해 필터링이 가능하여 성능에 유리합니다.

관련된 추가 정보를 원하신다면, 질문자님이 제시한 주제와 유사한 내용을 다룬 게시글 “PageResponseDTO 생성자의 매개변수 변수명에 관련된 질문”을 참고해 보세요. 이 게시글에서는 DTO의 매개변수 관련 문의사항과 이에 따른 해결책에 대해 다루고 있습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

항상배고픔님의 프로필 이미지
항상배고픔

작성한 질문수

질문하기