"org.springframework.http.converter.HttpMessageConversionException:
Typedefinition error: [simple type, class
org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]\r\n\tat
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.wri
teInternal(AbstractJackson2HttpMessageConverter.java:492)\r\n\tat
org.springframework.
예를 들어, Post 엔티티와 PostComment 엔티티가 OneToMany와 ManyToOne 관계로 서로 연결되어 있다.
예시:
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<PostComment> comments;
}
@Entity
public class PostComment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
}
Post는 comments 리스트를 통해 여러 PostComment를 참조하고, PostComment는 post 필드를 통해 다시 Post를 참조한다.
즉, Post → PostComment → Post → PostComment → ...가 무한 반복되는 재귀 참조가 발생한다.
Spring에서 Post 엔티티를 JSON으로 변환(Serialization)하려고 할 때, Jackson(혹은 기타 JSON 처리 라이브러리)이 Post와 PostComment 간의 무한 참조를 처리하지 못해 오류가 발생한다.
오류 메시지에서 org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor가 언급되는 이유는 Hibernate의 지연 로딩(Lazy Loading) 때문이다.
Hibernate는 관계된 엔티티를 Proxy 객체로 관리하며, 직렬화 시 이 Proxy 객체를 Jackson이 올바르게 처리하지 못할 경우 에러가 발생한다.
직렬화 : 객체(Object)를 바이트(byte) 형태로 변환하는 과정
ex. JSON 직렬화는 자바 객체를 JSON 형식으로 변환하는 것을 의미
Jackson : 자바에서 객체를 JSON으로 변환하거나(JSON 직렬화), JSON 데이터를 객체로 변환(JSON 역직렬화)하는 데 사용되는 라이브러리
양방향 재귀는 엔티티 A와 엔티티 B가 서로를 참조하면서 끝없이 참조가 반복되는 상황을 말한다.
Post의 comments 필드를 직렬화하려고 하면, PostComment의 post 필드가 직렬화된다.Post의 comments 필드가 직렬화되면서 무한 루프가 발생한다.JSON 생성이 끝나지 않으므로 오류가 발생하거나, 매우 큰 JSON이 생성되어 성능 문제가 발생한다.
@JsonIgnore 사용한쪽 관계를 직렬화 대상에서 제외한다.
@Entity
public class PostComment {
@ManyToOne
@JoinColumn(name = "post_id")
@JsonIgnore
private Post post;
}
이 방법은 JSON 직렬화 시 PostComment의 post 필드를 무시하게 한다.
@JsonManagedReference와 @JsonBackReference 사용Jackson의 @JsonManagedReference와 @JsonBackReference를 사용하여 직렬화 순서를 지정한다.
@Entity
public class Post {
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
@JsonManagedReference
private List<PostComment> comments;
}
@Entity
public class PostComment {
@ManyToOne
@JoinColumn(name = "post_id")
@JsonBackReference
private Post post;
}
@JsonManagedReference는 직렬화의 시작점 역할을 한다.@JsonBackReference는 직렬화에서 제외되어 재귀를 방지한다.양방향 엔티티를 그대로 직렬화하지 않고, 필요한 데이터만 포함하는 DTO(Data Transfer Object)를 생성하여 반환한다.
public class PostDTO {
private Long id;
private List<CommentDTO> comments;
}
public class CommentDTO {
private Long id;
private String content;
}
Lazy Loading 문제를 해결하기 위해 Hibernate.initialize()를 사용하여 필요한 데이터를 미리 초기화한다.
clear()와 함께 사용EntityManager.clear()를 사용하여 영속성 컨텍스트를 초기화한 뒤 데이터를 다시 조회한다.
@Transactional
public void updateAndClearContext() {
// 엔티티 업데이트
Post post = postRepository.findById(1L).orElseThrow();
post.setTitle("Updated Title");
// 영속성 컨텍스트 비우기
entityManager.flush();
entityManager.clear();
// 변경된 데이터 다시 조회
Post updatedPost = postRepository.findById(1L).orElseThrow();
System.out.println(updatedPost.getTitle()); // "Updated Title"
}
양방향 재귀는 엔티티 간의 상호 참조가 무한 반복되는 상황으로, JSON 직렬화 중에 발생할 수 있다.
이를 해결하려면 @JsonIgnore나 @JsonManagedReference/@JsonBackReference를 사용하거나 DTO로 변환하여 반환하는 방법을 사용한다.
또한, 필요에 따라 Hibernate의 초기화 방법이나 EntityManager.clear() 등을 활용할 수 있다.