Facts
✅ 회원 탈퇴 기능
✅ 연관 관계 bug fix
✅ Til Delete 기능 bug fix
✅ Comment Delete 기능 bug fix
Findings
👻 첫번째 Error : Til Delete 기능
좋아요가 추가된 TIL의 경우 TIL 삭제가 되지 않았다.
문제 원인
연관관계가 있으면, 상위 엔티티가 삭제될 경우, 하위 엔티티도 삭제처리를 해주어야 했다.
해결
이처럼 여러 entity가 연관관계로 엮어있을 때
삭제를 구현하려면, 여러가지 방법이 있다.
그중 대표적인 것이 casecade 옵션을 주는 것이다.
cascade.REMOVE를 사용하여,
상위 엔티티(TIL)가 삭제될 때마다 하위 엔티티(Like)도 삭제되게 처리해주었다.
casecade.REMOVE는 상위 엔티티에 삭제 요청을 하면 하위 엔티티까지 해당 요청이 전이되는 것을 말한다.
👻 두번째 Error : Comment List 기능
comment가 list가 되지 않고 해당 에러가 발생이 되었다.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
org.springframework.http.converter.HttpMessageConversionException:
Type definition error:
[simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor];
nested exception is 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[1] ->
com.cdp.tdp.domain.Comment["til"] -> com.cdp.tdp.domain.Til$HibernateProxy$8eMUUmsY["hibernateLazyInitializer"])] with root cause
문제 원인
Comment에 Lazy로딩 설정이 걸려서 생긴 에러였다.
Comment Entity 안에 Til Entity의 Fetch Type이 Lazy로 설정되어 있다.
Comment가 저장이 되면 Comment를 list 해주기 위해
저장된 Comment객체를 JSON형태로 return해주는 코드가 있었는데 이 시점에서 문제가 발생한 것이었다.
문제는 2가지가 발생한다.
1) ManyToOne, OneToMany 양방향 관계에서 자식 엔티티를 조회할 때
스프링에서는 JSON 변환을 담당하는 Jackson 라이브러리를 이용해
Entity 객체를 그대로 JSON 문자열으로 변환시키게 된다.
이를 변환시키는 과정에서 Comment 객체의 필드가 Til 엔티티를 참조하고,
Til 객체의 필드가 Comment 엔티티를 참조하기에 서로를 계속 참조하는 순환 참조가 발생한다.
2) 저장이 성공한 Comment객체 정보를 JSON으로 변환하는 과정에서
Comment와 연관된 Til를 같이 가져오는 순간,
fetchType이 Lazy이기에 실제 Til객체가 아닌 프록시로 감싸져있는 hibernateLazyInitializer를
가져오려고 하기 때문에 Type definition error가 발생한다.
해결
Comment Entity의 필드 Til에다가 @JsonIgnore을 사용하여
JSON response에서 til을 제외하고 보낼 수 있도록 했다.
→ 무한 루프 에러(= 순환 참조)와 Type definition error를 해결
이외에도 해결방법을 검색해보니, fetchType자체를 EAGER로 변경해서 해결해줄 수 있지만,
FetchType은 Lazy로 유지한채 오류의 원인을 제거하기 위해서 @JsonIgnore을 사용해주었다.
LAZY의 문제를 피하기 위해 EAGER로 설정하지 않는 이유는
즉시 로딩(EAGER)은 연관관계가 필요 없는 경우에도 데이터를 조회하기 때문에
성능 문제가 발생할 수 있기 때문이다.
따라서 지연 로딩을 기본으로 하고 필요하면 페치 조인을 이용하는 것이 좋다.
👻 세번째 Error : Comment Delete 기능
comment 삭제 기능이 갑자기 안 되었다.
문제 원인
로그를 통해, 원인을 찾아보니,
팀원분이 추가해준 코드에서 id로 comment를 조회해야 하는데 til을 조회해서
해당 til을 찾지 못해 발생한 에러였다.
해결
이를 해결해주기 위해, comment를 조회하게끔 처리해주고,
comment에 해당하는 til 객체가 필요한 경우, til과 comment가 연관관계 되어있으니,
comment.getTil()을 사용하여, 가져올 수 있게 하였다.
👻 네번째 Error : chatRoom과 chatUser 연관관계 에러 수정
회원탈퇴 기능을 구현하기 위해서는
chatRoom, chatUser 둘다 user와 연관관계를 맺어줘야 함으로,
먼저 이 둘의 연관관계 에러를 수정해주어야 했다.
문제 원인
Failed to initialize JPA EntityManagerFactory: mappedBy reference an unknown target entity property: com.cdp.tdp.chat.domain.ChatUser.ChatRoom in com.cdp.tdp.chat.domain.ChatRoom.chatUsers
chatUser.ChatRoom을 찾지못한다는 error가 발생했는데
'ChatRoom이 있는데 왜 못찾지?' 생각하면서, chatRoom과 관련된 파일을 보았다.
이는 mappedby에 대문자로 써서 해당 entity property를 찾지 못한 것임을 발견했다.
해결
소문자로 시작하게끔 수정해주니, 서버는 잘 돌아갔다. 🥳
👻 다섯번째 Error : OneToOne error
Unhandled exception from message handler method
org.springframework.orm.jpa.JpaSystemException: More than one row with the given identifier was found: 8, for class: com.cdp.tdp.chat.domain.ChatUser;
문제 원인
회원탈퇴 기능을 위해, ChatUser와 User를 연관관계를 맺어주었는데,
User가 여러방의 User가 될 수 있기에 OneToMany로 양방향 매핑시켜야 하는데
OneToOne으로 매핑시켜 위의 에러가 났다.
이는 ChatUser가 여러개가 발견되었다고 알려주는 에러인데,
OneToOne으로 매핑되어 있으니 생긴 에러였다.
해결
OneToMany로 매핑시켜주어 해결했다.
Feelings
- JPA는 깊게 공부해야할 분야라는 것을 느꼈다. 📚
- 마지막 에러는 발표전에 홈페이지 접속해서 여러방의 채팅방에 접속할 때, 발생한 에러였다.
왜 테스트 코드가 필요한지 더욱더 느꼈다. 🥲
출처
책 자바 ORM 표준 JPA 프로그래밍 (김영한 저)
'Project > TIL, WIL' 카테고리의 다른 글
TIL(55) AWS RDS 연동하면서 발생한 에러 고치기 (0) | 2021.12.27 |
---|---|
TIL(54) GithubAction + ElasticBeanstalk + Docker + AWS ECR로 CI/CD 하면서 발생한 에러 고치기 (0) | 2021.12.27 |
TIL(52) Elasticbeanstalk에 HTTPS를 연결 (0) | 2021.12.27 |
TIL(51) JPA 관련 에러 고치기 (0) | 2021.12.27 |
TIL(50) S3를 이용한 사진 업로드 기능 속도 개선하기 (0) | 2021.12.27 |