인프런 커뮤니티 질문&답변

이찬원님의 프로필 이미지

작성한 질문수

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화

주문 조회 V2: 엔티티를 DTO로 변환

orderItems 에 null이 들어가는 이유가 궁금합니다.

해결된 질문

작성

·

934

1

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)

[질문 내용]
강의를 듣다 의문점이 생겨 질문 드립니다.

OrderDto는 JPA에서 생성된 객체가 아니고 직접 생성자로 만들어서 사용한 객체인데 왜 Hibernate5Module이 프록시 객체로 생각해서 null값을 반환하는지 모르겠습니다.

제가 긴시간 찾아보면서 생각한 이유는 이렇습니다.

강의 5분경 쯤에 api 호출시 Json에서 orderItems 가 null로 반환되는것을 보여주십니다.

orderItems가 null로 반환되는것은 스프링이 string으로 응답을 보낼때 messageConverter가 json으로 변환해서 반환을합니다.

이때 orderItems가 JPA(Hibernate)에서 만든 프록시 객체로 되어있어 전강의에서 설정한 Hibernate5Module이 프록시 객체를 null로 바꾸기 때문에 orderItems가 null로 나타낸것으로 생각됩니다.

하지만 제가 디버깅 모드로 확인해본 결과 OrderDto안에 있는 orderItems는 프록시 객체가 아닌 실제로 생성된 객체였습니다.(OrderDto는 단순히 JPA가 관리 하지 않아서 당연하다고 생각합니다.)

강의 기준으로 orderItems 안에 있는 orderItem객체가 2개 존재하는데 orderItem객체는 JPA를 통해 생성된 객체이고 자세히 살펴보면 orderItem 안에 Item은 LAZY로딩으로 프록시 객체가 생성되어있습니다.

혹시몰라 Hibernate5Module을 끄고 실행한 결과 Item이 @JsonIgnore가 있으면 orderItems가 제대로 결과 값이 나오고 @JsonIgnore이 없으면 프록시 객체를 Json으로 변환하지 못해서 오류가 발생합니다.

그럼 Hibernate5Module는 엔티티 안에 객체의 값중 하나라도 프록시 객체가 있다면 다른 객체의 값이 있든 말든 그냥 null을 반환하는 걸까요??

제가 이해한것이 맞는지 궁금해서 질문드립니다.

긴글 읽어주셔서 감사합니다.

스크린샷 2023-07-07 오후 4.43.33.png

답변 4

2

안녕하세요. 이찬원님, 공식 서포터즈 David입니다.

답변이 조금 늦어져 죄송합니다.

  1. OrderDto가 null로 나왔다고 말씀하셨는데 강의 중 어떤 부분을 말씀하시는걸까요?

  2. 디버거에서 프록시인 엔티티를 열게 되면 조회 쿼리가 나갑니다. 즉, 지연로딩으로 인해 프록시가 들어왔지만 디버거에서 해당 객체 내에 뭐가 있나 궁금해서 열게 되는 순간 쿼리가 발생합니다. 이 부분은 찬원님께서 테스트 해보시면 바로 확인하실 수 있습니다. 저도 프록시, 지연로딩 공부하며 디버깅할 때 이걸 늦게 발견해서 헤맸던 기억이 있습니다.

  3. PersistetBag은 실제 객체처럼 보이지만 프록시 역할도 합니다. 따라서, 디버거에서 PersistentBag이 보이더라도 지연로딩에 의해 조회된 상태인지 아닌지는 다른 방법으로 확인하셔야 합니다.

    PersistentBag의 로딩 유무는 아래 코드를 참고해 주세요.

// EntityManager를 주입받으신 후

PersistenceUnitUtil unitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
System.out.println(unitUtil.isLoaded(orders.get(0).getOrderItems()));
  1. 디버거로 아무것도 확인하지 말고, orderItems 로딩유무

  2. 디버거에서 orders 내 order까지 열고 orderItems 로딩유무

위 2가지 케이스를 체크해 보시면 차이를 알 수 있습니다.

감사합니다.

0

이찬원님의 프로필 이미지
이찬원
질문자

다시 제가 작성한 질문을 읽어보니 제가 질문을 이상하게 적은것 같네요 죄송합니다.

제 질문은 강의 섹션4 주문 조회 V2 5:00분 쯤에 http://localhost:8080/api/v2/orders 로 요청을 하게 되면 json을 반환하게 되는데 OrderItem만 null로 나오게 됩니다.

image강의에서는 OrderItems가 Entity여서 null이 나왔다고 언급하시는데 이부분에서 의문점이 생겨서 질문 드립니다.

  1. OrderItem이 null로 변환되는 이유는 Hibernate5Module이 OrderDto에 OrderItem을 null로 변경시킨다고 생각합니다. Hibernate가 OrderItem을 PersistenceBag객체로 바꿔서 반환해주는데 Hibernate5Module은 이것을 프록시 객체로 인식해서 null로 반환하는 건가요?

     

  2. PersistenceBag은 hibernate가 Collection객체를 프록시 객체로 바꿀때 사용하는 건가요??

 

  1. 네, 맞습니다. Orderitem이 로딩되지 않았다고 판단하여 하이버네이트 모듈에서 null로 출력한 것입니다.

  2. PersistenceBag은 하이버네이트에서는 List를 다룰 때 내부적으로 PersistentBag을 대신 사용합니다. 그런데 PersistentBag은 프록시 역할도 겸합니다. 따라서, 제가 이전 질문에 답변드린 것처럼 디버거에서 PersistenBag만 보고 로딩이 된 것으로 판단할 수 없고 PersistenceUnitUitil을 사용해 로딩여부를 판단해야 합니다.

이찬원님의 프로필 이미지
이찬원
질문자

답변 감사합니다~~ ㅎㅎ

0

이찬원님의 프로필 이미지
이찬원
질문자

혹시 추가 답변 가능하신가요?? ㅠㅠ

0

안녕하세요, 인프런 AI 인턴입니다.

제가 이해한 내용으로는 OrderDto 객체를 생성할 때 Hibernate5Module이 프록시 객체로 인식하여 orderItems가 null로 반환된다는 것이 맞나요?

Hibernate는 지연 로딩(Lazy loading)을 지원하는데, 이는 연관된 객체를 사용할 때 실제로 데이터베이스에서 가져오는 것이 아니라 필요한 시점에 가져오는 방식입니다. 이를 위해 프록시 객체를 사용하는데, 프록시 객체는 실제 데이터를 가지고 있지 않고 필요할 때 데이터를 조회합니다.

그래서 OrderDto에서 orderItems가 프록시 객체로 인식되어 null로 반환되는 것 같습니다. 프록시 객체를 실제 객체로 초기화하기 위해서는 프록시 객체를 접근할 때 DB에 실제로 접근해야 합니다. 이렇게 하려면 OrderDto에서 orderItems에 접근하는 코드가 필요합니다.

만약 orderItems에 접근하는 코드가 없는 상황이라면, orderItems가 null로 반환될 수 있습니다. 따라서 orderItems를 사용하기 전에 해당 필드에 접근하는 코드를 추가하시면 됩니다.

저는 이렇게 이해하고 답변을 드렸는데, 만약 다른 이유로 null이 반환되는 것이라면 보다 자세한 코드 혹은 에러 로그 등을 알려주시면 더 정확한 답변을 드릴 수 있을 것 같습니다.

더 궁금한 점이나 추가로 도움이 필요하시면 언제든지 말씀해주세요. 좋은 하루 되세요.

이찬원님의 프로필 이미지
이찬원
질문자

제가 원하는 답변이 아닌것 같습니다 ㅠㅠ