블로그 만들기 - Entity의 Cascade + JpaRepository를 상속받는 Repository의 메소드 반환타입 조심

HotFried·2024년 4월 9일
0

Cascade

글 삭제 기능을 개발하다가 재밌는 일이 벌어졌다.

위는 회원 정보,
아래쪽은 회원이 작성한 글 정보이다.

자 이제 글을 삭제 하겠다!!!

글은 성공적으로 삭제가 되었다.

하지만.... 회원이 왜 삭제가 됐을까요??


원인

@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 옵션을 추가해준다.
(유저가 탈퇴를 할 경우 작성한 글이 전부 삭제되도록 한다.)


JpaRepository의 메소드 반환타입

문제점

현재 프로젝트에서 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());
        }
    }

성공적으로 글이 삭제되는 모습을 확인할 수 있다.


요약

  1. ManyToOneCascade 옵션을 적용하지 않도록 조심
  2. JpaRepository를 상속받는 Repository의 메소드의 반환형태는 Boolean이어서는 안된다.
profile
꾸준하게

0개의 댓글