vennygovennygo
tech▾
로그인

Vennygo

개발을 중심으로 게임, 일상 등 다양한 분야의 경험과 인사이트를 기록하고 공유하는 공간입니다.

개인정보처리방침이용약관문의하기

© 2026 Vennygo. All rights reserved.

JPA

3가지로 끝내는 JPA 성능 최적화 방법

2026.04.21 09:30 · 6분 읽기 · 조회 1

3가지로 끝내는 JPA 성능 최적화 방법

JPA 쓰면서 성능이 오히려 안좋아진 경험, 여러분도 있나요? 저는 JPA를 처음 썼을때 한 프로젝트에서 수백 번의 쿼리가 동시에 터져 서버가 버벅댔거든요. 근데 생각보다 간단한 설정 몇 개 바꾸고, 전략 몇 가지만 알면 JPA 성능 확확 좋아질 수 있더라구요

이 글에선 JPA 성능 최적화 추천 방법으로 실제 현업에서 효과 본 노하우와 구체적인 코드를 콕콕 집어 알려드릴게요. 읽고 나면 쿼리 폭발 걱정은 끝!

왜 JPA가 느려지는 걸까?

JPA는 편하지만 내부 동작을 몰라서 어려움이 커진다

JPA는 객체 중심 데이터 접근을 쉽게 해줘요. 근데 내부에서는 SQL 쿼리가 어떻게 나가는지 보면 깜짝 놀랄 때가 많아요. 특히 연관관계 매핑 설정 실수하면 N+1 문제라는 지옥행 티켓이 발급되죠.

여러분도 한 번쯤 "왜 내 서비스가 느리지?" 하면서 로그 뒤적여 본 적 있을 거예요.

그 이유는 주로 다음과 같아요:

  • 즉시 로딩(LAZY/EAGER) 설정 오류
  • 불필요한 조회(fetch join 미사용)
  • 과하게 많은 데이터 한꺼번에 요청
  • 캐시 활용 부족

⚠️즉시 로딩을 잘못 쓰면 10명 게시글 조회하는데 1명당 추가 쿼리 10개씩 나오는 N+1 문제가 발생해요.

JPA 성능 최적화 추천 방법 첫 번째, 페치 전략 제대로 잡기

LAZY와 EAGER, 언제 써야 할까?

페치 전략은 연관 엔티티를 언제 불러올지 결정하는 열쇠예요. 기본은 LAZY! 무조건 LAZY로 하고, 필요한 순간에만 fetch join으로 직접 가져오기.

아래는 LAZY로 설정하고 fetch join으로 효율적으로 데이터를 가져오는 코드예요.

@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;
    // 기타 필드 및 메서드
}

// Repository 메서드 예시: fetch join 사용하기
public List<Order> findAllWithMember() {
    return em.createQuery(
       "select o from Order o join fetch o.member", Order.class)
       .getResultList();
}

이렇게 하면 회원 정보를 주문과 함께 한방에 가져와서 N+1 문제 해결!

💡fetch join은 꼭 필요할 때만, 너무 남발하면 오히려 복잡해지고 메모리 낭비됩니다

두 번째, 쿼리 수 줄이는 DTO 직접 조회 방법

엔티티 다 필요 없을 때 굳이 다 가져오나?

사실 화면에 필요한 데이터만 딱 가져오는 게 가장 빠른 방법이에요. 복잡한 엔티티 대신 DTO를 바로 조회해서 네트워크 비용 절감하는 거죠. 아래처럼 JPQL 생성자 표현식을 써서 필요한 데이터만 쏙 빼올 수 있어요.

public List<OrderSimpleDto> findOrderDtos() {
    return em.createQuery(
        "select new com.example.OrderSimpleDto(o.id, m.name, o.orderDate) " +
        "from Order o join o.member m", OrderSimpleDto.class)
        .getResultList();
}

OrderSimpleDto 클래스는 필요한 필드와 생성자를 갖추고 있어야겠죠?

💡DTO 조회는 꼭 필요한 필드를 골라서 응답 속도와 트래픽 줄이기에 대박!

세 번째, 2차 캐시와 배치 사이즈 활용하기

DB 부하 줄이고 싶다면 캐시, 배치 사이즈 외면 못한다

JPA 제공 2차 캐시는 같은 엔티티 반복 조회 시 DB hit 없이 메모리에서 바로 반환돼 속도를 크게 높인답니다. 그리고 batch_size 옵션을 통해 IN 절 처리할 때 묶어서 보내 DB 통신 횟수를 감소시키기도 하죠. 아래 설정 예 참고하세요.

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
spring.jpa.properties.hibernate.default_batch_fetch_size=100

설정 후 아래처럼 컬렉션이나 연관 관계에도 배치 사이즈 지정 가능해요:

@OneToMany(mappedBy = "order")
@BatchSize(size = 100)
private List<OrderItem> orderItems;

적절한 캐시 정책과 배치 사이즈 조합으로 DB 부담 확 줄일 수 있어요.

⚠️캐시는 무조건 좋다고 생각하면 안 돼요. 일관성 유지 신경 쓰면서 신중히 적용하셔야 합니다!

마지막으로, JPA 성능 최적화 추천 방법 중 꼭 기억할 점들?

  • 불필요한 즉시 로딩은 금물! 기본 LAZY + 꼭 필요한 곳 fetch join 활용
  • DTO 직접 조회로 네트워크 비용 최소화하기
  • 2차 캐시 활성화와 적절한 배치 사이즈 조절로 DB 부하 분산하기

image

제가 실제 겪으면서 얻은 교훈은.. JPA가 편리하다고 막쓰면 무조건 병목 현상이 올 수 밖에 없더라구요

저렇게 개선하고 나서는 평균 쿼리 시간이 40% 넘게 줄었었습니다!

#Jpa#최적화

관련 글

    이전 글[Java] REST API 란? 쉽게 시작하기
    다음 글: 없음

    댓글

    불러오는 중…