이번에 새로 알게된 개념인 Soft Delete에 대해 같이 공부해보자.
먼저 가정해보자. 우리의 User가 하나 존재한다.
유저가 잘 서비스를 사용하다가, 탈퇴를 하려고 한다. 이럴 때 어떻게 처리해야 할까?
당연히 그냥 delete를 통해 user를 지워버리면 된다. 이게 JPA의 기본 수행 방법이자, Hard Delete라고 부르는 방법이다.
그런데, 요구 사항이 추가되었다. 유저가 실수로 탈퇴 했을 수 있으니 복구 기능을 추가하고 싶다.
그렇다면 어떻게 해야할까?
다양한 사용 방법이 존재하지만, 이럴 때 사용 가능한 것이, Soft Delete다.
삭제 여부를 체크하는 칼럼을 추가해서, 만약 탈퇴할 경우 user를 지우는 것이 아닌 delete_flag만 1로 업데이트 해주는 방식이다.
이러면 영구적으로 삭제되지 않고, delete_flag의 값을 where 절에 넣어서 탈퇴한 유저들의 값을 모아 볼 수도 있다.
그렇지만 위 장점만 있었으면 Soft Delete가 표준이 되었을 것이다..
@SQLDelete
라는 어노테이션을 통해, 삭제 쿼리 수행시 대체할 쿼리를 엔티티 레벨에서 제한 할 수 있다.위 단점 말고도, 나는 칼럼 중 Unique Constraint가 걸린 경우에 대해 의문이 생겼다.
만약 Nickname이라는 테이블이 있고, 닉네임은 중복일 수 없다.
이 테이블에 대해 Soft delete를 적용하면, 삭제로 표기된 튜플이더라도 DB에는 존재하기 떄문에, 한번 쓰인 닉네임은 다시는 쓸 수 없게 되어버린다.
이에 대해 찾아봤다.
ALTER TABLE users
ADD not_archived BOOLEAN
GENERATED ALWAYS AS (IF(deleted_at IS NULL, 1, NULL)) VIRTUAL;
MySQL 기준, deleted_at이 NULL이면 1이고, 아니면 NULL이 되는 Virtual 칼럼을 선언하고
ALTER TABLE users
ADD CONSTRAINT UNIQUE (email, not_archived);
그 두개를 같이 UNIQUE로 묶어버리는 방법을 사용했다.
나도 Virtual Column에 대해 처음 들어봤는데, 이는 실제 칼럼처럼 데이터는 추가되지 않기에 db 용량에는 영향을 주지 않는다. 하지만 select 구문이 실행 될 때 마다 AS 뒤에 있는 트리거가 실행되는 형태로 작동한다고 한다.
사실 나는 Soft Delete를 알고 나니까, 잘 써야할 큰 메리트를 모르겠다. 정말 삭제후 일정 기간동안 복구를 해야할 유예 기간을 준다던가 하는 경우정도가 아니면, 보통 Hard Delete가 더 좋을 것 같다. UPDATE로 인한 성능 개선보다는, 잃는 조회 성능이 더 뼈아플 것 같다.
만약 유예기간이 필요한 경우에는, 별도의 테이블에 데이터를 임시로 이관하는 방법도 떠오르기도 한다. 마지막으로 StackOverFlow에 있는
"Soft Delete는 좋은 아이디어인가요?"
라는 질문 게시글에서, 100개의 추천을 받은 답변의 첫 문장을 첨부하며 마무리한다.