어제 목표 & 오늘 완료한 한 일
- 알고리즘 문제
- 자바 종합 문법 복습 (3주차)
- 스프링 기초 개인과제 해설 강의
- 스프링 심화 과제 (테스트 코드 작성)
- JPA 심화 강의듣기
플러스 주차 복습 과제- CS 공부 (운영체제)
내일 목표
- 알고리즘 문제
- 자바 종합 문법 복습 (3주차)
- 스프링 기초 개인과제 해설 강의
- 스프링 심화 과제 (테스트 코드 작성)
- JPA 심화 강의듣기
- 플러스 주차 복습 과제
- CS 공부 (운영체제)
발생한 오류와 해결 방법
1️⃣. 게시글 상세 조회시 댓글 목록도 보여지게끔 코드를 작성했는데 아래와 같은 에러가 발생했다.
2023-12-20T17:28:32.517+09:00 ERROR 25840 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]] with root cause 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: com.example.market.domain.post.dto.response.DetailPostResponseDto["commentList"]->java.util.ArrayList[0]->com.example.market.domain.comment.entity.Comment["user"]->com.example.market.domain.user.entity.User$HibernateProxy$3DfpmUkK["hibernateLazyInitializer"])
해당 에러는 Lazy loading 설정이 걸려 발생한 에러라고 한다.
지연로딩(Lazy Loading)이란?
필요한 시점에서 연관된 Entity의 정보를 가져오는 것.
객체 조회시 항상 연관된 객체까지 함께 조회하는 것이 비효율적이기 때문에 실무에서는 모든 연관관계를 지연로딩으로 설정한다고 한다.
지연로딩을 사용하면 실제 엔티티가 사용되기 전까지 DB 조회를 지연하게 되기 때문에 DB 조회를 하기 전까지 실제 엔티티를 대신해줄 가짜 객체(프록시 객체)가 필요하다.
프록시 객체?
hibernate가 관리하는 객체로, 실제 엔티티를 상속받는 프록시 객체는 실제 객체의 참조를 보관하여 값을 가져올 수 있다. (값 자체를 가지고 있는 것이 아닌, 실제 엔티티의 정보를 가지고 있다는 의미.)
정리하면 프록시 객체는 실제 엔티티에서 데이터를 필요로 할 때 DB에서 데이터를 가져와 실제 엔티티에 넣어주는 역할을 하는 객체이다.
public DetailPostResponseDto getPost(Long id) {
Optional<Post> post = postRepository.findById(id);
List<Comment> comments = commentRepository.findAllByPostId(id);
if(post.isPresent()) {
return new DetailPostResponseDto(post.get(), comments);
} else {
throw new IllegalArgumentException("해당하는 게시글이 존재하지 않습니다.");
}
}
나는 위와 같이 CommentRepository에서 특정 postId를 갖는 Comment를 리스트 형태로 받아와 그것을 그대로 DetailsPostResponseDto의 생성자에 인자로 넣어주는 코드를 작성했었다.
에러 메시지의 내용을 봤을 때, 응답을 보내기 위해 JSON 형태로 변환하던 중 Comment와 관련 객체이자 지연로딩으로 설정되어 있는 User를 가져와 직렬화하는 과정에서 Hibernate Proxy로 감싸져있는 hibernateLazyInitializer를 조회하여 HttpMessageConversionException 에러가 발생했다는 것을 알 수 있다.
다시 말해, 나는 직접 Comment를 넘겨주려 했고 이 과정에서 FetchType.Lazy로 설정되어있던 User는 프록시 객체 상태라 실제 엔티티의 데이터를 가지고 있지는 않고 참조만 하고 있기 때문에 직렬화하는 과정에서 문제가 발생한 것이다.
public DetailPostResponseDto getPost(Long id) {
Optional<Post> post = postRepository.findById(id);
List<Comment> comments = commentRepository.findAllByPostId(id);
List<CommentResponseDto> commentList = new ArrayList<>();
comments.forEach(comment -> {
commentList.add(new CommentResponseDto(comment));
});
if(post.isPresent()) {
return new DetailPostResponseDto(post.get(), commentList);
} else {
throw new IllegalArgumentException("해당하는 게시글이 존재하지 않습니다.");
}
}
나는 직접 Comment를 넘겨주어 User가 프록시 객체 상태가 되는 것을 막기 위해 contents, User의 writer 등 CommentResponseDto를 통해 필요한 부분만 가져왔다. 그리고 CommentResposneDto를 리스트 형태로 DetailsPostResponseDto의 생성자에 인자로 넣어줬더니 문제를 해결할 수 있었다.
참고한 블로그
1) https://devjoy.tistory.com/87?category=1048343
2) https://velog.io/@ssssujini99/SpringJPA-%EC%A6%89%EC%8B%9C-%EB%A1%9C%EB%94%A9Eager-Loading%EA%B3%BC-%EC%A7%80%EC%97%B0-%EB%A1%9C%EB%94%A9Lazy-Loading
3) https://velog.io/@bagt/%ED%94%84%EB%A1%9D%EC%8B%9CProxy%EC%99%80-%EC%A7%80%EC%97%B0%EB%A1%9C%EB%94%A9Lazy-Loading
4) https://velog.io/@minjoon1324/org.springframework.http.converter.HttpMessageConversionException-Type-definition-error-simple-type-class-org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor
느낀점
계획했던 댓글 삭제, 수정 기능을 구현하고 게시글 상세 조회시 해당하는 게시글의 댓글까지 보이게끔 코드까지 작성하였다. 댓글 조회 부분에서 에러가 발생해 좋아요 부분까지 구현하지 못해서 아쉬움이 있긴하지만 에러가 발생한 이유를 알게 되어서 좋다. 예전에 강의를 들으면서 영속성 컨텍스트 부분이 어렵다고 느꼈었는데 공부를 하다보니 영속성 컨텍스트가 프록시 객체와 연관이 있다는 사실을 알게 되었다. 그래서 이 부분에 대해 공부를 해보고 싶다.
'TIL & WIL' 카테고리의 다른 글
[TIL] #100. 231222 (0) | 2023.12.23 |
---|---|
[TIL] #99. 231221 (0) | 2023.12.21 |
[TIL] #97. 231219 (1) | 2023.12.19 |
[WIL] #14. 231211 ~ 231217 (0) | 2023.12.18 |
[TIL] #96. 231218 (0) | 2023.12.18 |