Spring Boot 개발 중 학습이 필요한 내용을 정리하고,
트러블 슈팅 과정을 기록하는 포스팅입니다.
지난 포스팅에서 JPA
Delete
과정에서의 오류를 해결하고, JPA
영속성
과 연관 관계
를 고려하여 Entity
를 Delete
하는 작업에 관한 내용을 다뤘습니다. Delete
학습하던 중, DB
의Delete
작업은 Hard Delete
, Soft Delete
이렇게 두가지로 나뉘는 것을 배울 수 있었습니다.
이번 포스팅에서는 Hard Delete
와 Soft Delete
에 대해서 알아보고, Spring boot
와 JPA
를 활용하여 Soft Delete
를 구현하는 작업에 관한 글을 작성하겠습니다.
SQL
의 DELETE
명령어를 사용하여, 물리적으로 데이터를 삭제하는 방법입니다.JPA
의 Delete
의 Default
는 Hard Delete
입니다.SQL
의 UPDATE
명령어를 사용하여, Flag
나 State
등의 삭제 여부를 알수 있는 컬럼을 추가하여 삭제 여부를 나타내는 방법입니다.JPA
를 활용한 Delete
작업을 구현하는 방법은 다음과 같이 3가지로 나눌 수 있습니다.
Hard Delete
)Soft Delete
)저는 2번째 방식을 채택하였고, deleted
라는 boolean
형 타입의 칼럼을 추가해서 삭제여부를 표시했습니다. JPA
의 어노테이션을 활용하면 이러한 Soft Delete
를 손쉽게 구현 가능합니다.
@Entity
@Table(name = "video_space")
@Getter
@SQLDelete(sql = "UPDATE video_space SET deleted = true WHERE video_space_id = ?")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class VideoSpace extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "video_space_id", updatable = false)
private Long id;
//.. 중략
첫 번째로 살펴볼 어노테이션은 @SQLDelete
입니다. 해당 어노테이션은 Delete
쿼리가 발생할 때, 정의된 특정 쿼리로 바꿔서 실행시켜줍니다.
위의 예시로 해석해보자면 Delete
쿼리가 발생할때 delete = true
Update
쿼리가 발생하도록 합니다.
해당 쿼리는 transaction
이 종료되는 시점에 바껴서 나가게됩니다.
Hibernate: UPDATE video_space SET deleted = true WHERE video_space_id = ?
테스팅을 해보면, 실제로 위와 같이 delete
쿼리가 아닌 update
쿼리가 발생한 것을 확인할 수 있습니다.
@Entity
@Table(name = "video_space")
@Getter
@SQLDelete(sql = "UPDATE video_space SET deleted = true WHERE video_space_id = ?")
@Where(clause = "deleted = false") // 추가
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class VideoSpace extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "video_space_id", updatable = false)
private Long id;
//.. 중략
두 번째로 살펴볼 어노테이션은 @Where
입니다. 해당 어노테이션은 Select
쿼리가 발생할 때, 정의된 where
구문이 Default
옵션으로 추가돼서 실행됩니다.
즉 해당 Entity
를 Select
하는 모든 Select
쿼리에 where deleted = false
구문이 추가되면서 삭제되지 않은 Entity
만 조회될 수 있도록 합니다.
Hibernate: select ( .. 중략 .. ) where videospace0_.video_space_id=? and ( videospace0_.deleted = 0)
테스팅을 해보면, 실제로 위와 같이 where
끝 부분에 deleted = false
를 체크하는 구문이 붙은 것을 확인할 수 있습니다.
지금까지 JPA
를 통해서 손쉽게 Soft Delete
를 구현해봤습니다. JPA
의 어노테이션을 이용해서 생각보다 쉽고 빠르게 구현이 가능했습니다.
그런데 여기서 생각해볼 점이 있습니다. 위와 같이 Entity
단위로 @Where
어노테이션을 활용하면 모든 Select
쿼리에 Default
로 deleted = false
옵션이 들어갑니다.
이쯤에서, 우리가 Soft Delete
를 구현하는 이유에 대해서 다시 한번 생각해봐야합니다.
Soft Delete
를 구현하는 큰 이유 중 하나는 삭제된 값을 활용할 수 있게하기 위함입니다.
예를 들어, 어플리케이션 레벨에서 삭제된 데이터를 로직에 활용하거나 클라이언트 단으로 넘겨줘야할 수도 있습니다.
하지만, @Where
어노테이션을 활용한다면 Select
되면서부터 deleted = false
인 데이터를 가지고오지 않기 때문에 삭제되지 않은 데이터를 애플리케이션 레벨에서 활용하는 것이 불가능합니다.
@Where
어노테이션을 활용하면 분명 편리성 측면에서 얻는 이점이 크지만, soft delete
를 구현 함으로써 사용하고자 하는 기능을 구현하지 못할 수도 있습니다.
실제로 아래 글에서도 @when
어노테이션은 실무에서 사용하기 힘들다고 말하고 있습니다다.
@sql
,@where
등을 실무에서 사용하기가 많이 애매합니다.
왜냐하면 실무에서는 경우에 따라서 실제 어떤 데이터가 삭제되었는지 삭제된 데이터도 조회할 수 있어야 하기 때문입니다. 그래서soft delete
방법을 사용하더라도 좀 불편해도JPQL
에서 삭제 데이터를 제외하고 조회하거나 애플리케이션에서 제외합니다.
출처 : https://www.inflearn.com/questions/304378
정리를 해보자면 삭제된 데이터를 조회하기 위해서는,
@Where
을 사용하지 않고, JPQL
에서 삭제데이터를 제외하고 조회하거나, 애플리케이션 레벨에서 제어해주는 방법이 있습니다.@FilterDef
, @Filter
어노테이션을 활용하여 특정 엔티티 매니저 세션에 대해서만 필터를 따로 정의해주는 방법도 있습니다.이 부분은 비즈니스 명세상으로 프로젝트 성격과 비즈니스 기능 명세에 맞게 선택하는것이 맞는 것 같습니다. 제가 현재 진행 중인 프로젝트에 대해서는 deleted
된 데이터를 어플리케이션 레벨에서 활용하는 기능 명세가 없기 때문에 개발에 용이한 @Where
를 우선 채택하여 사용했습니다. 추후에 필요한 부분에 있으서는 @Where
어노테이션을 사용하지 않고 애플리케이션 레벨에서 삭제된 데이터를 제외하는 방식을 활용할 예정입니다.
https://www.baeldung.com/spring-jpa-soft-delete
https://velog.io/@taeha7b/hard-delete-softdelete