[JPA] FetchType.Lazy으로 인한 Jackson Serialize 에러 (InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer)

enjoy89·2024년 1월 10일
0
post-custom-banner

오류 내용

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



문제 상황

  • Item 엔티티는 아래와 같이 Brand, CoordinateLook, Category와 다대일 연관관계이다.
  • 이때 (fetch = FetchType.LAZY) 이므로 지연 로딩 설정이 된다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "brand_id")
    private Brand brand;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "coordinate_look_id")
    @JsonIgnore
    private CoordinateLook coordinateLook;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;
  • 지연 로딩은 연관된 엔티티를 실제로 사용할 때까지 로딩을 미루는 방식을 의미한다.
  • 즉, 실제로 엔티티의 데이터가 필요할 때 해당 엔티티를 로딩하는데, 이때 DB에 저장된 객체를 가져오는 것이 아닌, 프록시 객체를 사용하여 데이터를 지연 로딩하게 된다.
  • 그러므로 Jackson Serialize 에러는 지연 로딩 설정으로 비어있는 객체를 Serialize하려고 할때 발생하는 에러이다.



해결 방법

1. 지연 로딩을 즉시 로딩으로 바꾸기

지연 로딩과 즉시 로딩 비교

지연 로딩 (LAZY)
엔티티 A와 연관관계인 B를 조회할 때, 실제 엔티티 객체 대신에 가짜 객체인 프록시 객체를 조회한다.
이를 통해 실제 엔티티가 사용될 때까지 데이터베이스 조회를 지연시킬 수 있다.

즉시 로딩 (EAGER)
엔티티 A와 연관관계인 B를 조회할 때, 실제 엔티티 객체를 Outer Join을 사용하여 한 번에 조회한다.
Outer Join을 통해 A를 조회 할 때마다 B도 함께 가져오게 되면 많은 양의 데이터를 처리하게 되고, 많은 시간과 리소스를 소비하게 된다.

이처럼 즉시 로딩은 성능 저하를 야기할 수 있으므로 적절한 해결 방법이 아니다.



2. @EntityGraph 어노테이션 사용

  • 이 방식 또한, FetchType을 즉시 로딩으로 변경하는 방식이다.
  • 근본적인 Hibernate 프록시 객체와 관련된 문제를 해결하지 못하므로 적절한 대처가 아니다.



3. Response DTO를 사용

  • 해당 에러가 난 로직은 코디룩 정보 조회 시 아이템 정보도 함께 조회하려고 하는 부분이다.
  • 필요한 정보인 코디룩과 아이템 정보를 직접 엔티티를 노출하지 않고 Response DTO를 만들어서 반환하는 방식으로, 가장 일반적인 해결법이다.




DTO 추가

@Data
@NoArgsConstructor
public class CoordinateLookDto {

    private Long id;
    private String title;
    private String image;
    private List<ItemDto> items;
@Data
@NoArgsConstructor
public class ItemDto {

    private Long id;
    private String title;
    private String sales_link;
    private String image
    private Long brandId;
    private Long categoryId;




DTO 형식으로 반환하도록 Controller 수정

@GetMapping("/{id}")
public ResponseEntity<CoordinateLookDto> getCoordinateLook(@PathVariable Long id) {
        Optional<CoordinateLook> optional = Optional.ofNullable(coordinateLookAdminService.findById(id));

        return optional.map(coordinateLook -> {
            CoordinateLookDto coordinateLookResponseDto = coordinateLookAdminService.convertToResponseDto(
                    coordinateLook);
            return ResponseEntity.ok(coordinateLookResponseDto);
        }).orElse(ResponseEntity.notFound().build());

    }

위와 같이 Response DTO를 생성하여 반환하는 방식으로 Jackson Serialize 에러를 해결하였다.



Reference

https://offetuoso.github.io/blog/develop/troubleshooting/jpa/no-serializer-found-for-class/

profile
Backend Developer 💻 😺
post-custom-banner

0개의 댓글