Programming/Spring

[Spring] JPA에서 N+1이 발생하는 이유

_채채_ 2025. 9. 24. 09:59
JPA에서 발생하는 N+1 문제는 연관된 엔티티를 조회할 때 발생하는 성능 문제이다.

발생 원인

: 간단히 말하자면 JPA의 연관관계 처리 방식 때문에 발생한다고 할 수 있다. 

즉 지연로딩(LAZY)전략으로 연관 관계가 설정된 엔티티를 조회할 때, 각각의 조회 시도에 개별적으로 추가 조회가 발생하기 때문이다.

 

예를 들어, 회원(Member)과 주문 (Order)이 1:N 관계일 때, 회원만을 조회한다면, 이후 각 Member의 orders를 조회할 때 추가 쿼리가 발생한다.

이러한 경우, 

  ➡️ 첫 번째 쿼리:  회원 전체 조회 -> 1

  ➡️ 두 번째 쿼리: 각 회원마다 주문 조회 -> N

  ➡️ 총 1 + N번의 쿼리가 발생하여 문제가 됨

해결방안

1) Fetch Join을 사용한다.

JPQL에서 JOIN FETCH 구문을 사용하면, 연관된 엔티티를 한 번에 조회하는데 이건 SQL의 JOIN처럼 사용하지만, JPA가 연관 엔티티까지 한 번에 가져오게 오도록 한다. 즉, 한 번의 쿼리로 연관 객체까지 즉시 로드할 수 있다.

 

2) EntityGraph를 사용한다.

JPA에서 제공하는 @EntityGraph는 쿼리를 굳이 작성하지 않고도 fetch 전략을 재정의할 수 있는 방법이다.

특정 쿼리에만 Eager처럼 작동하게 변경이 가능하고 JPQL없이 선언적으로 연관 엔티티를 조회할 수도 있다. 이 방법은 다양한 쿼리에서 재사용이 가능하다는 장점이 있고 페이징 시에도 사용할 수 있다.

 

3) 쿼리를 최적화한다.(DTO를 직접 조회하는 방향으로)

필요한 데이터만 뽑아서 바로 DTO에 매핑하여 조회하게 하는 방법이다. N+1 문제가 애초에 발생하지 않도록 쿼리를 설계하는 것이다.

public class MemberOrderDto {
    private String memberName;
    private String orderName;

    public MemberOrderDto(String memberName, String orderName) {
        this.memberName = memberName;
        this.orderName = orderName;
    }
}

String jpql = "SELECT new com.example.MemberOrderDto(m.name, o.name) " +
              "FROM Member m JOIN m.orders o";
List<MemberOrderDto> result = em.createQuery(jpql, MemberOrderDto.class)
                                .getResultList();

즉 이런식으로 MemberOrderDto를 따로 만들어서 불필요한 데이터 로딩 없이 필요한 컬럼만 조회 가능하도록 하는 것이다.

 

방법 특징 장점 단점
Fetch Join JPQL에서 연관 엔티티를 함께 조회 간단하고 직관적 컬렉션(@OneToMany..) 조인 시 페이징 불가
EntityGraph 애노테이션으로 Fetch Join 지정 선언적이고 유연함 설정 이해가 필요, 단순 연관관계에서는 잘 동작하나 컬렉션에서는 성능 저하 발생 가능성이 높음
DTO 직접 조회 필요한 필드만 조회해 DTO로 반환 성능 효율적 코드 양 증가, 유지보수성이 떨어질 수 있음

 

 

방법 필요한 상황
Fetch Join 연관된 엔티티를 필수적으로 함께 조회햐아 하는 경우
단건 조회 또는 리스트 조회이지만 페이징이 필요 없는 경우

ex) 게시글 목록 조회 시 작성자 정보를 같이 보고 싶을 때
EntityGraph Spring Data JPA를 사용중일 경우
Fetch 전략을 선언적으로 지정하고 싶은 경우
페이징을 하면서 연관 엔티티를 조회해야 할 경우

ex) 유저 목록을 페이지 단위로 보여주면서 프로필 사진도 같이 가져와야 할 때
DTO 직접 조회 화면에 보여줄 데이터가 명확할 경우
엔티티 전체가 아니라 몇 개의 필드만 필요할 경우
복잡한 화면 구성에서 여러 테이블의 조인이 필요할 경우

ex) 주문 목록 화면에서 주문자의 이름, 상품명, 주문일자만 보여줄 때

 

'Programming > Spring' 카테고리의 다른 글

[Spring] Batch작업이란?  (0) 2025.11.19
[Spring]@Cacheable, @CachePut, @CacheEvict  (0) 2025.10.20
[Spring] JWT의 구조  (0) 2025.10.01
[Spring] Spring Framwork의 탄생 배경  (0) 2025.07.16