DB

CaseBuilder 대신 BooleanBuilder를 써야 했던 이유(Feat: querydsl)

수수한 인간 2025. 7. 27. 23:02

들어가면서

최근 QueryDSL로 조회 쿼리에서 응답 속도가 비정상적으로 느려지는 문제를 경험했습니다.
원인을 추적해보니, 문제의 핵심은 where 절에 사용한 CaseBuilder였습니다.

status 컬럼을 기준으로 조건 분기를 작성했는데, 특정 상태값일 때만 조회가 유독 느렸습니다.
이후 BooleanBuilder로 로직을 변경한 결과, 쿼리 속도가 수 초에서 수백 밀리초로 개선되었습니다.


사용했던 상황

비슷한 상황으로 다음과 같은 예제를 만들었습니다.
게시글의 상태(status)가 DELETED, PENDING, ACTIVE 등의 값으로 존재하는 게시판 시스템이 있다고 가정해보겠습니다.
A 사용자(예: 관리자)status = 'PENDING' 상태의 게시글을 그대로 보아야 하고,
B 사용자(예: 일반 직원)에게는 같은 게시글이 삭제된 상태로 보여야 하는 상황입니다.

즉, 같은 데이터라도 사용자에 따라 상태값이 다르게 해석되어야 하고,
그 상태값을 기준으로 조회 조건을 걸어야 하는 상황이었습니다.
이 때문에 where절 안에서 CaseBuilder를 사용해 사용자별 상태를 가공한 후, 원하는 조건으로 비교했습니다.


기존 코드

queryFactory.selectFrom(post)
    .where(
        new CaseBuilder()
            .when(currentUser.role.eq("ADMIN").and(post.status.eq("PENDING")))
                .then("PENDING")
            .when(currentUser.role.eq("USER").and(post.status.eq("PENDING")))
                .then("DELETED")
            .otherwise(post.status)
            .eq("DELETED") // 일반 사용자 기준으로 삭제 상태만 조회
    )
    .fetch();

하지만 이 코드는 생각보다 심각한 성능 저하를 유발했습니다.
특히 특정 상태값이 대량으로 존재하는 경우, 조회 속도가 수 초까지 늘어나는 현상이 발생했습니다.
정확한 원인을 완전히 파악하진 못했지만, 다음과 같은 두 가지 이유로 인해 성능 저하가 발생한 것으로 추정됩니다.

  1. where 절에서 CASE WHEN을 사용해 status 컬럼을 가공하니 인덱스를 사용할 수 없습니다.
  2. ELSE에 해당하는 조건이 대량의 row를 포함하고 있어 풀스캔 + 대량 필터링이 일어났습니다.

개선된 코드

BooleanBuilder statusBooleanBuilder = new BooleanBuilder();
if (currentUser.getRole().equals("ADMIN")) {
    // 관리자: 보류 상태 조회
    statusBooleanBuilder.and(post.status.eq("PENDING"));
} else {
    // 일반 사용자: 삭제 상태로 보여야함
    statusBooleanBuilder.and(post.status.eq("DELETED"));
}

queryFactory.selectFrom(post)
    .where(statusBooleanBuilder)
    .fetch();

CaseBuilder를 이용하여 간결하게 표현할 수 있지만
컬럼이 가공되는 순간 인덱스를 사용할 수 없습니다.

복잡한 조건 분기도 BooleanBuilder.or(), .and() 메서드를 활용해 충분히 유연하게 처리할 수 있습니다.
해당 방식은 정상적으로 인덱스를 탈 수 있습니다.


마치며

CaseBuilder는 SELECT 절에서 값 가공 시에 유용하게 사용할 수 있습니다.
WHERE 절에 CaseBuilder를 사용하는 경우, 성능 문제가 발생할 수 있으므로 가능한 한 BooleanBuilder로의 전환을 추천드립니다.
특히 Where에 컬럼을 가공하는 경우(타입변환, case when문, 문자열 가공등) 인덱스를 타지 않아서 쿼리 속도 저하로 이어질 수 있습니다.