Daily Dev Q&A: N+1 문제 & 해결책

2026. 1. 13. 17:29·Archive/Daily Dev Q&A

Topic (오늘의 주제)

JPA의 성능 킬러: N+1 문제와 해결책

Why (왜 사용하는가? 왜 중요한가?)

  • 실무: JPA를 사용하는 프로젝트에서 앱 성능 저하의 원인 1순위입니다. 로컬(데이터 적음)에선 모르다가 운영(데이터 많음)에서 서버를 뻗게 만듭니다.
  • 구조적 의미: ORM이 개발자 편의를 위해 제공하는 **지연 로딩(Lazy Loading)**이나 연관 관계 매핑이, 의도치 않게 과도한 쿼리를 유발하는 현상입니다.
  • 면접 의도: JPA의 동작 원리를 이해하고 있는지, 그리고 이 치명적인 문제를 **어떤 기술(Fetch Join, Batch Size)**로 해결해본 경험이 있는지 검증합니다.

Core Concept (핵심 개념 정리)

요소 내용
개념 정의 1번의 쿼리로 가져온 결과(N개)를 사용할 때, 연관된 데이터를 얻기 위해 추가로 N번의 쿼리가 더 나가는 문제. (총 1 + N번 실행)
발생 원인 JPA가 객체를 조회할 때 연관된 객체(ManyToOne 등)를 바로 가져오지 않고, 실제 사용할 때 쿼리를 날리는(Lazy Loading) 특성 때문에 발생.

(EAGER로 설정해도 최초 쿼리 후 즉시 N번 날아가므로 해결책이 아님)
상황 예시 Team(팀) 목록을 10개 조회함 (쿼리 1번).

각 팀의 Member(멤버) 이름을 출력하려고 for문을 돌림.

팀 1의 멤버 조회, 팀 2의 멤버 조회... (쿼리 10번 추가 발생).
해결책 1. Fetch Join: 가장 확실한 방법. 조인을 통해 한 방 쿼리로 다 가져옴.

2. Batch Size: N번 나갈 쿼리를 IN 절로 묶어서 1/(Batch Size) 번으로 줄임.

3. EntityGraph: 어노테이션으로 Fetch Join 효과를 냄.

Interview Answer Version (면접 답변식 요약)

"N+1 문제는 연관 관계가 설정된 엔티티를 조회할 때, 조회된 데이터 갯수(N)만큼 연관 데이터를 얻기 위한 추가 쿼리가 발생하는 현상입니다.

주로 지연 로딩(Lazy Loading) 상황에서 객체를 탐색할 때 발생하며, 이를 해결하기 위한 가장 일반적인 방법은 Fetch Join을 사용하여 SQL 한 번에 연관 데이터를 미리 가져오는 것입니다.

추가적으로 컬렉션 조회 시 페이징이 필요하다면 Batch Size 설정을 통해 쿼리 수를 최적화합니다."

Practical Tip (사용시 주의할 점 or 활용 예)

1. Fetch Join 사용법 (JPQL)

  • 일반 join은 연관 엔티티를 SELECT 절에 포함하지 않지만, fetch join은 객체 그래프를 한 번에 긁어옵니다.
  • Java
    // N+1 발생 코드
    @Query("select t from Team t")
    List<Team> findAll();
    
    // 해결 코드 (Fetch Join)
    @Query("select t from Team t join fetch t.members")
    List<Team> findAllJoinFetch();
    

2. Fetch Join과 페이징(Pagination) 주의

  • 1:N 관계(컬렉션)를 Fetch Join 하면서 페이징(Pageable)을 하면, 하이버네이트는 "모든 데이터를 메모리에 퍼올린 다음" 페이징을 시도합니다. -> Out of Memory 발생 위험.
  • 해결: 컬렉션 페이징이 필요하면 Fetch Join을 빼고, hibernate.default_batch_fetch_size 옵션(글로벌 설정)을 100~1000 정도로 설정하여 해결합니다. (IN 쿼리로 최적화됨)

예상 꼬리질문 정리

  1. Q: 일반 Join과 Fetch Join의 차이점은 무엇인가요?
    • 답변: 일반 Join은 연관 엔티티를 쿼리 조건(WHERE)에는 쓸 수 있지만 SELECT 절에는 안 가져옵니다(영속화 안 됨). Fetch Join은 연관 엔티티까지 모두 조회해서 영속성 컨텍스트에 저장합니다.
  2. Q: Batch Size는 어떻게 동작하나요?
    • 답변: 설정한 사이즈만큼 PK를 모아서 WHERE id IN (?, ?, ...) 형태의 쿼리를 날려, N번의 쿼리를 1번(혹은 소수)으로 줄여줍니다.
  3. Q: MultipleBagFetchException이 뭔가요?
    • 답변: 2개 이상의 컬렉션을 동시에 Fetch Join 할 때 발생하는 에러입니다. 컬렉션 Fetch Join은 하나만 가능합니다.

'Archive > Daily Dev Q&A' 카테고리의 다른 글

Daily Dev Q&A: ApplicationContext  (0) 2026.01.19
Daily Dev Q&A: DNS  (0) 2026.01.14
Daily Dev Q&A: Spring MVC  (0) 2026.01.13
Daily Dev Q&A: JPA vs MyBatis  (0) 2026.01.09
Daily Dev Q&A: 영속성 컨텍스트  (0) 2026.01.09
'Archive/Daily Dev Q&A' 카테고리의 다른 글
  • Daily Dev Q&A: ApplicationContext
  • Daily Dev Q&A: DNS
  • Daily Dev Q&A: Spring MVC
  • Daily Dev Q&A: JPA vs MyBatis
tlsgkstj
tlsgkstj
짱구의 성장 일기
  • tlsgkstj
    코딩하는 짱구
    tlsgkstj
  • 전체
    오늘
    어제
    • 분류 전체보기 (159)
      • About (1)
      • Projects (35)
        • Personal Projects (21)
        • Team Projects (14)
      • Engineering (20)
        • CS & Tools (0)
        • Backend Core (15)
        • Frontend (1)
        • Infra & Cloud (2)
        • AI & Tools (1)
      • Trouble Shooting & Issues (0)
      • Growth & Career (38)
        • Interview Prep (0)
        • Retrospectives (38)
      • Archive (65)
        • TIL (8)
        • Daily Dev Q&A (56)
  • 블로그 메뉴

    • 홈
    • About
    • Projects
    • Tech Stack
    • Dev Log
    • GitHub
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    SpringBoot
    til
    커리어리셋
    spring
    프로덕트개발자
    Project_Review
    REACT
    backend
    jpa
    devlog
    Spring비교
    aws_s3
    클로드코드
    데브페스트
    java
    프로젝트회고
    network
    경기기후바이브코딩
    OrphanRemova
    DevFestIncheon2025
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
tlsgkstj
Daily Dev Q&A: N+1 문제 & 해결책
상단으로

티스토리툴바