JPA를 사용하다 보면 즉시로딩(Eager)과 지연로딩(Lazy)을 자연스레 접하게 됩니다. 사실 실무에서 모든 연관관계는 지연 로딩으로 설정하는 것이 좋지만, 이렇게 하는 설정하는 법이 왜 좋은지에 대한 의문이 생겨 공부하게 되었고 기록을 남기기 위해 포스팅하게 되었습니다.
개요
JPA에서는 테이블 간 연관 관계는 객체의 참조를 통해 이루어집니다. 실무에서는 서비스의 규모가 대부분 크기 때문에, 데이터의 양이 많은 것은 물론이거니와 각각의 데이터들끼리 참조하고 있기에, 연관된 데이터들을 한번에 가져오는 행동은 부담이 큽니다.
따라서 JPA는 참조하는 객체들의 데이터를 가져오는 시점을 정할 수 있는데, 이것을 Fetch Type이라고 합니다. Fetch Type에는 Eager와 Lazy 방식이 있습니다.
Eager는 즉시로딩으로 말 그대로 데이터를 가져올 때 하나의 객체만 가져오는 것이 아닌 참조 객체들의 데이터까지 전부 읽어오는 방식을 뜻합니다.
Lazy는 지연 로딩으로 참조 객체들의 데이터들은 무시하고 해당 엔티티의 데이터만 가져오는 방식입니다.
어떤 것을 쓰는 게 좋을까❓
사실 상황에 맞게 필요한 방법을 찾는 것이 가장 좋지만, 보편적으로는 (Lazy)지연로딩을 사용합니다. 앞서 말씀 드린 것처럼, (Eager) 즉시 로딩은 관련되어 있는 모든 객체들을 가져오기 때문에 불필요한 조인으로 인해 성능 저하를 피할 수 없게 됩니다.
쉬운 예시로 1000개의 주문을 조회했을 때, 그 주문자를 찾기 위해 1000개의 멤버를 찾는 쿼리도 함께 발생하게 됩니다. 보통 이 문제를 n +1 문제라고 일컫습니다. 개발자가 예상했던 것보다 2배의 쿼리가 진행되기 때문에 예상치 못한 큰 비용이 발생할 수 있고, 서비스의 규모가 커지면 커질수록 기하급수적으로 비용이 발생하게 됩니다.
연관관계 Default📚
- OneToMany : Lazy - 지연 로딩
- ManyToMany : Lazy - 지연 로딩
- ManyToOne : Eager - 즉시 로딩
- OneToOne : Eager - 즉시 로딩
기본적으로 각각의 연관관계의 default 속성은 위와 같습니다. 앞서 설명드린 것처럼 실무에서는 Lazy를 사용하는 것을 가급적으로 권장하기 때문에, ManyToOne과 OneToOne의 연관관계만 직접 Lazy로 설정하시는 것을 추천드립니다😄
간단 예시👀
@Entity
@Getter
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; // Order, Cancel
...
예시로 Order Entity를 보시면 위에서 말한 @ManyToOne 관계와 @OneToOne 관계를 fetch = FetchType.LAZY로 직접 수동 설정해준 것을 볼 수 있습니다. 두 개 다 기본 값이 Eager이기 때문에 어노테이션을 수정하였습니다.
실제로 쿼리를 날려보면
hibernate
select
order0_.order_id as order_id1_6_,
order0_.delivery_id as delivery4_6_,
order0_.member_id as member_i5_6_,
order0_.order_date as order_da2_6_,
order0_.status as status3_6_
from
orders order0_
직접 Lazy로 설정한 값들에 대한 쿼리들은 모두 제외하고 Order Entity 하나만 조회하는 것을 확인할 수 있습니다.
👨👩👦👦 오픈채팅방 운영
취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁
참여코드 : 456456
https://open.kakao.com/o/gVHZP8dg
👨💻 전자책 출간
아울러 제가 🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁
마치며
코드 한 줄로 프로그램의 성능이 천차만별 차이가 나는 게 놀라웠습니다. 실무에서 개발을 하는 과정이 성장에 있어 도움이 크게 되지만, 한편으로는 fetch와 같은 이론적인 측면도 빠삭하게 익혀놔야 할 것 같습니다. 많이 부족한 글이기에 잘못된 정보가 있으면 댓글로 알려주시면 감사하겠습니다😄
Reference
'JPA' 카테고리의 다른 글
SpringBoot JPA 쇼핑몰 도메인&테이블 설계 (0) | 2022.09.07 |
---|---|
JPA 영속성 관리 Dirty Checking & Merge (0) | 2022.09.06 |
SpringBoot JPA 게시판 CRUD 구현(예외 처리) (0) | 2022.07.27 |
SpringBoot JPA 게시판 CRUD 구현(Delete-TDD) (0) | 2022.07.25 |
SpringBoot JPA 게시판 CRUD 구현(Update-게시글 수정) (0) | 2022.07.21 |