@OneToOne(fetch = FetchType.LAZY)
로 설정한 Brand
→ ImageFile
연관관계에서, 주문 목록을 조회하는 findMyOrders()
쿼리 실행 시 예상치 못한 N+1 문제가 발생했습니다. ImageFile
필드를 명시적으로 호출하거나 참조하지 않았음에도, ImageFile
테이블에서 brand_id
로 조회하는 쿼리가 수십 건 이상 반복 실행되었습니다.
@Entity
public class Brand {
@OneToOne(mappedBy = "brand", fetch = FetchType.LAZY)
private ImageFile imageFile;
}
@Entity
public class ImageFile {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "brand_id")
private Brand brand;
}
queryFactory.selectFrom(order)
.join(order.delivery, delivery).fetchJoin()
.join(order.orderItemList, orderItem).fetchJoin()
.join(orderItem.item, item).fetchJoin()
.join(item.brand, brand).fetchJoin()
// ImageFile은 fetchJoin 하지 않음
.where(조건...)
.fetch();
Hibernate는 ImageFile
에 대해 수십 건의 개별 쿼리를 실행:
select i1_0.image_file_id, i1_0.brand_id, i1_0.image_url, i1_0.item_id
from ImageFile i1_0
where i1_0.brand_id=?
이는 결과적으로 ImageFile을 LAZY로 설정했음에도 각 Brand에 대해 지연 로딩이 트리거된 것이며, N+1 문제입니다.
@OneToOne(fetch = FetchType.LAZY)
는 연관관계의 주인인 쪽에서만 실제로 Lazy 로딩이 동작합니다. 이 문제의 핵심은 null은 프록시로 감쌀 수 없다는 Hibernate의 설계 제약에서 비롯됩니다.
Brand
→ ImageFile
구조에서 Brand.imageFile
필드는 mappedBy
를 통해 연관관계의 주인이 아니며, 이 상태에서 fetch = LAZY
를 설정해도 실제로는 Lazy가 동작하지 않습니다.Brand.imageFile
에 null
을 넣을지, 프록시를 넣을지 결정하려면 DB를 조회해봐야 하기 때문입니다.null
인지 프록시인지 알 수 없습니다.Brand.imageFile
이 존재하는지 확인하기 위해 매번 SELECT
쿼리를 실행하게 되고, 이것이 N+1 문제로 이어집니다..join(brand.imageFile, imageFile).fetchJoin()
ImageFile
에서만 @OneToOne(fetch = LAZY)
방향을 유지Brand
는 ImageFile
을 참조하지 않도록 설계@Entity
public class ImageFile {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "brand_id")
private Brand brand;
}
Brand에서 ImageFile을 참조하는 흐름만 존재했고, ImageFile에서 Brand를 직접 참조할 필요는 없었습니다. 따라서, 의미 없는 양방향 연관관계로 인해 불필요한 지연 로딩 쿼리가 발생하느니, 단방향으로 단순화하는 것이 도메인 설계와 성능 측면 모두에서 더 적절하다고 판단했습니다.
이번 경험을 통해 JPA의 @OneToOne
연관관계에서만 발생하는 프록시 및 지연 로딩의 제약사항을 명확히 이해하게 되었습니다.
@OneToOne(fetch = LAZY)
로 설정하더라도 연관관계의 주인이 아닌 쪽에서는 Lazy 로딩이 동작하지 않을 수 있다는 점
그 이유는 Hibernate가 null인 경우를 프록시로 감쌀 수 없기 때문이며, 연관된 엔티티가 존재하는지 확인하기 위해 결국 쿼리를 실행하게 된다는 점을 새롭게 알게 되었습니다.
또한, 실질적으로 필요하지 않은 무의미한 양방향 연관관계는 오히려 성능과 유지보수 측면에서 악영향을 줄 수 있다는 점을 체감했고, 앞으로는 연관관계의 방향성과 필요성에 대해 더 신중하게 판단해야겠다고 느꼈습니다.