[스프링부트] Soft delete 구현
배경
현재 프로젝트를 진행하고 있는데,
30일이 지난 탈퇴처리 계정을 영구삭제하는 작업을 하려 한다!
즉, 이 두 가지를 구현해야 한다.
1. soft delete 구현
2. soft delete 이후 30일이 지나면 실제 영구 삭제 구현
1. soft delete 구현
soft delete란?
DB에 데이터를 실제로는 삭제하지 않고,
사용자는 데이터에 접근 불가하도록 조치한 것을 말한다.
이게 왜 필요한가? 하면
사용자가 탈퇴 요청을 하고난 이후,
10일 후 완전히 탈퇴되는 경우가 있을 수 있다.
이외에도 분석할 가치있는 유의미한 데이터는 soft delete하여 보존한다고 한다
(물론 개인정보 보호 약관에 적혀있는 대로 보존해야한다)
그렇다면 hard delete는
실제로 DB에서 데이터를 삭제하는 것을 말한다.
이를 구현하기 위해서는 2가지가 필요하다.
(1) DB에 필드 추가
(2) soft delete된 데이터에 접근 안되도록 기존 코드 수정
(1) DB에 필드 추가
DB에 deleted 필드를 true / false 로 처리하거나
deletedAt 필드에 null / 날짜 하는 등 원하는 방향으로 둘 수 있다
(처리하는 방식은 자유롭게 설정 가능하다! 어차피 그에 맞는 SQL문을 두면 되니 말이다)
나는 deletedAt 필드를 두기로 했다.
만일 null이면 삭제할 필요가 없고, time data가 있다면 탈퇴 요청이 들어온 것이다.
(2) soft delete된 데이터에 접근 안되도록 기존 코드 (Member Service) 수정
어떤 코드를 수정해야 하는가?
Member DB에 접근하는 코드를 수정해야 한다!
예를 들어 나의 DB 구조는
Member DB - Customer DB - ChatHistory DB 세 가지이고,
다른 DB가 Member DB에 접근할 때에는 Member Service를 이용하므로
Member Service 코드를 수정하면 된다.
그 부분은 이 코드에 있다.
@Transactional(readOnly = true)
public PageResponse<GetCustomerSummaryResponse> getCustomerList(String memberToken, Pageable pageable) {
Member member = memberService.getMember(memberToken);
Page<Customer> pages = customerRepository.findByMember(member, pageable);
return PageResponse.of(pages.getContent().stream().map(GetCustomerSummaryResponse::from).toList());
}
예를들어 이런 Customer 서비스 코드가 있다고 했을 때,
Member member = memberService.getOnlyMember(memberToken);
부분에서 soft delete된 데이터는 조회가 되면 안된다.
그렇다면 memberService 코드를 수정한다.
// 수정 전
@Transactional
public Member getMember(String token){
Member member = memberRepository.findByMemberToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_MEMBER));
return member;
}
// 수정 후
@Transactional
public Member getMember(String token){
Member member = memberRepository.findByMemberTokenAndDeletedAtIsNull(token)
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_MEMBER));
return member;
}
수정 후에는 memberRepository의 다른 메소드를 부르는 것이 보이는가?
Member를 가져오는 서비스 코드에서, soft delete되지 않은 데이터만 불러오도록 할 수 있다.
// MemberRepository
public interface MemberRepository extends JpaRepository<Member, MemberKey> {
// token으로 멤버 조회
Optional<Member> findByMemberToken(String memberToken);
// (추가) token으로 softDeleted되지 않은 멤버 조회
Optional<Member> findByMemberTokenAndDeletedAtIsNull(String memberToken);
}
놀라운 점은, findMyMemberTokenAndDeletedAtIsNull 함수를 구현하지 않아도 된다!
(일일이 SQL문을 작성하지 않아도 된다!)
JpaRepository에서 구현해준다!
JpaRepository에서는 메소드 이름으로 쿼리를 자동으로 생성해준다 한다 (적절한 규칙 하에)
물론, 직접 SQL문을 작성하여 구현할 수 있으며
// MemberRepository
public interface MemberRepository extends JpaRepository<Member, MemberKey> {
// token으로 멤버 조회
Optional<Member> findByMemberToken(String memberToken);
// token으로 softDeleted되지 않은 멤버 조회
Optional<Member> findByMemberTokenAndDeletedAtIsNull(String memberToken);
// 30일 이상 지난 soft delete 멤버 조회
@Transactional
@Query("SELECT m FROM Member m WHERE m.deletedAt <= :threshold")
List<Member> findExpiredMembers(@Param("threshold") LocalDateTime threshold);
}
findExpiredMembers 메소드가 그 예시이다!
2. soft delete 이후 30일이 지나면 실제 영구 삭제 구현
soft delete된 계정을 실제로 삭제해야 할것이다.
우리팀은 30일 이후에 DB에서 완전히 삭제되도록 설정했다.
정확히는
매일 자정 soft delete된지 30일이 지난 Member 데이터 삭제하도록 하자
이 작업은 Spring Batch를 이용해 구현할 수 있다.
자세한 적용 방법은 이전 글 을 참고하자!
[스프링부트] Spring Batch와 적용방법
배경프로젝트 진행 중, Spring Batch를 적용해야 했다.정확히는 매 0시마다 DB에서 특정 데이터를 삭제해야 했다. (soft delete 된 데이터 영구삭제 구현)Spring Batch란?- 배치 프레임워크- 대용량 데이터
www.kyumin.blog
여담
이번에 프로젝트를 진행하며 soft delete라는 개념을 처음 알게 되었다.
이번과 같이 회원 탈퇴, 나아가 데이터 분석이 필요한 경우 등 다양한 사용 방법을 알게 되었고
(그동안 프론트엔드에서 처리해주었던) 개인정보보호 약관에 대해 더 생각하게 되었다 !
또, 스프링부트 JpaRepository에서 이름으로 쿼리문을 자동으로 생성해준다는게 퍽 신기하다!
다음 프로젝트에서도 soft delete, 적절하게 사용해봐야겠다!