Soft Delete & Hard Delete란?
Soft Delete 란?
데이터를 실제로 제거하지 않고, 삭제된 것처럼 값을 변경해 논리적으로 삭제를 하는 방식입니다.
예시를 들어서 설명하자면, member 테이블에 isDelete라는 boolean 필드가 있다고 할 때 삭제를 하는 경우 이 필드의 값을 yes로 변경하는 것입니다.
그런 뒤, member를 조회할 때는 isDelete 필드가 false인 필드만 조회를 해서 isDelete 필드가 true인 레코드들은 조회가 되지 않습니다. 이를 논리적 삭제 (Soft Delete)라고 합니다.
Hard Delete 란?
데이터베이스에서 실제로 객체를 제거하는 방식입니다.
Soft Delete를 사용하는 경우
만약 유저가 실수로 삭제를 한 경우, 유저는 복구를 하고 싶어 합니다.
Soft Delete로 레코드를 복구할 수 있기 때문에 사용자 경험이 올라갈 수 있습니다.
이처럼 Soft Delete에는 많은 장점들이 있지만 복구가 가장 큰 장점이라고 생각합니다.
Soft Delete를 구현하는 여러 가지 방법
1. 특정 필드를 변경해 삭제를 구분한다.
필자가 Soft Delete를 설명할 때 예시로 든 방법입니다.
is_delete or etc.. 등 삭제를 구분할 수 있는 필드를 생성한 뒤, 해당 필드를 통해 삭제를 구분하는 방법입니다.
하지만! 이 방법은 여러가지 문제점을 가지고 있습니다.
특정 필드를 변경해 삭제를 구분하게 되면 논리적 삭제가 된 레코드는 여전히 하나의 레코드를 차지합니다.
예를 들어 member 테이블에서 name에 unique를 보장해야 하는 상황이 있다고 가정했을 때 논리적 삭제를 하게 되면 여전히 하나의 레코드를 차지하므로 조회는 되지 않는데 name은 설정할 수 없는 상황이 발생할 수 있습니다. (이는 사용자에게 버그처럼 느껴질 가능성이 있습니다)
또한 자주 CRUD가 일어나는 테이블에 특정 필드를 변경해 삭제를 구분하는 방식으로 삭제하게 되면 칼럼이 계속 쌓여 성능이 떨어질 우려가 있습니다.
2. 별도의 테이블을 생성해서 삭제된 칼럼들을 저장
https://blog.sqlauthority.com/2010/09/03/sql-server-soft-delete-isdelete-column-your-opinion/
I am firm believer of the architecture where only necessary data should be in single table and the useless data should be moved to an archived table.
위 게시물의 작성자는 논리적 삭제에서 필요한 데이터만 단일 테이블에 있고 필요 없는 데이터는 보관된 테이블로 옮겨야 하는 아키텍처를 굳게 믿는다고 했습니다.
또한 위 의견은 Soft Delete 방법을 설명, 질문하는 글에도 꾸준히 올라오는 방법입니다.
하지만 위 의견은 특정 상황에서 매우 안 좋은 방법이 될 수 있습니다.
1. 연관된 테이블이 존재하는 경우
예를 들어 member - book 연관 관계가 존재한다고 했을 때 보관된 테이블로 옮기는 방식을 채택하면 보관된 테이블(archived member)과 book 간의 연관관계가 필요합니다.
만약 archived member과 book 간의 연관 관계가 없다면 복구를 했을 때 book과 다시 연관 관계를 맺을 수 있는 방법이 없습니다.
코딩에 정답은 없듯이 맺을 수 있는 방법이 있을 수도 있습니다... 하지만 그렇게 하려면 비 효율적인 로직을 추가해야 하기에 없다고 했습니다.
그러므로 book으로 하여금 member, archived member 이렇게 두 개의 연관 관계를 형성해야 한다는 단점이 있습니다.
2. 테이블 동기화
member 테이블에 address라는 필드가 email이라는 필드로 변경되었습니다.
테이블에 변경이 생긴 뒤, 논리적 삭제를 하니 archived member에는 email이라는 필드가 없기에 저장이 되지 않습니다.
해결 방법으로는 member 테이블에 변화가 생길 때마다 archived member에도 변경 사항을 동기화시켜줘야 합니다.
이렇게 한다면 유지보수에 상당한 어려움이 생깁니다...
급하게 원본 테이블을 변경해 push를 했을 때 보관된 테이블을 동기화시키지 않으면 논리적 삭제가 되지 않는다는 큰 버그가 생깁니다.
3. Redis Soft Delete Pattern
Redis를 이용해 Soft Delete를 설명한 글이 없길래 필자가 이름을 붙여 봤다....
우선 Redis를 채택한 이유를 크게 3가지로 설명하자면:
- Redis는 NoSQL(Key - Value)이므로 원본 테이블이 변경 돼도 영향을 받지 않는다.
- TTL 기능을 통해 복구 기간을 선택할 수 있다.
- 연관 관계에 있는 테이블도 직렬화 가능. ex) member - book (N:1)이면 book도 직렬화해서 저장 가능
이렇게 3가지의 이유로 인해 필자는 Redis를 이용해 Soft Delete를 구현했다.
Member를 Soft Delete 하는 경우 DB에서는 Hard Delete를 합니다.
삭제를 할 때 삭제 된 객체를 반환받으며 이를 JSON으로 직렬화를 한 뒤, Redis에 저장합니다.
복구를 할 때는 Redis에서 JSON을 가져온 뒤, 역 직렬화를 하고 객체를 다시 DB에 저장합니다.