java

Spring Data JPA 커스텀 쿼리 활용법

개발에대해 2025. 10. 7. 09:00
반응형

 

Spring Data JPA 커스텀 쿼리 활용법

Spring Data JPA는 기본적인 CRUD 기능을 제공하지만, 실제 프로젝트에서는 단순한 메서드 이름 기반 쿼리만으로는 복잡한 조회 요구사항을 모두 처리하기 어렵습니다. 이때 필요한 것이 커스텀 쿼리(Custom Query)입니다.

 

본 글에서는 @Query 애노테이션, 네이티브 쿼리, Querydsl 등 실무에서 자주 사용하는 방법과 최적화 팁을 정리합니다.

 

1. @Query를 활용한 JPQL 커스텀 쿼리

Spring Data JPA에서는 @Query 애노테이션을 사용하여 JPQL(Java Persistence Query Language)로 직접 쿼리를 정의할 수 있습니다.

 

예를 들어 회원(Member) 엔티티에서 이름으로 조회하는 경우:

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m from Member m where m.name = :name")
    List<Member> findByNameCustom(@Param("name") String name);
}
    

 

이 방식의 장점은 JPQL 문법을 그대로 활용하면서도 Repository 메서드로 쉽게 호출할 수 있다는 점입니다.

또한, 필요한 컬럼만 선택(select)하여 조회하거나, 조인(join)을 활용해 복잡한 조회도 가능합니다.

 

2. 네이티브 쿼리 활용

JPQL로 처리하기 어려운 복잡한 SQL은 네이티브 쿼리를 사용하면 됩니다.

@Query에서 nativeQuery = true 옵션을 주면 실제 SQL을 그대로 실행할 수 있습니다.

@Query(value = "SELECT * FROM member WHERE name LIKE %:name%", nativeQuery = true)
List<Member> findByNameNative(@Param("name") String name);
    

 

단, 네이티브 쿼리는 DB 종속적이므로 다른 DB로 이전 시 호환성 문제가 발생할 수 있습니다.

따라서 가급적 JPQL과 Repository 메서드를 우선 사용하고, 성능 최적화가 필요할 때 네이티브 쿼리를 활용하는 것이 좋습니다.

 

3. Querydsl을 이용한 동적 쿼리

복잡한 조건과 동적 필터링이 필요한 경우, Querydsl을 사용하는 것이 효율적입니다.

Querydsl은 타입 안전(Type-safe) 쿼리를 제공하며, 코드 자동 완성 기능을 활용할 수 있습니다.

QMember member = QMember.member;

List<Member> result = queryFactory.selectFrom(member)
    .where(member.name.eq("홍길동")
           .and(member.age.gt(20)))
    .fetch();
    

 

Querydsl을 사용하면 조건이 복잡해져도 문자열 기반 쿼리보다 가독성이 높고, 컴파일 시 타입 체크가 가능해 안정적입니다.

 

4. 커스텀 리포지토리 활용

Repository 인터페이스만으로는 처리하기 어려운 복잡한 로직은 커스텀 리포지토리를 만들어 구현할 수 있습니다.

구현체를 작성하고 Repository와 연결하면, 특정 비즈니스 로직을 깔끔하게 분리할 수 있습니다.

// Repository 인터페이스
public interface MemberRepositoryCustom {
    List<Member> findMembersWithSpecialCondition();
}

// 구현체
public class MemberRepositoryImpl implements MemberRepositoryCustom {
    @PersistenceContext
    private EntityManager em;

    @Override
    public List<Member> findMembersWithSpecialCondition() {
        String jpql = "select m from Member m where m.status = 'ACTIVE'";
        return em.createQuery(jpql, Member.class).getResultList();
    }
}
    

5. 성능 최적화 팁

  • 필요한 컬럼만 선택하여 조회(select) → 불필요한 데이터 로딩 방지
  • 조인(fetch join) 활용 → N+1 문제 방지
  • Pageable 사용 → 대량 데이터 조회 시 메모리 부담 감소
  • 캐싱 활용 → 자주 조회되는 데이터는 2차 캐시나 Redis 캐시 적용
반응형