BackEND/Java

QueryDSL과 JPA 성능 튜닝 전략

mingmingIT 2025. 4. 4. 10:07

JPA와 QueryDSL을 활용할 때 성능 최적화는 필수적인 요소입니다. 잘못된 설정이나 비효율적인 쿼리는 성능 저하를 초래할 수 있습니다. 이번 포스팅에서는 QueryDSL과 JPA의 성능 튜닝 전략을 소개합니다.


1. Lazy Loading과 Fetch Join 최적화

1) Lazy Loading 기본 설정

JPA에서는 연관된 엔티티를 로딩할 때 LAZY 전략을 기본으로 설정하는 것이 성능 최적화에 유리합니다.

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 설정
    private Team team;
}

2) Fetch Join 활용

Lazy Loading으로 설정하면 N+1 문제가 발생할 수 있습니다. 이를 해결하기 위해 Fetch Join을 사용할 수 있습니다.

List<Member> members = queryFactory
    .selectFrom(member)
    .join(member.team, team).fetchJoin()
    .fetch();

💡 Fetch Join을 사용하면 연관된 엔티티를 한 번의 쿼리로 가져올 수 있습니다.


2. Batch Size 설정을 통한 최적화

1) @BatchSize 어노테이션 사용

Batch Fetch Size를 설정하면 연관된 엔티티를 IN 절을 활용하여 한 번에 가져올 수 있습니다.

@Entity
public class Team {
    @OneToMany(mappedBy = "team")
    @BatchSize(size = 10)
    private List<Member> members;
}

2) 글로벌 설정 적용

spring.jpa.properties.hibernate.default_batch_fetch_size=100

💡 BatchSize 설정을 활용하면 N+1 문제를 줄일 수 있습니다.


3. 인덱스 활용 및 DTO 조회 최적화

1) 인덱스 설정

컬럼에 인덱스를 추가하면 검색 속도를 향상시킬 수 있습니다.

@Entity
@Table(indexes = {@Index(name = "idx_member_name", columnList = "name")})
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

2) DTO 조회 최적화

엔티티가 아닌 DTO를 사용하여 필요한 데이터만 조회하면 성능을 개선할 수 있습니다.

List<MemberDto> result = queryFactory
    .select(Projections.constructor(MemberDto.class,
            member.name,
            member.age))
    .from(member)
    .fetch();

💡 DTO 조회를 활용하면 불필요한 데이터를 로딩하지 않도록 최적화할 수 있습니다.


4. 페이징 최적화 및 캐싱 적용

1) 페이징 쿼리 최적화

fetch()count()를 분리하여 성능을 개선할 수 있습니다.

JPAQuery<Member> query = queryFactory.selectFrom(member);

List<Member> content = query
    .offset(0)
    .limit(10)
    .fetch();

long total = queryFactory
    .select(member.count())
    .from(member)
    .fetchOne();

2) Redis 캐싱 적용

반복적인 조회는 Redis를 활용하여 캐싱할 수 있습니다.

@Cacheable(value = "memberCache", key = "#id")
public Member findMember(Long id) {
    return memberRepository.findById(id).orElse(null);
}

💡 페이징 및 캐싱을 적용하면 성능을 더욱 개선할 수 있습니다.


결론

QueryDSL과 JPA를 사용할 때 성능을 최적화하는 방법은 다음과 같습니다.

Lazy Loading을 기본으로 설정하고 Fetch Join 활용
Batch Fetch Size 설정으로 N+1 문제 해결
인덱스를 활용하여 검색 속도 개선
DTO 조회를 통해 불필요한 데이터 로딩 방지
페이징 최적화 및 캐싱을 활용한 성능 개선