글 삭제 기능을 개발하다가 재밌는 일이 벌어졌다.
위는 회원 정보,
아래쪽은 회원이 작성한 글 정보이다.
자 이제 글을 삭제 하겠다!!!
글은 성공적으로 삭제가 되었다.
하지만.... 회원이 왜 삭제가 됐을까요??
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post extends BaseTimeEntity {
private static final long DEFAULT_LIKE = 0L;
private static final long DEFAULT_VIEW = 1L;
@Id
@GeneratedValue
@Column(name = "post_id")
private Long postId;
@ManyToOne(fetch = LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", nullable = false)
private User user;
지금 User - Post는 OneToMany 관계이다.
Post 코드를 보면 cascade가 적용되어있는데 이렇게 되면 글이 삭제될때 당연히 유저도 삭제 되는게 당연하다.
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
위와같이, Post의 코드에서 cascade 옵션을 제거해준다.
@JsonIgnore
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Post> posts = new ArrayList<>();
그리고 User 코드에서 cascade 옵션을 추가해준다.
(유저가 탈퇴를 할 경우 작성한 글이 전부 삭제되도록 한다.)
현재 프로젝트에서 SpringData
를 이용하고 있어 아래의 Repositoyr를 작성했다.
public interface PostRepository extends JpaRepository<Post, Long> {
Boolean deletePostByPostIdAndUser(Long postId, User user);
}
Boolean
타입을 반환하고, 아래의 Service
계층 메소드에서 삭제된 정보가 없다면 Forbidden 상태를 반환하고 싶었다.
public void delete(Long userId, Long postId) {
// querydsl 대신 JpaRepository 이용
User user = userRepository.findByUserId(userId);
Boolean deleted = postRepository.deletePostByPostIdAndUser(postId, user);
if (!deleted) {
throw new ForbiddenUserException(FORBIDDEN.getMessage());
}
}
아래와 같이 500 Internal Server Error
가 발생했다.
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.Boolean (java.lang.Integer and java.lang.Boolean are in module java.base of loader 'bootstrap')
at jdk.proxy2/jdk.proxy2.$Proxy141.deletePostByPostIdAndUser(Unknown Source)
at klog.blog_project.service.PostService.delete(PostService.java:70)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
에러코드를 살펴보면 아래와 같은 항목이 있다.
Integer cannot be cast to class java.lang.Boolean
JpaRepository
를 상속받는 Repository에서 메소드를 작성 시 반환 형태를 주의해야 한다.
public interface PostRepository extends JpaRepository<Post, Long> {
Long deletePostByPostIdAndUser(Long postId, User user);
}
반환 형태를 Long
형태로 바꾸어주고 아래와 같이 메소드를 수정해주면 된다.
public void delete(Long userId, Long postId) {
// querydsl 대신 JpaRepository 이용
User user = userRepository.findByUserId(userId);
Long deletedCount = postRepository.deletePostByPostIdAndUser(postId, user);
if (deletedCount == 0) {
throw new ForbiddenUserException(FORBIDDEN.getMessage());
}
}
성공적으로 글이 삭제되는 모습을 확인할 수 있다.
ManyToOne
에 Cascade
옵션을 적용하지 않도록 조심JpaRepository
를 상속받는 Repository의 메소드의 반환형태는 Boolean
이어서는 안된다.