Pink Spider/JPA 사용시 N+1 이슈에 대하여

Created Wed, 23 Apr 2025 20:48:45 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
611 Words 3 min

JPA에서 N+1 문제는 성능 저하를 유발할 수 있는 대표적인 ORM 사용 시의 이슈 중 하나입니다. 아래에 자세히 설명드릴게요.


N+1 문제란?

N+1 문제는 JPA에서 연관된 엔티티를 지연 로딩(LAZY loading) 방식으로 조회할 때 발생합니다.

예를 들어, 다음과 같은 상황을 가정해 봅시다:

  • 게시글(Post)과 작성자(Member)는 다대일(N:1) 관계이다.
  • 게시글 1개는 작성자 1명을 가진다.
@Entity
public class Post {
    @ManyToOne(fetch = FetchType.LAZY)
    private Member author;
}

게시글 리스트를 조회하는 쿼리를 날리면:

List<Post> posts = postRepository.findAll();

이때 JPA는 다음과 같은 방식으로 작동합니다:

  1. 1번의 쿼리posts를 전부 조회합니다. (이게 1)
  2. 각각의 post.getAuthor()가 호출될 때마다 작성자(Member)를 별도 쿼리로 조회합니다. 게시글이 N개라면 작성자 쿼리가 N번 나감. (이게 N)

즉, 총 1 + N번의 쿼리가 실행되며, 이것이 바로 N+1 문제입니다.


왜 문제가 될까?

  • 게시글 수가 적을 때는 괜찮지만, 수천 건이 되면 데이터베이스에 엄청난 부하가 발생합니다.
  • 의도하지 않은 대량 쿼리로 성능 병목을 유발합니다.

해결 방법

1. Fetch Join 사용

@Query("SELECT p FROM Post p JOIN FETCH p.author")
List<Post> findAllWithAuthor();

JPA의 Fetch Join은 연관된 엔티티를 한 번의 쿼리로 함께 가져오게 해줍니다.

2. EntityGraph 사용

@EntityGraph(attributePaths = {"author"})
List<Post> findAll();

3. Batch Size 설정

JPA 구현체(Hibernate 등)에서 @BatchSize를 설정하면 지연 로딩 시 연관된 엔티티를 IN 쿼리로 묶어서 가져올 수 있습니다.

@BatchSize(size = 10)
@ManyToOne(fetch = FetchType.LAZY)
private Member author;

정리

방식 장점 단점
Fetch Join 쿼리 수 최소화 조인 수가 많으면 복잡해짐
EntityGraph 선언적 방식 복잡한 그래프에서는 한계
BatchSize 비교적 간단 메모리 사용량 증가 가능