개발하다 보면 "N + 1 문제"라는 용어를 자주 접하게 됩니다. 이 문제는 주로 JPA와 같은 ORM(Object Relational Mapping) 기술을 사용할 때 발생합니다. 간단히 말해, 하나의 데이터를 조회하는데 필요한 쿼리와 함께 관련된 데이터를 조회하기 위해 추가적으로 N개의 쿼리가 실행되는 상황을 의미합니다.

이 문제를 해결하기 위해 몇 가지 방법이 있습니다.
1. FetchType.EAGER 사용
FetchType.EAGER를 설정하면 연관된 데이터를 한 번의 쿼리로 가져올 수 있습니다. 하지만, 모든 경우에 EAGER를 사용하는 것은 권장되지 않습니다.
2. JPQL 또는 Fetch Join 사용
JPQL의 join fetch 구문을 사용하면 필요한 데이터만 한 번의 쿼리로 가져올 수 있습니다.
@Query("SELECT p FROM Post p JOIN FETCH p.author")
List<Post> findAllWithAuthor();
3. Batch Size 설정
JPA에서 default_batch_fetch_size를 설정하면 N + 1 문제를 효과적으로 해결할 수 있습니다. 이 설정은 지연 로딩 시 한 번에 가져오는 엔티티 수를 제한합니다.
hibernate.default_batch_fetch_size=10
4. DTO를 활용한 데이터 조회
필요한 데이터만 선택적으로 가져오기 위해 DTO(Data Transfer Object)를 사용합니다.
@Query("SELECT new com.example.dto.PostWithAuthorDTO(p.title, a.name) FROM Post p JOIN p.author a")
List<PostWithAuthorDTO> findPostWithAuthor();
문제 상황
@Entity
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
private Author author;
}
@Entity
public class Author {
@Id
@GeneratedValue
private Long id;
private String name;
}
@ManyToOne으로 설정되어 있습니다.FetchType.LAZY로 설정되었으므로 N + 1 문제가 발생합니다.해결 코드 (Batch Size)
@Configuration
public class JpaConfig {
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
adapter.getJpaPropertyMap().put("hibernate.default_batch_fetch_size", 10);
return adapter;
}
}