"오늘의 문제를, 내일의 기록으로 남깁니다."

막연한 이론보다, 구체적인 코드가 필요할 때. 직접 겪고 해결한 문제들을 기록합니다. 실무에서 부딪히는 진짜 이슈와, 내가 이해한 방식 그대로 정리한 가이드입니다.

웹개발/Spring

Spring Boot에서 JPA와 Querydsl 함께 사용하는 방법 (설정부터 실전 예제까지)

자바를잡아 2025. 8. 14. 07:30
반응형

Spring Boot에서 JPA와 Querydsl 함께 사용하는 방법 (설정부터 실전 예제까지)

Spring Data JPA는 빠르게 CRUD 기능을 구현할 수 있는 장점이 있지만, 복잡한 동적 쿼리 작성에는 한계가 있습니다.
이때 실무에서 많이 활용하는 것이 Querydsl입니다. 정적 타입 기반으로 쿼리를 작성할 수 있어, 컴파일 타임 오류를 줄이고 유지보수가 편해집니다.

1. 왜 JPA + Querydsl 조합이 필요한가?

  • @Query 또는 JPQL로는 복잡한 조건 처리에 한계가 있음
  • Querydsl은 정적 타입 기반으로 IDE 자동완성이 가능
  • 복잡한 조건 쿼리, 동적 검색, 페이징 처리에 강력함

2. build.gradle.kts 설정 (Querydsl 의존성 추가)


dependencies {
    implementation("com.querydsl:querydsl-jpa")
    kapt("com.querydsl:querydsl-apt")
}

kapt {
    arguments {
        arg("querydsl.entityAccessors", "true")
    }
}

sourceSets {
    main {
        java {
            srcDirs("build/generated/querydsl")
        }
    }
}

Gradle 빌드 시 Q타입 클래스가 생성됩니다. 예: QMember

3. Entity 예제


@Entity
@Getter
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
    private int age;
}

4. 사용자 정의 Repository 인터페이스


public interface MemberRepositoryCustom {
    List<Member> search(String username, Integer age);
}

5. 사용자 정의 Repository 구현 클래스


@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    @Override
    public List<Member> search(String username, Integer age) {
        QMember m = QMember.member;

        return queryFactory.selectFrom(m)
                .where(
                    username != null ? m.username.eq(username) : null,
                    age != null ? m.age.eq(age) : null
                )
                .fetch();
    }
}

6. Spring Data JpaRepository에서 상속


public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
}

7. JPAQueryFactory Bean 등록


@Configuration
public class QuerydslConfig {

    @PersistenceContext
    private EntityManager em;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

8. 테스트 예시


@Autowired
MemberRepository memberRepository;

@Test
void 검색테스트() {
    List<Member> result = memberRepository.search("홍길동", 30);
    assertThat(result).isNotEmpty();
}

9. 주의할 점

  • Q클래스 생성 누락: Gradle 빌드 후 build/generated 확인
  • Spring Boot 3.x에서는 JPAQueryFactory 설정이 조금 다를 수 있음
  • Querydsl의 null 무시 처리는 자동 적용되므로, where에 바로 조건 넣으면 됨

10. 실무 팁

  • 검색 조건이 많아질 경우 BooleanBuilder 또는 Predicate 활용
  • DTO 매핑 시 Projections.constructor() 또는 fields() 사용
  • 단순 CRUD는 JPA, 복잡한 쿼리는 Querydsl로 역할을 분리하는 것이 좋음

맺음말

JPA만으로 모든 쿼리를 처리하려다 보면 한계에 부딪히게 됩니다.
Querydsl을 함께 사용하면 타입 안정성까지 갖춘 복잡한 쿼리를 손쉽게 작성할 수 있고, 생산성과 유지보수성 모두 잡을 수 있습니다.

오늘 소개한 구성은 실무에서도 가장 흔히 쓰이는 구조이니, 직접 적용해보시길 추천드립니다.

반응형