Backend
home

JPA 지연로딩 문제 해결

생성 일시
2025/07/19 09:44
태그
SpringBoot
게시일
2025/07/19
최종 편집 일시
2025/07/19 09:47

N + 1 문제란?

정의: 연관된 엔티티를 조회할 때, 1개의 쿼리(N=1)로 시작하지만 연관된 데이터를 각각 조회하면서 N개의 쿼리가 추가로 실행되는 문제.
예시: 게시글 10개를 조회 → 각 게시글의 작성자 정보를 지연 로딩 → 총 11개의 쿼리 실행

원인: 지연 로딩(Lazy Loading)

JPA는 성능 최적화를 위해 @ManyToOne, @OneToMany 관계를 기본적으로 LAZY로 설정
하지만 지연 로딩된 객체를 루프에서 꺼내면 N번의 쿼리가 추가 실행됨 → N + 1 발생

해결 가이드라인

1. Fetch Join 사용 (JPQL)

@Query("SELECT p FROM Post p JOIN FETCH p.member") List<Post> findAllWithMember();
Java
복사
JOIN FETCH를 사용해 연관 객체를 한 번에 함께 조회
가장 일반적인 해결 방법
단점: 페이징 불가 (ToMany 관계에서는)

2. EntityGraph 사용 (Spring Data JPA)

@EntityGraph(attributePaths = {"member"}) @Query("SELECT p FROM Post p") List<Post> findAllWithMember()
Java
복사
쿼리는 명시하지 않고 연관 엔티티를 함께 조회
동적 쿼리가 필요 없을 때 사용

3. FetchType 설정 주의

@ManyToOne(fetch = FetchType.LAZY) private Member member;
Java
복사
기본적으로 LAZY 유지 (EAGER는 오히려 성능 악화 및 순환 참조 위험)
직접 사용하는 시점에서 JOIN FETCH 또는 EntityGraph로 해결

4. QueryDSL 사용 시 fetchJoin

queryFactory .selectFrom(post) .join(post.member, member).fetchJoin() .fetch();
Java
복사
QueryDSL 사용 시에도 fetchJoin 명시
동적 쿼리에 유리

5. 배치 사이즈 설정 (컬렉션 처리)

컬렉션 조회 시 N + 1 발생 → Hibernate의 in-query 방식으로 줄이기
# application.yml spring: jpa: properties: hibernate.default_batch_fetch_size: 100
YAML
복사
@OneToMany(mappedBy = "post") private List<Comment> comments
Java
복사
컬렉션이 있을 경우 설정한 batch size만큼 IN 쿼리로 처리
즉시 로딩 아님, 지연 로딩 시 효율적 로딩

6. DTO 직접 조회 (JPQL or QueryDSL)

@Query("SELECT new com.example.dto.PostDto(p.id, p.title, m.name) FROM Post p JOIN p.member m") List<PostDto> findAllPostDto();
Java
복사
필요한 데이터만 조회하여 DTO로 직접 매핑
오버페치 방지 + 성능 최적화

7. 캐시 활용

Redis 같은 캐시를 통해 자주 조회되는 연관 객체 캐싱
지연 로딩된 객체를 반복 조회 시 캐시 활용 가능