JPA의 Lazy Loading과 LazyInitializationException 이해하기

JunSuPark·2025년 4월 16일

JPA에서 FetchType.LAZY로 설정된 연관 필드에 대해, 영속성 컨텍스트(Session)가 닫힌 이후에 접근할 때 발생하는 예외야.

brandService.getBrandById를 호출하는 시점이 service단 @Transactional(readOnly = true) 환경에서 실행되고,
그 이후 컨트롤러에서 brand.getBranches()를 호출하는 순간에 세션이 닫혀 있으면 LazyInitializationException 발생함.

@Transactional의 역할은
→ EntityManager (=영속성 컨텍스트 = 세션)를 열고 닫는 라이프사이클을 관리하는 것

상황 결과
@Transactional 안에서 Lazy 필드 접근 ✅ 정상 작동
@Transactional 밖에서 Lazy 필드 접근 ❌ LazyInitializationException 발생

brandService.getBrandById() 안에서는
service단) @Transactional(readOnly = true) 트랜잭션 안에서 실행됨 이건 OK
하지만, 그 이후 getBrandById()가 끝나고 나면 트랜잭션이 종료되고, EntityManager가 close됨. ( 컨트롤러나 그 이후 상황...)
이 상황에서 컨트롤러에서 brand.getBranches()를 호출하면
연관 데이터는 아직 DB에서 안 가져왔는데, 영속성 컨텍스트는 이미 사라짐.
-> 즉, JPA가 데이터를 가져올 수 없음... -> LazyInitalizationException이 발생함.

결국 엔티티를 그대로 노출 하는건 위험하고 DTO 노출로 가야함.
CRUD를 쓰는 Entity들은 다 DTO로 감싸서 보내야함.

  • Lazy 문제를 방지함.
  • 꼭 필요한 정보만 전달함. ( 프론트엔드에 맞춘 데이터 형식 가공 가능함.)
  • 엔티티 직접 노출로부터 보호. ( 엔티티 수정시. 의도치 못한 버그 발생 가능함.)
  • 계층 간 의존성 분리.
    엔티티는 DB와 밀접한 구조 -> 리포지토리/도메인 계층
    DTO는 view/API 전용 구조 -> 컨트롤러/프론트엔드용.
    ✅ 엔티티 구조가 바뀌어도 API 스펙을 바꾸지 않아도 됨 → 유지보수 용이
    성능 최적화. ( select 쿼리 튜징쉬움 )
profile
배움을 추구하는 개발자

0개의 댓글