패스트캠퍼스 챌린지

패스트캠퍼스 환급 챌린지 35일차 : 9개 도메인 프로젝트로 끝내는 백엔드 웹 개발 (Java/Spring) 초격차 패키지 Online 강의 후기

ssoonearth 2025. 5. 5. 22:58

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

https://abit.ly/lisbva

 

 

 

패스트캠퍼스의 '9개 도메인 프로젝트로 끝내는 백엔드 웹 개발 (Java/Spring) 초격차 패키지 Online' 강의 수강 35일차!
오늘은 커뮤니티 피드 서비스의 admin 게시글 관리 페이지 구현 작업을 진행하였다.

 

강의에서는 더미 데이터 50만개를 추가한 상태로 진행하였다.

 

저번 강의에서 구현했던 유저 목록을 조회하는 작업에 거의 4초 가량이 소요되고 있다.

이렇게 데이터가 많을 때 조회 시간이 크게 늘어나는 문제의 원인은 offset에 있다.

기존의 쿼리는 50만개의 데이터를 가져온 후에 정렬하고, 거기에서 10개의 데이터를 offset과 limit을 기준으로 끊어오는 식으로 구성되어 있다.

데이터의 수가 적을 때는 큰 문제가 없지만 지금의 경우처럼 데이터가 많은 경우에는 전체 데이터(50만개)를 모두 가져온 후에 부가적인 작업을 수행하게 된다.

그렇기 때문에 데이터를 가지고 올 때 PK 클러스터 인덱스에 대한 접근이 많아지게 되는 것이다.

// 기존 코드
List<GetUserTableResponseDto> result = queryFactory
            .select(
                    Projections.fields(
                            GetUserTableResponseDto.class,
                            userEntity.id.as("id"),
                            userAuthEntity.email.as("email"),
                            userEntity.name.as("name"),
                            userAuthEntity.role.as("role"),
                            userEntity.regDt.as("createdAt"),
                            userEntity.updDt.as("updatedAt"),
                            userAuthEntity.lastLoginDt.as("lastLoginAt")
                    )
            )
            .from(userEntity)
            .join(userAuthEntity).on(userAuthEntity.userId.eq(userEntity.id))
            .where(likeName(dto.getName()))
            .orderBy(userEntity.id.desc())
            .offset(dto.getOffset())
            .fetch();

 

 

이러한 성능 문제를 해결하기 위해 커버링 인덱스를 사용할 것이다.

 

List<Long> ids = queryFactory
            .select(userEntity.id)
            .from(userEntity)
            .where(
                    likeName(dto.getName())
            ).orderBy(userEntity.id.desc())
            .offset(dto.getOffset())
            .limit(dto.getLimit())
            .fetch();

 

 

먼저 id 리스트를 조회하는 쿼리를 작성하였다.

커버링 인덱스로 id만 빠르게 가져오기 때문에 데이터가 많아지는 것에 대한 부하가 훨씬 줄어든다.

List<GetUserTableResponseDto> result = queryFactory
                .select(
                        Projections.fields(
                                GetTableListResponse.class,
                                userEntity.id.as("id"),
                                userAuthEntity.email.as("email"),
                                userEntity.name.as("name"),
                                userAuthEntity.role.as("role"),
                                userEntity.regDt.as("createdAt"),
                                userEntity.updDt.as("updatedAt"),
                                userAuthEntity.lastLoginDt.as("lastLoginAt")

                        )
                )
                .from(userEntity)
                .join(userAuthEntity).on(userAuthEntity.userId.eq(userEntity.id))
                .where(
                        userEntity.id.in(ids)
                )
                .orderBy(userEntity.id.desc())
                .fetch();

이후, 클러스터 인덱스에 접근하여 id 리스트에 해당하는 데이터를 가져오는 쿼리로 작성해주었다.

튜닝을 진행한 후 다시 실행해보았더니, 기존에는 3초대로 소요되던 시간이 1초대로 3배가 넘게 단축된 것을 확인할 수 있었다.

 

 

이것 외에도 더보기 형식을 사용하여 페이지 인덱스, 페이지 사이즈를 통한 페이징이 아닌, 마지막으로 본 id 기준으로 조회하는 커서 기반의 페이징 방식도 하나의 방법이다.

 

 

 

지금까지 많고 많은 조회 기능을 만들면서 이렇게 확실하게 성능을 향상시킨 적이 없었던 것 같다...

백엔드 개발자로서 데이터베이스에 대한 깊은 이해는 정말 필수적이라는 것을 다시 깨닫게 된다🥹