2025-11-07

오늘 배운 것

📌 정의

하나의 쿼리를 실행했는데, 연관된 데이터를 불러오느라 추가 쿼리가 N번 발생하는 문제

즉,

1번의 조회 쿼리 + N번의 추가 쿼리 → 총 N+1 쿼리 발생!


🎯 예시 상황

@Entity
public class Team {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members;
}

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Team team;
}


🧨 문제가 되는 코드

java
코드 복사
List<Team> teams = teamRepository.findAll(); // ✅ 쿼리 1번

for (Team team : teams) {
    System.out.println(team.getMembers());  // ❗ 매 팀마다 쿼리 N번
}

👉 실행 쿼리 요약

sql
코드 복사
SELECT * FROM team;          -- 1번
SELECT * FROM member WHERE team_id = 1;  -- N번 반복
SELECT * FROM member WHERE team_id = 2;
...

✅ 예상: 1번 쿼리

❌ 실제: N+1 쿼리 → 성능 저하


📦 발생 원인

원인 설명
연관관계의 기본 fetch 전략 @OneToMany, @ManyToOne → 기본이 LAZY
객체를 순회하며 getter 호출 그 시점에 실제 쿼리 날림 (프록시 초기화)

✅ 해결 방법 1: Fetch Join

JPQL로 연관된 엔티티를 한 번에 JOIN해서 가져오기

@Query("SELECT t FROM Team t JOIN FETCH t.members")
List<Team> findAllWithMembers();

✅ 실행 쿼리:

SELECT t.*, m.* FROM team t
JOIN member m ON t.id = m.team_id

→ Team과 Member를 한 번에 조회!


✅ 해결 방법 2: @EntityGraph

JPQL 없이 fetch join처럼 작동

@EntityGraph(attributePaths = "members")
List<Team> findAll();

✅ 해결 방법 3: BatchSize (for LAZY 컬렉션)

JPA가 IN 쿼리로 모아서 한번에 가져오도록 설정

@BatchSize(size = 100)
@OneToMany(mappedBy = "team")
private List<Member> members;

또는 application.yml:

spring:
  jpa:
    properties:
      hibernate.default_batch_fetch_size: 100

results matching ""

    No results matching ""