N+1 문제는 ORM(Object-Relational Mapping)에서 발생하는 성능 저하 문제입니다.
특정 데이터를 조회할 때 1개의 기본 쿼리(1)를 실행한 후, 연관된 데이터를 가져오기 위해 추가로 N개의 쿼리(N)를 실행하면서 발생합니다.
결과적으로 쿼리가 (1 + N)번 실행되면서 성능이 저하됩니다.
@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;
}
// 팀 리스트 조회
List<Team> teams = teamRepository.findAll(); // (1) 팀 조회 (1번 쿼리 실행)
// 각 팀에 속한 멤버 조회 (N번 쿼리 실행)
for (Team team : teams) {
List<Member> members = team.getMembers(); // (N) 멤버 조회 (N번 쿼리 실행)
}
문제점:
findAll() 실행 시 팀을 한 번 조회 (SELECT * FROM team) getMembers() 호출할 때마다 각 팀의 멤버를 조회 (SELECT * FROM member WHERE team_id = ?) @Query("SELECT t FROM Team t JOIN FETCH t.members")
List<Team> findAllWithMembers();
JOIN FETCH를 사용하여 한 번의 쿼리로 팀과 멤버를 함께 조회 @EntityGraph(attributePaths = {"members"})
@Query("SELECT t FROM Team t")
List<Team> findAllWithMembers();
@EntityGraph를 사용하여 연관 데이터를 한 번에 로딩 JOIN FETCH와 비슷한 효과 spring:
jpa:
properties:
hibernate.default_batch_fetch_size: 100
IN 절을 활용하여 한 번에 여러 개의 연관 데이터를 가져옴 JPA를 사용할 때 Lazy Loading(지연 로딩)이 기본 설정이므로 연관된 데이터를 조회할 때 N+1 문제가 발생할 가능성이 높다는 점을 깨달았습니다.
Fetch Join, EntityGraph, Batch Size 조정 등 다양한 최적화 기법을 활용하여 성능 문제를 해결하는 것이 중요하다고 느꼈습니다!