📍Lazy Loading 과 Eager Loading는
ORM에서 연관 데이터를 가져오는 방법을 결정하는 전략으로 JPA에서 연관된 엔티티를 가져오는 방식

1. Lazy Loading (지연 로딩)
- 기본적으로 필요할 때만 연관된 데이터를 가져옴
- 처음에는 프록시 객체를 반환하고 실제 데이터가 필요할 때 쿼리가 실행
- 성능 최적화에 좋지만 LazyInitializationException(영속성 컨텍스트가 닫힌 후 조회 시 발생)을 주의해야함
2. Eager Loading (즉시 로딩)
- 연관된 엔티티를 즉시 조회하여 한 번의 쿼리로 가져옴
- 필요 없는 데이터도 미리 가져오기 때문에 성능 저하가 발생할 수 있음
ORM(Object-Relational Mapping)
객체와 데이터베이스를 매핑하여 SQL 없이 데이터 조작을 가능하게 하는 기술
ex. JPA (Hibernate), TypeORM, Sequelize
📍예제
- User, Order, OrderItem, Product 테이블 간의 1:N, N:1 관계를 포함
1️⃣ 데이터 모델
- User(사용자) → 여러 개의 Order(주문)을 가질 수 있음 (1:N)
- Order(주문) → 여러 개의 OrderItem(주문 상세)을 가짐 (1:N)
- OrderItem(주문 상세) → Product(상품)을 참조 (N:1)
2️⃣ 엔티티 설계
(1) User 엔티티 (Lazy Loading)
import jakarta.persistence.*;
import java.util.List;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY) // Lazy Loading
private List<Order> orders;
// Getter, Setter
}
(2) Order 엔티티 (Eager Loading)
import jakarta.persistence.*;
import java.util.List;
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderDate;
@ManyToOne(fetch = FetchType.LAZY) // Lazy Loading
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", fetch = FetchType.EAGER) // Eager Loading
private List<OrderItem> orderItems;
// Getter, Setter
}
(3) OrderItem 엔티티 (Lazy Loading)
import jakarta.persistence.*;
@Entity
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int quantity;
@ManyToOne(fetch = FetchType.LAZY) // Lazy Loading
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne(fetch = FetchType.LAZY) // Lazy Loading
@JoinColumn(name = "product_id")
private Product product;
// Getter, Setter
}
(4) Product 엔티티 (Eager Loading)
import jakarta.persistence.*;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int price;
// Getter, Setter
}
3️⃣ Lazy vs Eager Loading 실행 예제
(1) Lazy Loading 사용 시
User user = entityManager.find(User.class, 1L);
System.out.println(user.getName()); // ✅ User 정보만 가져옴
List<Order> orders = user.getOrders(); // ❌ 아직 Orders 조회 안 됨
System.out.println(orders.size()); // ✅ 이 시점에서 Order 조회 쿼리 실행됨
Order order = orders.get(0);
List<OrderItem> orderItems = order.getOrderItems(); // ✅ Eager Loading이므로 즉시 조회됨
실행되는 SQL
SELECT * FROM User WHERE id = 1; -- User만 조회
SELECT * FROM Order WHERE user_id = 1; -- 이 시점에서 Order 조회됨
SELECT * FROM OrderItem WHERE order_id = 1; -- Eager로 OrderItem 즉시 조회됨
(2) Eager Loading 사용 시
Order order = entityManager.find(Order.class, 1L);
System.out.println(order.getOrderDate()); // ✅ Order 즉시 로드됨
List<OrderItem> items = order.getOrderItems(); // ✅ Eager Loading이므로 이미 가져옴
System.out.println(items.size());
실행되는 SQL
SELECT o.*, oi.* FROM Order o
LEFT JOIN OrderItem oi ON o.id = oi.order_id
WHERE o.id = 1;
- 장점: 한 번의 SQL로 조회 가능
- 단점: 필요 없는 데이터까지 불러올 가능성 있음
4️⃣ Lazy Initialization Exception 방지 방법
Lazy Loading을 사용할 때 트랜잭션이 끝난 후 객체를 조회하면 LazyInitializationException이 발생
@Transactional
public void loadUserData() {
User user = entityManager.find(User.class, 1L);
System.out.println(user.getOrders().size()); // ✅ 트랜잭션 내에서 실행해야 함
}
JOIN FETCH를 사용하여 미리 가져올 수도 있음
List<User> users = entityManager.createQuery(
"SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id", User.class)
.setParameter("id", 1L)
.getResultList();'개발인생 > Backend' 카테고리의 다른 글
| JSON 파싱이란? (1) | 2025.03.25 |
|---|---|
| [ORM] JPA, JPQL, QueryDSL - Spring과의 관계 (1) | 2025.03.19 |
| Spring vs Spring Boot (2) | 2025.03.19 |
| [Spring] @Transactional (0) | 2025.03.19 |
| [Spring] Spring Data JPA (0) | 2025.03.19 |