Programming/Spring & Springboot

[Spring] com.fasterxml.jackson.databind.exc.InvalidDefinitionException 에러 발생 원인 및 해결 방법

오늘도출근하는다람쥐 2023. 11. 6. 19:07

★ 에러 발생 상황

1. 멤버 등록

멤버 등록

2. 제품 등록

제품 등록

3. 제품 주문

제품 주문
주문 조회 화면에 아무것도 뜨지 않는다

 

 

여기서 주문 조회를 클릭했을 때 빈 화면이 나오면서 에러 로그가 화면에 출력되었다

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->site.devdaramg.shop.dto.OrderResponseDto["orderItems"]->org.hibernate.collection.internal.PersistentBag[0]->site.devdaramg.shop.entity.OrderItem["product"]->site.devdaramg.shop.entity.Product$HibernateProxy$6bWNacdL["hibernateLazyInitializer"])

 

에러명 : com.fasterxml.jackson.databind.exc.InvalidDefinitionException

에러명으로는 무슨 에러인지 잘 모르겠다

 

좀 더 자세하게 확인 해보자 

 : No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered

   to create BeanSerializer 

뭔가 Jackson에서 에러가 발생한 것 같다

 

고민을 하면서 여러가지 시도를 해보다가 원인을 찾은 것 같다

ChatGPT 선생님께 문의드린 결과 (객체를 JSON으로 직렬화 할 때 발생하는 문제 + Lazy Loading 기능)

 

에러 원인 

 : 반환되는 값을 Jackson이 변환하지 못해서 발생하는 문제 

   

> 로그를 찍어보니 product 값이 없는 상태에서 이 값을 Jackson으로 변환하려고 하니까 문제가 발생

product에 id, name, price, stockQuantity 모두 값이 없는것을 확인할 수 있다

 

그럼 도대체 왜 값을 못가져 왔던걸까?

그 이유는 Product를 가져올 때 Lazy Loading으로 가져오게끔 해두었기 때문이다

Entity 설계를 할 때 OrderItem쪽에 지연로딩을 통해서 product를 가져오게끔 작성

 

즉, 다른 값들은 다 가지고 있지만, product 값은 지연로딩(Lazy Loading)이기 때문에 실제로는 값이 없는상태에서 Jackson 변환 시도

 

※ 지연로딩의 경우 실제로 product를 사용할 때, 쿼리를 날려서 값을 가져오는 방식

 

JPA와 Entity에 대해서 아직 잘 모르기 때문에 발생한 초보적인 실수라고 생각한다

 

 

해결 방법 3가지

1. FetchType.LAZY 부분을 제거한다(지연 로딩 => 즉시 로딩 변경)

FetchType.LAZY 제거

2.  @JsonIgnore를 문제가 발생하는 속성 위에다가 붙여주기(자동으로 Jackson 직렬화에서 제외)

@JsonIgnore를 붙여주면 직렬화에서 제외

 

3. 새로운 getter를 만들어준다

getProductInfo 메서드 생성 후 필요한 정보만 반환

반환할 때 @Getter 속성(즉, getXXX으로 시작하는 모든 메소드)는 데이터에 담아서 반환하는 방식을 이용

 

해결 코드

2번과 3번을 사용하여 문제 해결

 

에러 나는 product는 @JsonIgnore를 통해서 직렬화 되는 것을 막고, 필요한 product 데이터는 get메서드를 만들어서 가져오는 방식

문제 해결 

 

[개인적인 의견]

1번의 경우 즉시로딩으로 변경 필요

 - 하지만 우리는 지연로딩을 쓰고 싶다 (나중에 N+1등 문제를 예방하는데 도움이 된다)

 

2번의 경우 원하는 데이터를 수집할 수가 없다(JsonIgnore를 사용하기 때문에 product 정보를 가져올 수 없기 때문이다)

 

그래서 일단 3번을 적용해서 필요한 데이터를 가져오는 방식을 사용해서 코드를 수정하였다

 

☆ 가장 중요 Point : 반환 시 Entity를 반환하는게 아니라 ResponseDTO를 만들어서 반환하자

 

[요약]

REST API를 사용해서 반환할 때는 Entity가 아닌 ResponseDTO를 만들어서 반환하자

만약 Jackson 에러가 발생하면 속성의 값이 있는지 여부를 체크하자(지연로딩 체크)

 

getXXX메서드를 붙여주면 반환 데이터에 그 값이 포함된(원하는 데이터를 직접 추가할 수 있다)

 

 

 

 

아직 JPA와 Spring에 대해서 학습 진행중이기 때문에 잘못된 정보가 들어있을수 있습니다

부족하지만 저와 비슷한 경험을 하시는 분들에게 도움이 되었으면 좋겠으며, 혹여라도 잘못된 정보가 있을경우 댓글 또는 메일로 알려주시면 수정하도록 하겠습니다

 

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