문제

서버를 실행하고 포스트맨으로 내 파트 도메인을 테스트하던 중 친구 목록 조회 API를 실행하니까 아래와 같은 오류가 발생했다.

No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor
(through reference chain: ...["requester"]->inxj.newsfeed.user.entity.User$HibernateProxy$EWjtGRtm["hibernateLazyInitializer"])

 

 

원인

기존의 코드를 보면 서비스 레이어가 레포지토리에서 받아온 데이터를 dto에 담아 전달한다.

이때 dto는 아래와 같이 엔티티 자체를 가지고 있는 상태였다.

그리고 이 FriendRequest 엔티티는 지연로딩되는 User 엔티티를 필드로 가지고 있다.

public class FriendRequestResponseDto {
    private FriendRequest friendRequest;
}
public class FriendRequest extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "requester_id", nullable = false)
    private User requester;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "receiver_id", nullable = false)
    private User receiver;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Status status = PENDING;

    public FriendRequest(User requester, User receiver) {
        this.requester = requester;
        this.receiver = receiver;
    }
}

 

 

이 dto를 클라이언트로 전달하는 과정에서 dto의 데이터를 직렬화하게 되는데, 엔티티 자체가 dto에 담겨있으면, 엔티티 내부에 있는 User 엔티티도 직렬화를 시도하게 된다.

하지만 User는 지연로딩되기 때문에, 이 엔티티를 사용하기 전까지는 프록시 객체가 내부에 들어있다.

이 프록시 객체는 실제 객체가 아니기 때문에 위와 같은 오류가 발생하게 된다.

 

 

해결

지연로딩되어야 하는 엔티티를 직렬화하지 않도록, 엔티티 자체가 아닌 필요한 데이터만 필드로 가지는 dto로 수정했다.

그리고 서비스 레이어에서도 dto에 엔티티 자체를 넘기지 않고, 필드로 분리해서 집어넣도록 코드를 수정했다.

public class FriendRequestResponseDto {
    private String username;
    private String name;
    private String profileImageUrl;
}

 

return friendRepository.findByUserAndStatus(user, ACCEPT).stream()
        // FriendRequest -> User
        .map(friendRequest ->
                friendRequest.getReceiver().equals(user) ? friendRequest.getRequester() : friendRequest.getReceiver())
        // User -> Dto
        .map(friend -> new FriendResponseDto(
                friend.getUsername(),
                friend.getName(),
                friend.getProfileImageUrl()))
        .toList();

 

 

결과

아직 값을 넣지 않은 상태라 빈 배열을 반환했지만, 클라이언트로 제대로 값을 전달하는 것을 볼 수 있다.