백엔드 면접 용어 사전
Spring·JPAlazy loading

지연 로딩

연관 엔티티를 실제 사용 시점까지 조회하지 않는 전략. 프록시로 구현되며 N+1의 원인이자 해법의 출발점.

연관 엔티티를 실제로 쓰는 순간까지 조회를 미루는 전략(FetchType.LAZY).

메모리에 무엇이 들어가 있나

Order를 조회하면 order.member 자리에 진짜 Member가 아니라 프록시가 들어간다.

Order 인스턴스
├── id      = 7
├── amount  = 10000
└── member  → Member$HibernateProxy @f9e8      ← 껍데기
                ├── id = 1          (이것만 갖고 있다)
                ├── initialized = false
                └── target = null   ← 아직 비어 있음

프록시는 하이버네이트가 런타임에 만든 Member의 하위 클래스다. 필드는 비어 있고 식별자만 알고 있다.

초기화 흐름

다이어그램 로딩 중…

getMember()까지는 SQL이 나가지 않는다. 그 객체의 내용을 건드리는 순간 나간다. getId()는 프록시가 이미 갖고 있어 예외적으로 SQL 없이 반환된다.

기본값 — 자주 틀린다

연관기본 fetch
@ManyToOne · @OneToOneEAGER
@OneToMany · @ManyToManyLAZY

@ManyToOne이 EAGER인 것이 함정이다. 모든 연관을 LAZY로 명시하고 필요할 때만 fetch join으로 가져오는 것이 권장 방식이다.

두 가지 대표 사고

① N+1 — 루프에서 프록시를 건드려 쿼리가 N번 나간다.

LazyInitializationException — 트랜잭션이 끝나 컨텍스트가 사라진 뒤 프록시를 건드릴 때 난다. 프록시가 초기화를 요청하려면 컨텍스트가 필요한데 이미 없기 때문이다. 컨트롤러·뷰에서 엔티티를 그대로 쓰다 자주 겪는다.

→ 트랜잭션 안에서 필요한 것을 미리 조회하거나 DTO로 변환해서 내보낸다.

프록시 주의

프록시는 원본의 하위 타입이므로

  • order.getMember().getClass()Member.class 가 아니다
  • instanceof 는 통과하지만 == 클래스 비교는 실패한다
  • equals를 id 기반으로 구현해야 프록시와 실제 엔티티를 같게 볼 수 있다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Spring·JPA — IoC/DI·AOP·영속성 컨텍스트·N+1