글을 작성한 이유
Member Entity의 삭제처리(del_dt)를 SOFT_DELETE을 사용하여 처리할려고 합니다.
이때, 찾아보면 편리한 많은 기능들이 존재하는데 해당 기능들을 사용할려고했으나, 생각해보니 사용하지 않는것이 나을 것같다는 결론을 내린 과정들에 대해 정리해보려고 합니다. (물론, 제가 진행하는 프로젝트에 한해서입니다.)
SOFT_DELETE을 개발하려고 고민한 방안들
첫번째, @SQLDelete 를 통하여 DELETE 호출시 자동으로 SOFT_DELETE 처리
1. @SQLDelete 어노테이션 : JPA의 구현체인 Hibernate에서 제공하는 기능으로, Delete를 호출 시 자동으로 del_dt를 업데이트 시켜주는 어노테이션입니다.
@SQLDelete(sql = "UPDATE member SET del_dt = CURRENT_TIMESTAMP WHERE member_no=?")
적용예시로는,
MemberEntity에 SQLDelete 어노테이션을 추가해주면 끝입니다.
@Entity
@SQLDelete(sql = "UPDATE member SET del_dt = CURRENT_TIMESTAMP WHERE member_no=?")
@Where(clause = "del_dt is null")
public class Member extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long member_no;
@Column(length = 100, nullable = false, unique = true, name = "member_id")
private String member_id;
@Column(length = 100, nullable = false)
private String member_password;
@Column(length = 100, nullable = true)
private String member_nickname;
@Column(nullable = false)
private boolean member_from_social;
@Column(name = "del_dt")
private LocalDateTime del_dt;
}
이렇게 적용시키면, Jpa를 extends 하고 있는 MemberRepository 의 기본메서드인 delete를 호출할시 자동으로 SQLDelete에 있는 쿼리문이 대신 실행됩니다.
public interface MemberRepository extends JpaRepository<Member, Long> {
}
위의 2개 코드만 작성하면 올바르게 작성이 됩니다.
두번째, @Where 를 통하여 DEL_DT 의 NULL 체크를 통하여 값 가져오기
Where 어노테이션 입니다.
@Where(clause = "del_dt is null")
@Entity
@SQLDelete(sql = "UPDATE member SET del_dt = CURRENT_TIMESTAMP WHERE member_no=?")
@Where(clause = "del_dt is null")
public class Member extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long member_no;
@Column(length = 100, nullable = false, unique = true, name = "member_id")
private String member_id;
@Column(length = 100, nullable = false)
private String member_password;
@Column(length = 100, nullable = true)
private String member_nickname;
@Column(nullable = false)
private boolean member_from_social;
@Column(name = "del_dt")
private LocalDateTime del_dt;
}
이 Member Entity에 해당 어노테이션이 붙여짐으로써, SELECT 를 통하여 Member를 호출할시 항상 Member의 where문에 삭제가 되지 않은 값들만 가져오게 처리가 됩니다.
세번째, BaseEntity에 del_dt 추가하여 공통으로 관리할까?
BaseEntity의 정보들은 단순히 매핑 역할을 위한 정보이므로, del_dt는 BaseEntity가 아닌 Member Entity에 추가하는것이 나아보입니다. 만약에 Repository의 반환형이 MemberDTO로 설정하여 BaseEntity의 컬럼들을 모두 가져와서 사용하면 가능하겠지만, Repository는 일반적으로 Entity를 생성/수정하기에 Entity를 반환하므로 삭제여부를 알기위할때는 한계가 존재합니다.
( 추가로, inst_dt와 updt_dt같은경우 @CreatedDate 와 @LastModifiedDate 처럼 Spring Boot에서 제공하는 Annotation이 있지만, del_dt 같은경우 그러한 어노테이션이 존재하지 않습니다.
@Column(name = "del_dt")
private LocalDateTime del_dt;
https://passionfruit200.tistory.com/389 (이전에 BaseEntity를 활용하여 inst_dt와 updt_dt를 추가한 글)
실제로 위의 방안들을 적용했을까?
우선 결론적으로 말하자면, 위의 3가지 경우 모두 사용하지 않았습니다.
이유를 정리해보면,
@SQLDelete을 사용함으로써 발생할 수 있는 문제
예시로, 어떤 Member가 delete처리를 하여 고정된 SQLDelete가 실행되었는데, 그떄 Member가 영구탈퇴인지 혹은 일시탈퇴인지에 따라서 del_dt만 업데이트 되는것이 아닌 추가로 다른 컬럼들도 업데이트가 되어야할 필요가 있을텐데 그러한 조건들을 처리하기에 힘들어보여, 사용하지 않는것이 낫다고 판단했습니다.
@Where을 사용함으로써 발생할 수 있는 문제
예시로, 관리자단에서 삭제된 데이터를 조회를 하거나, 삭제된 데이터를 여러 조건에 따라서 작업이 필요할떄가 있을 것입니다. 이때, 이러한 조건들이 다른 개발을 진행하는데 있어서 제약조건을 준다고 생각하여 사용하지 않는 것이 낫다고 판단했습니다. ( 이떄, @Filter, @FilterDef 와 같은 어노테이션을 통해 조건을 조절할 수 있습니다. )
BaseEntity에 del_dt를 추가하지 않은 이유
del_dt의 값을 정확하게 알기 위해서는 BaseEntity가 아닌 Member Entity에 있어야 가능합니다. ( 저는 Repository가 Entity를 반환하고 있습니다. )
구현하면서 느낀점
SOFT_DEL에 대해 알아보면서, JPA에서 제공하는 SOFT_DEL 전략과 해당 전략들을 내 프로젝트나 대규모 프로젝트에 적용했을때 발생할 수 있는 Side Effect를 생각해보게 되었습니다. 또한, 개발을 할시 특정 기술에 대한 의존성이 존재하지 않아, 개발자가 자유롭게 개발을 할 수 있게 하는것이 더욱 중요하다는 생각이 들게 되었습니다.
[도움받은 글들]
How to Implement a Soft Delete with Spring JPA, stackoverflow
: https://www.baeldung.com/spring-jpa-soft-delete
JPA에서 soft_delete 쉽게처리하기 (@sqldelete, @where 사용방법과 테스트까지)
https://lahezy.tistory.com/100
BaseEntity에서 삭제기록 사용안하는 이유
https://okky.kr/questions/1321520
실무에서 사용하는 예시중 하나
https://www.inflearn.com/questions/304378/baseentity%EC%99%80-softdelete-%EC%A7%88%EB%AC%B8