본문 바로가기
Framework & Lib & API/JPA

jpa 연관관계 EAGER와 LAZY(etc, 실제 겪은 문제들)

by 코딩공장공장장 2022. 6. 26.

jpa는 기본적으로 하나의 엔티티를 가져올 때, 연관관계에 있는 엔티티를 모두 가져온다. 

 

사용하지 않는 엔티티까지 모두 가져오는 것은 굉장한 손해이다. 

 

물론, jpa가 mybatis에 비해 쿼리 재사용성이 높으니  이 정도 손해는 감수해도 되지 않나(?) 라고 생각할 수도 있지만

 

그럼에도 불구하고 사용하지 않는 엔티티를 가져오는것은 누가 생각하더라도 손해이다.

 

이러한 손해를 해결해줄 수 있는 방법이 바로 "지연로딩 LAZY"이다.

 

 

이를 알아보기 전에 먼저 알고 있으면 도움이 되는 개념이 "프록시" 이다. 

 

내가 알고 있는 프록시라는 개념은 서로 다른 서버의 연결이나 데이터 전달을 해주는 매개체 정도로 알고 있다.

 

하나의 웹어플리케이션을 본다면 web서버와 was서버를 연결하는 프록시, was서버와 db를 연결하는 프록시 말이다.

 

jpa의 프록시가 내가 알고 있는 was와 db를 연결하는 프록시와 같은 프록시의 개념으로 이해해도 되는지 잘 모르겠다. 

(아시는 분이 있으시다면 댓글로 남겨주시면 감사하겠습니다.)

 

jpa의 프록시는 실제 엔티티를 상속받은 가짜 엔티티이다. 

 

따라서 실제 엔티티와 겉모양이 같고 실제 엔티티에 접근할 수 있는 권한이 있다.

 

개발자 입장에서 굳이 이게 프록시인지 실제 엔티티인지 구분이 불가하며 구분해서 사용할 필요도 없다.

 

만약 내가 조회한 엔티티가 연관관계가 있는 엔티티이고 지연로딩을 사용했다면 

 

연관관계 엔티티를 get하는 메서드를 사용할 때 query문이 실행되어 해당 엔티티를 가져온다. 

 

이를 초기화라고 하며 초기화는 처음 get할 때 최초에 한번만 일어난다.

 

즉, 상위 엔티티를 조회하면 하위 칼럼의 식별자를 가지고 있으니 하위 엔티티를 실제 사용할 때,

 

식별자를 통해 쿼리문을 날려 조회해온다는 것이다. 

 

"즉시로딩 EAGER"는 연관관계에 있는 엔티티를 조인하여 한번에 엔티티를 가져오고

 

"지연로딩 LAZY"는 프로그래밍에서 get 할 때, 연관관계 엔티티를 식별자를 통해 가져온다. 

 

일반적으로 연관관계에있는 엔티티가 거의 함께 사용된다고 하면 EAGER를 사용하고

 

함께 사용되는 경우가 많지 않다면 LAZY를 사용하면 유리할 것 같다. 

 

일반적으로 실무에서는 LAZY를 주로 사용한다고 한다.

 

EAGER의 경우 N+1이라는 문제를 맞닥뜨리기 쉬운데 이는 나중에 포스팅하도록 하겠다. 

 

---------------실제 개발 중 겪은 문제들-------------------

 

"onetoone lazy 정상작동안함"

 

나의 경우 onetoone lazy로 연관관계를 맺은 테이블이 있었다.

 

허나 연관관계의 객체에서 데이터를 불러오면 lazy로 작동하지 않았다.

 

원인부터 말하자면 연관관계 엔티티에서 null이 존재하는 경우 eager로 가져온다고 한다.

 

jpa에서 엔티티는 오로지 식별자로 구분한다. 

 

lazy 방식의 동작 원리는 연관관계의 엔티티의 식별자만 가지고 있다 필요할 때 db에 접근하여 가져오는 방식이다.

 

허나 null인 경우 식별자조차 존재하지 않으니 lazy 방식으로 접근할 수 없다. 

 

참고로 프록시는 null을 감쌀 수 없다고 한다. 

 

결과적으로 oneToMany 종으로 설계하여 해결하였다. 

 

oneToOne 주인으로도 설계해보았지만 n+1 문제도 생겨 oneToMany 종으로 설계하여

 

entityGraph와 함께 사용하여 n+1 문제와 lazy 작동방식을 함께 해결하였다.

 

 

"LAZY 전략 사용시 dto객체로 변환하여 web단으로 넘겨주기 "

 

EAGER로 설정 된 연관관계를 LAZY로 변경후 오류가 나는 부분이 있었다.

 

LAZY의 경우에는 가짜 엔티티(프록시)이기 때문에 컨트롤러단에서 web으로 리턴하게되면

 

json객체로 바꾸는 과정에서 내부적으로 get함수가 작동한다고 한다.

 

jpa의 프록시 생명주기는 트랜잭션 단위인데 트랜잭션 범위에서 벗어나서 컨트롤러단에서 get함수가 실행되버리니

 

오류가 나는 것이다.

 

따라서 LAZY로 fetchType을 설정한 경우 트랜잭션 범위 안에서 Dto로 변경하여 컨트롤러로 넘겨주고

 

web으로 넘겨주어야 한다. 그렇지 않으면 생명주기가 다 끝난 프록시에게 db에 접근해서 값을 가져오라는 것과 같다.

반응형