지연 로딩과 즉시 로딩, 그리고 트랜잭션 관리

coldrice99·2024년 10월 28일
0
post-thumbnail

1. 로딩 방식: 지연 로딩(Lazy Loading) vs 즉시 로딩(Eager Loading)

🟠 즉시 로딩 (Eager Loading)

즉시 로딩은 엔티티를 로딩할 때 연관된 엔티티도 함께 조회하는 방식입니다. 필요하지 않은 데이터도 미리 로딩되기 때문에 메모리를 많이 사용하지만, 반복 쿼리 발생을 방지할 수 있습니다.

  • 설정: @OneToOne(fetch = FetchType.EAGER) 또는 @ManyToOne(fetch = FetchType.EAGER)
  • 장점: 조회할 때 모든 데이터가 한 번에 로드되어 추가적인 쿼리가 없음
  • 단점: 불필요한 데이터 로딩으로 메모리 낭비 가능성

예시 코드:

@OneToOne(fetch = FetchType.EAGER)
private Address address;

🔵 지연 로딩 (Lazy Loading)

지연 로딩은 실제로 엔티티에 접근할 때 데이터를 로딩하는 방식입니다. 메모리를 절약하고 성능을 최적화할 수 있지만, 트랜잭션이 끝나면 지연 로딩된 필드에 접근할 때 예외가 발생할 수 있습니다.

  • 설정: @OneToMany(fetch = FetchType.LAZY) 또는 @ManyToOne(fetch = FetchType.LAZY)
  • 장점: 필요할 때만 데이터를 로드하여 메모리 사용 절약
  • 단점: 트랜잭션 종료 후 접근 시 LazyInitializationException 발생 가능

예시 코드:

@OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
private List<Order> orders;

2. 트랜잭션 관리와 지연 로딩

지연 로딩을 안전하게 수행하려면 트랜잭션 범위 내에서 영속성 컨텍스트가 열려 있어야 합니다. 서비스 계층에서 @Transactional 어노테이션을 사용하여 트랜잭션 범위 내에서 지연 로딩된 엔티티에 접근하는 방식이 일반적입니다.

🟡 @Transactional 어노테이션 사용 예시

아래는 서비스 계층에서 @Transactional을 적용하여 지연 로딩을 안전하게 수행하는 예시입니다.

@Service
public class MemberService {

    @Autowired
    private EntityManager em;

    @Transactional  // 트랜잭션이 시작되어 영속성 컨텍스트가 열림
    public Member getMemberWithOrders(Long memberId) {
        // Member 엔티티 조회 (이때 orders 필드는 로딩되지 않음)
        Member member = em.find(Member.class, memberId);

        // 트랜잭션 범위 내에서 지연 로딩이 발생하므로 LazyInitializationException이 발생하지 않음
        System.out.println(member.getOrders().size());  // 이때 orders에 대한 쿼리 실행

        return member;
    }
}

⚠️ 트랜잭션 종료 후 지연 로딩의 문제점

트랜잭션이 종료된 후 지연 로딩된 필드에 접근하려 하면 LazyInitializationException이 발생합니다. 아래와 같은 상황에서 예외가 발생할 수 있습니다.

public void someMethod() {
    Member member = memberService.getMemberWithOrders(1L); // 트랜잭션 없이 호출
    System.out.println(member.getOrders().size());  // LazyInitializationException 발생
}

3. 트랜잭션 전이와 지연 로딩 문제 해결 방법

  1. 필요한 엔티티를 트랜잭션 범위 내에서 미리 로드: 트랜잭션이 종료되기 전에 필요한 데이터를 모두 로드하여 예외를 방지합니다.
  2. Hibernate.initialize()로 초기화: 트랜잭션이 종료되기 전에 지연 로딩 대상을 초기화하여 이후 예외가 발생하지 않도록 합니다.
  3. DTO로 Projection 사용: 필요한 데이터만 DTO로 매핑하여 조회하는 방식으로 지연 로딩을 대체할 수 있습니다.

💡 정리

지연 로딩은 성능 최적화를 위해 중요하지만, 트랜잭션 범위 내에서 안전하게 사용하지 않으면 LazyInitializationException이 발생할 수 있습니다. @Transactional을 통해 트랜잭션을 관리하고, 필요에 따라 즉시 로딩을 선택하는 등 로딩 전략을 상황에 맞게 최적화하는 것이 중요합니다.

profile
서두르지 않으나 쉬지 않고

0개의 댓글