Spring/JPA
[JPA] Soft Delete: JPA에서 Soft Delete를 구현하는 방법, @SqlDelete, @Where
깃짱
2023. 8. 27. 11:00
반응형
프로젝트를 진행하던 도중, 우리 팀은 모든 데이터를 soft delete하기로 했다.
먼저 Soft Delete, Hard Delete의 차이점부터 간단히 짚고 넘어가보자!
💋 Soft Delete VS Hard Delete
✔ Soft Delete
- 논리적 삭제
- 데이터를 실제로 삭제하지 않고, 삭제 플래그를 설정하여 삭제된 것으로 표시하는 방법
- 데이터는 시스템에서 더 이상 사용되지 않지만, 필요한 경우 되돌릴 수 있다.
- 공간 절약과 데이터 관리의 편의성을 위해 유용하지만, 복구가 불가능하므로 신중하게 사용해야 한다.
- 데이터 보존을 위해 유용하며, 실수로 삭제된 데이터를 복구할 수 있는 장점이 있다.
- 삭제된 데이터를 유지하려면 추가적인 저장 공간이 필요하고, 데이터베이스 쿼리에 삭제 여부를 고려해야 할 수도 있다.
✔ Hard Delete
- 물리적 삭제
- 데이터를 실제로 삭제하는 방법
- 삭제된 데이터는 시스템에서 완전히 제거되어 복구할 수 없다.
💋 JPA에서 Soft Delete 구현하기
Soft Delete를 구현하고 싶다면, 크게 아래 두 가지를 지켜야 한다.
- 엔티티 삭제는 실제 DELETE 쿼리가 아니라, UPDATE로 deleted을 true로 만드는 것이다.
- @SqlDelete 어노테이션으로 구현
- 모든 SQL 쿼리에 where deleted = false를 붙여야만 삭제되지 않은 데이터에 대한 조회, 변경 작업을 수행할 수 있다.
- @Where 어노테이션으로 구현
✔ Entity에 @SqlDelete 어노테이션 추가
Soft Delete를 도입하는 경우, 삭제 로직이 실행될 때 DELETE 쿼리가 아니라, UPDATE 쿼리를 통해서 deleted 필드의 값만 true로 변경해야 한다.
@SqlDelete를 통해서 JpaRepository의 메서드 중 delete()를 호출했을 때, DELETE 쿼리 대신 실행시킬 SQL문을 설정할 수 있다.
아래와 같이 설정해주면, JpaRepository의 delete() 메서드가 호출되었을 때 작성한 update 쿼리가 나간다.
@NoArgsConstructor(access = PROTECTED)
@Getter
@SQLDelete(sql = "UPDATE cafe_policy SET deleted = true WHERE id = ?")
@Entity
public class CafePolicy extends BaseDate {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
private Integer maxStampCount;
private String reward;
private Integer expirePeriod;
private Boolean deleted = Boolean.FALSE; // 기본값을 FALSE로 설정
// ...
}
SQL Delete도 마찬가지로 곧바로 쿼리가 날아가는 것이 아니라, 영속성 컨텍스트에서 변경사항이 관리되다가 트랜잭션이 끝날 때 처리된다.
✔ Entity에 @Where 어노테이션 추가
Soft Delete를 사용했을 때 모든 쿼리문에 where deleted = false를 추가해줄 어노테이션이다.
이렇게 붙여주면, 모든 요청에 대해 default 요청으로 적용된다.
@NoArgsConstructor(access = PROTECTED)
@Getter
@SQLDelete(sql = "UPDATE cafe_policy SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
@Entity
public class CafePolicy extends BaseDate {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
private Integer maxStampCount;
private String reward;
private Integer expirePeriod;
private Boolean deleted = Boolean.FALSE; // 기본값을 FALSE로 설정
// ...
}
반응형