배너 이미지

N+1 문제

2025. 4. 21. 20:15·CS공부/기타 CS지식

스프링 부트 기반의 애플리케이션에서 자주 발생하는
N+1 쿼리 문제의 개념과 원인, 그리고 이를 해결하기 위한 실용적인 전략들을 정리하였다.
특히 JPA(Hibernate)와 연관된 엔티티 조회 시 발생할 수 있는 성능 이슈 중심으로 설명되었다.


1. N+1 문제란?

N+1 문제는 다음과 같은 상황에서 발생한다:

  1. 1번의 쿼리로 N개의 엔티티를 조회한 뒤,
  2. 각 엔티티에 연관된 데이터를 조회할 때 추가로 N번의 쿼리가 실행되는 현상

즉, 총 1 + N번의 SQL 쿼리가 실행되며, 이는 대규모 데이터 처리 시 심각한 성능 저하를 초래할 수 있다.

예시 (JPA 기준):

List<Post> posts = postRepository.findAll(); // 1번 쿼리
for (Post post : posts) {
    post.getComments().size(); // N번 쿼리 발생
}

이 경우, 각 Post마다 연관된 Comment를 지연 로딩(LAZY) 방식으로 가져오기 때문에
N개의 추가 쿼리가 발생한다.


2. 발생 원인

  • JPA의 기본 연관관계 로딩 전략은 @ManyToOne, @OneToMany 관계에서 기본적으로 지연 로딩(LAZY)이다.
  • 엔티티 컬렉션을 반복하면서 연관 객체를 호출할 경우, 프록시가 초기화되며 개별 쿼리가 실행된다.
  • SQL 수준에서 보면, SELECT N+1 쿼리가 순차적으로 발생한다.

3. 해결 방법

✅ 1) 페치 조인(Fetch Join) 사용

JPA에서는 연관된 엔티티를 한 번의 쿼리로 함께 조회할 수 있도록 JOIN FETCH 구문을 사용할 수 있다.

@Query("SELECT p FROM Post p JOIN FETCH p.comments")
List<Post> findAllWithComments();
  • 연관된 Comment 엔티티도 함께 조회되어 N번의 추가 쿼리 없이 처리됨
  • 다만, 페치 조인은 다대일 관계에서는 안전하지만, 일대다에서는 중복 데이터 주의가 필요하다

✅ 2) EntityGraph 사용

JPA 2.1부터 지원되는 @EntityGraph를 활용하면, JPQL 없이도 연관 엔티티를 함께 로딩할 수 있다.

@EntityGraph(attributePaths = "comments")
List<Post> findAll();
  • 선언적이고 재사용 가능한 방식
  • 페치 조인보다 유지보수에 유리할 수 있음

✅ 3) DTO로 직접 조회

JPQL이나 QueryDSL 등을 활용해 필요한 데이터만 DTO로 직접 조회하는 방식도 대안이 될 수 있다.

@Query("SELECT new com.example.dto.PostWithCommentDto(p.id, p.title, c.content) " +
       "FROM Post p JOIN p.comments c")
List<PostWithCommentDto> findAllPostWithComment();
  • 필요한 필드만 선택적으로 가져올 수 있어 성능상 유리함
  • 엔티티와의 결합도를 낮출 수 있음

4. 개발 시 체크리스트

  • 연관관계는 항상 지연 로딩을 기본으로 설정하고, 필요한 곳만 페치 조인 사용
  • 실무에서는 반드시 쿼리 로그 확인 (spring.jpa.show-sql=true, Hibernate format 설정 등)
  • 페이징 처리 시에는 컬렉션 페치 조인 금지 (Pageable과 @Query 조합 필요)
  • 복잡한 조회는 DTO 변환 또는 QueryDSL로 대체

학습 정리

N+1 문제는 ORM을 사용할 때 매우 흔하게 발생하는 성능 이슈이며,
특히 무심코 작성한 코드가 수백 수천 개의 쿼리를 유발할 수 있다는 점에서 주의가 필요하다.
스프링 부트 환경에서는 JPA의 Fetch Join, EntityGraph, DTO 직접 조회 등 다양한 방식으로 이를 해결할 수 있으며,
쿼리 구조와 로딩 전략을 이해하는 것이 핵심이다.

'CS공부 > 기타 CS지식' 카테고리의 다른 글

락(Lock)의 종류와 적용 전략: Optimistic vs Pessimistic  (0) 2025.05.14
해싱 보안 이슈 및 강화 가이드  (1) 2025.04.26
SK텔레콤 유짐정보 해킹, BPFdoor 악성코드 사건 정리  (3) 2025.04.26
데드락(Deadlock)  (0) 2025.04.21
BASE 64  (0) 2025.04.21
'CS공부/기타 CS지식' 카테고리의 다른 글
  • 해싱 보안 이슈 및 강화 가이드
  • SK텔레콤 유짐정보 해킹, BPFdoor 악성코드 사건 정리
  • 데드락(Deadlock)
  • BASE 64
quokkaST
quokkaST
  • quokkaST
    stquokka
    quokkaST
    • 개발자 (77)
      • n8n (2)
      • CS공부 (46)
        • Java & Spring (15)
        • 인프라 (7)
        • 운영체제 & 시스템 (9)
        • 기타 CS지식 (7)
        • 네트워크 (6)
        • 데이터베이스 (2)
      • 알고리즘 (16)
      • 프로젝트 (8)
        • 감정&금융챗봇 (8)
      • 리팩토링 (5)
        • horong (5)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
상단으로

티스토리툴바