일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- ORM
- spring boot
- 배포
- 백엔드
- 가상화
- vm
- spring batch
- 도커
- CS
- 웹 서버
- 스프링 배치
- Spring
- computer science
- 자바
- Spring Security
- Java
- 데이터베이스
- 스프링
- 스프링 부트
- web server
- 영속성 컨텍스트
- mysql
- JPA
- spring cloud
- Container
- 스프링 시큐리티
- virtualization
- CI/CD
- 컨테이너
- HTTP
- Today
- Total
개발 일기
[Spring Boot] Soft Delete(논리 삭제) 도입 - @SQLDelete & @SQLRestriction 본문
[Spring Boot] Soft Delete(논리 삭제) 도입 - @SQLDelete & @SQLRestriction
개발 일기장 주인 2024. 6. 2. 18:23왜 사용하게 됐는가
깃허브에 다른 사람이 개발한 코드를 보면서 그냥 memberRepository.delete()를 하면되지 왜 굳이 entity에 softDelete()를 따로 만들어서 처리하는지에 대한 의문이 계속 있었는데 사실 처음엔 그냥 넘겼다.
그런데 최근 동아리 시간에 같은 동아리원이 CRUD에서 Delete를 다루는 시간에 "저렇게 Hard Delete보다는 Soft Delete로 처리해줘야지"라고 얘기했었다. 그때 내 머리속에 위에서 언급했던 코드가 스쳐지나가면서 이게 뭐길래 쓰지? 왜 굳이 저렇게 처리해주는 것일까? 라는 궁금증이 생겼고 그래서 한번 도입해보게 됐다.
Hard Delete와 Soft Delete 어떻게 다를까?
Hard Delete(물리 삭제)와 Soft Delete(논리 삭제)는 삭제 방법에 차이가 있다.
- Hard Delete는 물리적 삭제로 흔히 알고 있을 데이터베이스에서 해당 데이터를 제거하는 삭제 방법이다.
- 반대로 논리적 삭제인 Soft Delete는 삭제를 수행해도 물리적인 데이터 자체는 데이터베이스에 그대로 남아있다. 그냥 삭제됐다는 상태만 표시해두는 것이다.
즉, DELETE로 데이터베이스의 실제 행을 삭제하는 것을 Hard Delete라고 하고 삭제 플래그를 이용해 데이터가 삭제된 것처럼 가정하는 삭제 방법을 Soft Delete라고 한다.
-- Hard Delete
DELETE FROM member WHERE memberId = ?
-- Soft Delete
UPDATE member SET activated = 0 where memberId = ?
각각의 장단점?
Hard Delete 장점
- 물리 삭제를 통해 메모리 사용량을 줄일 수 있다.
- 논리 삭제 처럼 삭제된 데이터를 고려할 필요가 없기 때문에 쿼리 성능이 향상될 수 있다.
- 구현 시 로직이 단순한다. 즉, 구현이 쉽다.
Hard Delete 단점
- 데이터를 한번 삭제하고 나면 복구할 수 없다.
즉, 그냥 삭제하면 끝이다. - 삭제된 데이터를 참조하는 다른 데이터가 있는 경우 참조 무결성 문제가 발생할 수 있다.
Soft Delete 장점
- 물리 삭제가 아니기에 데이터 복구 로직 구현이 가능해진다. 즉, 데이터 복구에 용이하다.
- 따라서 비즈니스 로직도 유연해질 수 있다.
Soft Delete 단점
- 그렇기 때문에 삭제된 내용이 클라이언트로 노출될 수도 있다.
- 삭제가 되어도 필드에 그대로 남아있기 때문에 메모리가 비효율적으로 관리된다. 즉, 데이터베이스의 용량이 커진다.
- 삭제된 상태의 데이터도 고려해야하기 때문에 쿼리 성능도 떨어진다.
언제 Soft Delete를 도입해야할까?
요즘 같은 빅데이터가 시대에 이러한 데이터 하나하나가 앞으로 서비스에 영향을 끼칠 수있다. 사용자의 정보를 분석하여 마케팅의 방향성을 정할 수도 있는것이다. 또한 주문 내역을 관리할때 주문 처리가 배달까지 완료됐다 하더라도 제거하지 않고 남겨 두면 이것 또한 귀중한 데이터가 될 수 있다.
실제 서비스의 정책들을 보면 사용자의 개인정보를 동의 하에 탈퇴 후 몇년까지 보관한다는 식으로 정책이 정해준 경우가 있다. 이런식으로 내 서비스를 구현하면서 추후에 컴플레인에 걸릴 수도 있는 데이터나 서비스에서 직접 활용할만한 데이터 등은 영구적이던 반 영구적이던 바로 물리 삭제를 하는 것이 아니라 논리 삭제로 처리하는 것이 좋다고 생각이 든다.
Soft Delete를 어떻게 구현될 수 있을까?
주로 @SQLDelete와 @SQLRestriction(@Where) 어노테이션을 통해 구현하는 것 같다. 하나씩 살펴보자.
@SQLDelete Annotation
구현 방식은 정말 단순했다.
/**
* Member Entity
*/
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SQLDelete(sql = "UPDATE member SET is_deleted = true WHERE id = ?")
public class Member extends BaseTimeEntity {
// ...
@Column(name = "is_deleted", columnDefinition = "BOOLEAN DEFAULT false")
private Boolean isDeleted;
// ...
}
/**
* MemberService
*/
@Service
@RequiredArgsConstructor
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
public void deleteMember(Long memberId) {
Member member = memberRepository.findMemberByIdOrThrow(memberId);
memberRepository.delete(member);
}
}
위와 같이 구현했다. 원래처럼 MemberRepository의 delete()를 쓰지만 원래라면 DELETE로 인해 물리 삭제가 되어야하지만 멤버 엔티티에 @SQLDelete() 어노테이션을 통해 delete() 시 DELETE 쿼리 대신에 발생할 쿼리를 직접 지정해줄 수 있었다.
그래서 위와 같이 설정하여 구현할 수 있었다.
삭제한 유저를 id_deleted 즉, soft delete 상태로 만들 수 있었다.
@Where
해당 어노테이션은 엔터티에 대한 쿼리에 추가적인 조건을 적용하는 데 사용되며 소프트 삭제된 데이터를 자동으로 필터링하는 데 유용하다. 그래서 @SQLDelete와 함께 사용하여 소프트 삭제된 데이터를 기본적으로 조회하지 않도록 할 수 있다.
멤버 전체 조회 시에 memberId가 2인 유저는 is_deleted가 true라서 탈퇴한 유저라서 저렇게 조회되면안된다. 그런데 저러한 로직을 필요로하는 API들이 많기 때문에 Default로 @Where 어노테이션을 통해 추가적인 쿼리 조건을 덧붙혀 줄 수 있다.
위와 같이 어노테이션을 추가한 결과
의도한대로 현재 탈퇴하지않은 회원만 조회되는 것을 확인할 수 있었다.
select m1_0.id,m1_0.created_date,m1_0.deleted_at,m1_0.deleted_reason,m1_0.email,m1_0.is_deleted,m1_0.last_modified_date,m1_0.member_intro,m1_0.name,m1_0.nickname,m1_0.oauth2_id,m1_0.refresh_token,m1_0.role_type from member m1_0 where (m1_0.is_deleted = 0);
발생한 코드를 봐도 뒤에 저렇게 조건이 추가로 붙는 것을 확인할 수 있었다.
@SQLRestriction
그러나 보면 현재 Where 어노테이션은 deprecated됐다. 그래서 대안을 보니 @SQLRestriction()이라는 어노테이션을 사용하는 것을 확인했다.
그래서 위와같이 @SQLRestriction을 통해 처리해줬더니 똑같은 결과를 얻을 수 있었다.
SoftDelete를 구현하기는 했지만 뭐 이 유저가 작성한 주문내역이나 게시글이나 댓글 처럼 종속적인 엔티티들에 대한 처리는 어떻게해야할지 추가적인 고민이 필요할 것 같다!
'Back-End > Spring' 카테고리의 다른 글
[Spring Boot] 연관관계 영속성 전이(CASCADE) - REMOVE, PERSIST, ALL을 중심으로 (1) | 2024.06.03 |
---|---|
[Spring Boot] Soft Delete시 연관관계 엔티티 처리 (다중 논리 삭제 처리) (0) | 2024.06.02 |
[Spring Boot] ModelMapper와 MapStruct (0) | 2024.05.31 |
[Spring Boot] 예외 처리(Exception Handling) Reference (0) | 2024.05.27 |
[Spring Boot] 스프링 프로젝트에 로깅 적용하기 (0) | 2024.05.27 |