삽질노트 - Casacade

Junho Bae·2021년 5월 13일
3

삽질노트

목록 보기
1/1
post-thumbnail

요약 : 무작정 Casacade.ALL로 설정하지 말고, 연관관계를 잘 살펴보자.

Comment가 왜 안지워지지..?

프로젝트를 거의 마무리 하던 도중, 댓글과 post의 매핑을 잘못하고 있었다는 충격적인 실수를 발견하고, 수정을 했다. Http Response로도 잘 넘어오는 것 까지 확인하고, 전체 테스트 코드를 돌려봤는데.... 댓글 delete 부분이 에러가 나는 것이었다..ㅠㅠ

대충 외래키 무결성이 안맞는다는 얘긴데... 보아 하니까 post에 포함된 comment들이 지워지지 않는 것이 문제였다..

이유가 뭐지

지금 Comment Entity는 약간 복잡하게 섥혀있는 상태였다.

@Getter
...
@DiscriminatorValue("COMMENT")
public class Comment extends Component {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "super_comment_id")
    private Comment superComment;

    @OneToMany(mappedBy = "superComment", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> subComment = new ArrayList<>();
    
    ...
    }


@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@DiscriminatorValue("POST")
public class Post extends Component {

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "channel_id")
    private Channel channel;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL,orphanRemoval = true)
    private List<Image> images = new ArrayList<>();

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL,orphanRemoval = true)
    private List<Comment> commentList = new ArrayList<>();
    
    ....
    }

나는 포스트와 댓글 둘다 Like를 가능하게 하고 싶었고, Post-Like,Comment-Like 의 관계를 따로 두고싶지 않아서, Post와 Like의 부모 클래스를 만들고 이를 상속받게 했다.

그 상태에서, Comment는 당연히 Post와 @ManyToOne의 관계를 맺고 있었고, 대댓글을 위한 Comment와 Comment의 Recursion한 @ManyToOne의 관계를 갖고 있었다.

그러다 보니까, 사실은 같은 부모 클래스를 두고 있는 아이들이 서로를 참조하고 있다보니, 이 부분에서 뭔가 문제가 있지 않을까 싶었다.

일단 쿼리를 살펴봤다.

보아하니, 포스트와 @OneToMany로 엮여있는 또다른 Image는 Delete 쿼리가 먼저 나간다.. Image가 CASACADE.ALL로 묶여있으니, 영속성 전이를 통해서 포스트가 삭제된다면 이미지 역시 삭제되어야 하니까 잘 나갔다.

하지만 Comment delete 쿼리가 나가지 않았다.

구글링을 해본 결과, 만약 다른 여러 엔티티와 연관관계를 맺고 있을 때, 영속성 전이가 잘못 된다면 삭제가 되지 않을 수도 있다는 것 같았다.. 그래서 아까 말한 것 처럼, 서로 재귀 참조 하는 과정에서 뭔가 문제가 있지 않을까 하고 열심히 삽질을 해봤는데 사실 문제는 다른데 있었으니...

해결 : 등잔 밑이 어둡다..

문제는 그쪽이 아니라, Member와의 연관관계에 있었다. Comment와 Post는 Component를 상속받게 하였는데, Component는 멤버와 @ManyToOne으로 연관관계를 맺고 있었다.

어떻게 된 것인지 파악을 해보자면,

  1. Post가 삭제됨에 따라, 영속성 전이가 일어남 -> 따라서 이미지 역시 삭제 쿼리가 나감. ( orphan removal= true )
  2. 하지만 현재 Member는 commentList를 CasacadeType.ALL로 참조하고 있음. -> 따라서, 멤버에게 영속성 전이를 받은 Comment는 삭제 상태가 아니게 됨.
  3. Comment는 여전히 남아서 Post를 참조하려고 하는데, Post를 삭제하려는 쿼리가 나가니 여기서 외래키 무결성 문제가 발생.

의 과정이 있었던 것 같다. 따라서, Member에서 Comment를 참조하는 부분을 CasacadeType.REMOVE로 바꾸니 테스트가 통과하였따..

만약 잘못 이해한 과정이 있으면 알려주세요..

결론

Jpa를 배울 때 보통 CasacadeType.ALL로 배웠다 보니, 무의식적으로 그냥 죄다 이렇게 썼었는데, 연관관계를 잘 살펴보고 필요한 옵션을 골라야 할 것 같다.

수정

팀원들과 논의와 실험 겨과, CasacadeType.REMOVAL이 없이 orpahnremoval=true 인 상태에서도 정상 작동하는 것을 확인했다. 물론 차이는 있지만, 원하는 기능에서는 문제가 없기 때문에 불필요한 코드로 판단, 아예 삭제하였다.

profile
SKKU Humanities & Computer Science

0개의 댓글