독크독크 플랫폼에서 개선 포인트를 찾는 중에
데이터 양이 증가할수록 성능 저하를 유발할 수 있는 비효율적인 로직을 발견했다.
특히 페이지네이션 처리 방식에서 개선할 지점을 포착하게 되었고,
이에 대한 고민과 해결 과정을 기록해보고자 한다.
독크독크 플랫폼에서는 사용자가 읽은 책을 기록하고 관리할 수 있는 ‘내 책장’ 기능을 제공한다.
사용자가 직접 등록한 도서는 한 권 단위로 정리되어 표시되며,
해당 페이지에서는 다양한 조건에 따라 목록을 조회할 수 있다.
이 기능을 통해 사용자는 자신의 독서 기록을 목적에 맞게 정리하고 효율적으로 관리할 수 있다.

코드를 통해 흐름을 조금 더 정리해보자.
우선 내 책장 페이지의 Repository 계층을 보면
해당 쿼리에는 커서 페이지네이션에 필요한 ORDER BY, 커서 조건, LIMIT이 포함되어 있지 않다.
즉, DB 레벨에서는 단순 조회만 수행하고 있다.

반면 Service 계층을 살펴보면
정렬 기준(시간순/평점순 × 오름차순/내림차순)이 동적으로 변경된다는 이유로,
이 모든 로직을 애플리케이션 레벨에서 처리하고 있다.



그 결과 실제로는 10건만 필요한 요청임에도 불구하고,
사용자가 보유한 책 전체 데이터를 DB에서 모두 조회한 뒤
결국 정렬이 동적이라는 이유로 페이지네이션을 애플리케이션 계층으로 올려버리면서,
DB가 가장 잘할 수 있는 작업(정렬 + 제한)을 활용하지 못하고 있는 구조라고 볼 수 있다.
이 부분은 동적 정렬을 SQL 레벨에서 처리하도록 개선하면,
불필요한 전체 조회 없이 필요한 데이터만 효율적으로 가져올 수 있다고 생각했다.
우선 정렬, 필터링, LIMIT과 같은 작업을 애플리케이션이 아니라
데이터베이스 레벨에서 수행하도록 구조를 변경하는 것을 목표로 했다.
기존 쿼리는 GROUP BY를 통해 책 단위의 집계 결과를 생성하고 있었다.
이 과정에서 다음과 같은 집계 컬럼들이 만들어진다.
rating (max(br.rating))addedAt (max(pb.added_at))bookReadingStatus (array_agg)gatherings (json_agg)문제는 이러한 값들이 GROUP BY로 그룹이 만들어진 뒤,
집계 함수에 의해 계산되어 생성되는 컬럼이라는 점이다.
따라서 기존 구조에서는 이 값들을 WHERE 절에서 바로 활용할 수 없어,
페이지네이션이나 추가 필터링을 데이터베이스에서 처리하기 어려웠다.
그래서 집계 결과를 먼저 생성한 뒤,
그 결과를 기준으로 필터링과 페이지네이션을 수행하는 구조로 쿼리를 재구성하기로 했다.
이를 위해 CTE(Common Table Expression)를 사용하여 다음과 같이 쿼리를 분리했다.


이렇게 구조를 분리함으로써
집계 → 필터링 → 정렬 → 페이지네이션의 흐름을 명확하게 만들 수 있었고,
애플리케이션 레벨이 아니라 DB 레벨에서 데이터 양을 줄일 수 있게 되었다.
테스트 환경
우선 내 책장에 200권의 책을 등록한 사용자를 가정하여 테스트를 진행하였다.
초기 실행에서 발생할 수 있는 캐시 미적중이나 JVM 워밍업 등의 영향을 최소화하기 위해 5회의 웜업 실행을 먼저 수행하였다.
이후 동일한 요청을 10회 반복 실행하여 평균 응답 시간을 측정하였다.
또한 쿼리 변경 전후의 차이를 보다 명확히 확인하기 위해 데이터베이스에서 실제로 반환되는 row 수를 함께 출력하도록 구성하였다.

기존에는 데이터베이스에서 200개의 row를 모두 조회한 뒤 애플리케이션 레벨에서 필터링을 수행하고 있었다.
쿼리를 개선한 이후에는 필요한 11개의 row만 반환하도록 변경되었다.
그 결과 평균 응답 시간이 24.66ms → 17.99ms로 감소하였다.

데이터가 많아질수록 전체 조회 비용이 증가하기 때문에,
이러한 구조 개선의 효과는 더욱 커질 것으로 예상된다.
이번 개선의 핵심은 단순한 응답 시간 단축보다
애플리케이션 레벨에서 처리하던 필터링과 페이지네이션을
데이터베이스 레벨로 이동시켜 구조를 개선했다는 점에 있다.
그동안 성능 개선이라고 하면
응답 시간과 같은 수치적인 변화에만 집중하는 경향이 있었다.
하지만 이번 작업을 통해 좋은 코드는 단순한 성능 수치뿐 아니라
코드의 흐름과 구조를 개선하는 과정에서도 만들어질 수 있다는 점을 배울 수 있었다.