프로젝트 중 hard delete 에서 soft delete 로 리팩토링한 과정과 @Where, @SQLDelete 에 관한 생각이다.
Spring boot + JPA 로 프로젝트 진행 중, delete 관련 에러가 생겼고 관련 코드를 보며 앞으로의 delete 에러를 줄이기 위해서는 hard delete 가 아닌 soft delete 로 리팩토링 해야겠다는 생각을 했다.
Soft Delete vs Hard Delete
soft delete (물리 삭제) 는 SQL 의 delete 를 사용해 실제로 데이터를 삭제하는 방법이다.
hard delete (논리 삭제) 는 삭제 여부를 판단하는 필드를 만든 후, 해당 필드를 SQL 의 update 를 통해 변경하여 삭제가 되었다고 표기하는 방법이다. 이때, 다른 쿼리들은 삭제된 데이터를 가져오지 않기 위해 쿼리의 where 절에 delete = false 같은 조건을 설정해주어야 한다.
soft delete 와 hard delete 에는 저장 용량, 데이터 복 등의 다양한 장단점이 있다. 그중에 내가 생각하는 soft delete 의 큰 장점은 삭제 시 의존성을 고려하지 않아도 되는 것이다. 예를들어 User ← Member ← Board ← Image 처럼 의존성을 가지는 경우, hard delete 를 통해 User 를 삭제할 때 ON DELETE NO ACTION 이 설정되어 있다면 모든 Image, Board, Member 를 순서대로 삭제한 후에 User 를 삭제할 수 있다. 반면, soft delete 는 순서에 크게 상관없이 udpate 를 통해 삭제가 가능하다. 이를 통해 삭제 구현시 복잡함과 에러에서 벗어날 수 있게 된다.
Spring boot + JPA 환경에서 soft delete 를 구현하기 위해 우선 모든 엔티티에 deleted = false 필드를 추가하고 deleted 필드를 변경할 수 있는 메서드인 updateDeleted(boolean deleted) 도 추가했다. 이후에 해야하는 작업은 아래 두 가지였다.
- Repository 에서 select 나 join 의 where/on 절에 deleted = false 를 추가
- Service 에서 delete 메서드 내에 사용된 XXXRepository.delete(XXX) 를 XXX.updateDeleted(true) 로 변경
도메인의 수가 많이 위 작업에 많은 시간이 걸릴 것 같아 보다 편한 방법을 찾는 도중 Hibernate 가 제공하는 @Where, @SQLDelete 어노테이션의 존재를 알게되었다.
@Where 과 @SQLDelete
우선 @Where 어노테이션은 JPA를 통해 생성된 select 쿼리에 대해 설정한 조건을 where 절에 자동으로 추가해준다.
@Entity
@Where(clause = "deleted = false")
public class Example {
...
}
위와 같이 설정을 하면, 모든 select 쿼리의 where 절에 ( deleted = 0 ) 이 추가되어 나온다.
@SQLDelete 는 JPA를 통해 엔티티 삭제가 발생할 시, delete 쿼리가 아닌 입력한 sql 을 실행시킨다.
@Entity
@SQLDelete(sql = "update example set deleted = true where id = ?")
public class Example {
...
}
위와 같이 설정을 하면, JPA 의 delete 는 delete 쿼리가 아닌 update 쿼리를 실행시켜 deleted 필드를 true 로 변경한다.
@Entity
@Where(clause = "deleted = false")
@SQLDelete(sql = "update example set deleted = true where id = ?")
public class Example {
...
}
최종적으로 위와 같이 엔티티들을 수정했고, @Where 와 @SQLDelete 의 사용으로 상당한 시간이 걸릴 것 같았던 리팩토링 작업은 빠르게 마무리 되었다. 하지만 이후, 서버의 유저 관련 로직의 변경사항이 생겨 deleted = true 인 데이터까지 select 해야하는 상황이 생겼는데, 이때 문제가 발생했다.
@Where 과 @SQLDelete 의 문제
JPA 를 통한 모든 select 의 where 절에 deleted = false 가 붙어, 정작 deleted = true 를 select 할 수 없게 된 것이다. @Where 절을 삭제하자니 너무 많은 작업량이 예상되었다. JPA 에서만 작동한다는 것에 힌트를 얻어 JdbcTemplate 을 사용해 deleted = true 인 데이터를 조회하는 메서드를 만들었다.
또한, 만약 나중에 실제로 데이터를 삭제해야 하는 작업이 생긴다면 JPA 의 delete 가 update 쿼리를 생성하기 때문에, 다른 방법을 통해 delete 쿼리를 생성하는 메서드를 만들어야 하는 문제도 생긴다.
편리에만 매몰에 되어 나중에 생길 문제 상황을 보지 못한 것이다.
내 생각
Soft delete 를 구현 시, @Where 과 @SQLDelete 는 쓰지 않을 것 같다. soft delete 를 사용하면, 삭제된 데이터를 실제 조회할 수도, 복구할 수도 있을 것이고 데이터를 실제로 삭제하는 작업도 필요할 것이다. @Where 과 @SQLDelete 를 사용한다면, 필요할 때마다 JPA 를 사용하지 않고 쿼리를 생성하는 메서드를 만들어야 한다. 이는 불편함을 초래할 뿐더러 ORM 의 사용 장점또한 발휘할 수 없다.
다음에 soft delete 를 구현한다면, delete = false 를 적용해 조회하는 repository 와 그냥 조회할 수 있는 repository 를 모두 생성해야 할 것 같다.
'JAVA > JPA' 카테고리의 다른 글
[JPA] DirtyCheck vs merge (0) | 2022.05.15 |
---|---|
[JPA] getSingleResult() 와 null (0) | 2022.03.22 |
[JPA] CascadeType.REMOVE vs orphanRemoval = true (0) | 2022.03.19 |
댓글