test

컴개론·2024년 8월 2일

이슈 정의

JPA를 사용하다 보면 종종 N+1 문제를 마주하게 된다. 이는 데이터베이스 쿼리 성능에 큰 영향을 미칠 수 있는 문제로, 특히 대량의 데이터를 다룰 때 심각한 성능 저하를 초래할 수 있다. 이번 블로그 포스트에서는 JPA N+1 문제의 원인과 이를 해결하는 방법에 대해 알아보겠다.

issue example code

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = 'author')
    private List<Book> books = new ArrayList<>();
}

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @ManyToOne
    @JoinColumn(name = 'author_id')
    private Author author;
}

public List<Author> findAllAuthors() {
    return entityManager.createQuery('SELECT a FROM Author a', Author.class).getResultList();
}

위 코드에서 findAllAuthors 메서드는 모든 저자를 조회하는 쿼리를 실행한다. 하지만 각 저자에 대해 책 목록을 조회할 때 추가적인 쿼리가 실행되며, 이는 N+1 문제를 야기한다.

원인 추론

N+1 문제는 기본적으로 지연 로딩(Lazy Loading)으로 인해 발생한다. Author 엔티티를 조회할 때 books 필드는 지연 로딩으로 설정되어 있어, 실제로 접근할 때마다 추가적인 쿼리가 실행된다. 이로 인해 저자 수가 N명이라면, N개의 추가 쿼리가 실행되어 총 N+1개의 쿼리가 발생하게 된다.

해결 방법

N+1 문제를 해결하기 위해서는 조인을 사용하여 한 번의 쿼리로 필요한 데이터를 모두 가져오는 것이 좋다. JPA에서는 fetch join을 사용하여 이를 해결할 수 있다.

solution example code

public List<Author> findAllAuthorsWithBooks() {
    return entityManager.createQuery(
        'SELECT a FROM Author a JOIN FETCH a.books', Author.class)
        .getResultList();
}

위 코드에서는 JOIN FETCH를 사용하여 저자와 책을 한 번의 쿼리로 가져오도록 수정하였다. 이를 통해 N+1 문제를 해결할 수 있다.

공식 문서에서도 fetch join을 사용하여 N+1 문제를 해결하는 방법을 설명하고 있다. 자세한 내용은 [Hibernate 공식 문서](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html# fetching-fetch-join)를 참고하길 바란다.

이와 같이 JPA N+1 문제를 해결하면 데이터베이스 쿼리 성능을 크게 향상시킬 수 있다. 앞으로 JPA를 사용할 때 N+1 문제를 주의 깊게 살펴보고, 필요할 때 fetch join을 활용하여 최적화된 쿼리를 작성하길 바란다.

0개의 댓글