글로벌 페치 전략 - 여전히 N+1?

PGD·5일 전
0

JPA를 사용할 때 가장 주의해야 할 N+1 문제

JPA의 훌륭한 점 중 하나인 Object Graph를 보장한다는 점은, 주의를 기울여 사용하지 않으면 성능에 치명적인 부하를 줍니다. JPA를 사용하면서 가장 주의를 기울여야 할 성능 이슈는 N+1 문제입니다. N+1 문제가 뭔지는 유명하니 따로 언급을 하지 않고...

N+1 문제를 해결하는 방법은 여러가지가 있습니다.

  1. 글로벌 페치 전략을 설정하거나
  2. 페치 조인을 사용하거나
  3. 기타 등등...

여기서 언급하고 싶은 점은 글로벌 페치 전략이 N+1 문제를 제대로 해결해 주지 않는다는 점입니다.

FetchType.EAGER의 허점

글로벌 페치 전략이란? 연관관계 매핑된 엔티티에 대한 로딩 전략을 글로벌하게 설정하는 것입니다.

@Entity
public class Member {
    ...

    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
    private List<Order> orders = new ArrayList<>();
    
    ...
}

이렇게 하면 Member를 조회하는 시점에 Order 리스트도 같이 로딩되어 N+1 문제가 발생하지 않....지 않습니다.
Member 엔티티를 ID로 조회할 때는 N+1 문제가 발생하지 않지요.

EntityManager em = getEntityManager();
em.find(Member.class, memberId);

이에 대한 위 코드 실행 시 발생하는 쿼리는 다음과 같습니다.

Hibernate: 
    select
        m1_0.id,
        m1_0.member_name,
        o1_0.member_id,
        o1_0.id 
    from
        members m1_0 
    left join
        orders o1_0 
            on m1_0.id=o1_0.member_id 
    where
        m1_0.id=?

한 번의 쿼리로 Member와 Order 엔티티 둘 다 가져오므로 썩 괜찮다고 볼 수 있습니다.

문제는 JPQL을 사용할 때 나타납니다.

다음 코드가 있습니다.

EntityManager em = getEntityManager();

Member findMember = em.createQuery("SELECT m FROM Member m WHERE m.id = :id", Member.class)
        .setParameter("id", member.getId())
        .getSingleResult();
System.out.println(findMember.getMemberName());
findMember.getOrders()
        .forEach((o) -> System.out.println(o.getId()));

그러면 쿼리는 다음과 같이 나타납니다.

Hibernate: 
    select
        m1_0.id,
        m1_0.member_name 
    from
        members m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        o1_0.member_id,
        o1_0.id 
    from
        orders o1_0 
    where
        o1_0.member_id=?

쿼리가 두 번 발생하는 것을 볼 수 있습니다. 글로벌 페치 전략을 Eager Fetch로 설정한다고 하더라도 N+1 문제가 해결되지 않습니다.

JPA가 JPQL을 분석해서 SQL을 생성할 때 글로벌 페치 전략을 참고하지 않고 오직 JPQL 자체만 사용합니다.

다양한 N+1 문제 해결 전략을 알아야 한다.

이 짧은 글의 결론은 이렇습니다. JPA는 개발자에게 주어진 축복임은 틀림없지만 편리한 만큼 고려해야 할 사항이 많습니다. JPA를 제대로 활용하지 못한다면 JPA는 재앙에 지나지 않습니다. 특히 JPA를 3-layer 아키텍처에서 활용한다든지, 헥사고날 아키텍처에서 활용한다든지 할 때 JPA를 비즈니스 로직으로부터 분리시켜야 한다는 아키텍처 철학이 JPA 활용 난이도를 더욱 가중시킬 수 있습니다. 그래서 JPA를 수박 겉 핥기식이 아닌 제대로 알고, 멋지게 활용할 수 있는 방법에 대해서 끊임없이 고민해야 합니다.

profile
student

0개의 댓글