BindProject

[Backend] Auth모듈 : 유저 탈퇴 처리

dding-shark 2025. 6. 19. 19:21
728x90

유저 탈퇴 처리: 7일 보류 후 완전 삭제까지

1. 도입

유저 탈퇴는 단순히 User 엔티티의 삭제로 끝나지 않는다.
데이터 보존 기간, 탈퇴 후 복구 기회, 법적 보관 의무 등 다양한 현실적 이유로 인해,
즉시 삭제가 아닌 보류 후 삭제 방식이 필요하다.

우리 서비스에서는 탈퇴 요청이 들어오면 UserWithdraw 테이블에 기록하고,
7일이 지난 후 해당 유저 데이터를 완전히 삭제한다.


2. 설계 목표

  • 유저의 탈퇴 요청 시 즉시 삭제가 아닌 7일 보류 처리
  • 보류 요청은 UserWithdraw 테이블에 기록
  • 하루에 한 번 스케줄러가 보류 만료된 유저를 삭제
  • 삭제 시 연관된 권한 정보(UserRole 등)도 함께 제거

3. 설계 고려사항

고려 항목 설명
언제 실제 삭제할 것인가? 탈퇴 요청일 기준 7일 이후
탈퇴 요청은 어디 저장하는가? UserWithdraw 테이블
유저 데이터를 완전히 삭제하는가? User, UserRole 등 직접 삭제
삭제는 어떻게 트리거되는가? @Scheduled 어노테이션 기반 cron 작업
성능 이슈 고려했는가? withdrawDate에 인덱스 설정 예정

4. 구현 설명

4.1 UserWithdraw 테이블

@Entity
@Table(name = "user_withdraw", indexes = {
    @Index(name = "idx_withdraw_date", columnList = "withdrawDate")
})
public class UserWithdraw {

    @Id
    private Long userId;

    private String reason;

    private LocalDateTime withdrawDate;
}
  • withdrawDate 필드로 7일 경과 여부를 판단
  • userId@Id로 단일 키 관리

4.2 UserWithdrawService - 삭제 스케줄러

@Service
@RequiredArgsConstructor
public class UserWithdrawService {

    private final UserWithdrawRepository withdrawRepository;
    private final UserRepository userRepository;
    private final UserRoleRepository userRoleRepository;

    @Scheduled(cron = "0 0 5 * * *")
    @Transactional
    public void deleteWithdrawnUsers() {
        LocalDateTime threshold = LocalDateTime.now().minusDays(7);
        List<UserWithdraw> withdraws = withdrawRepository.findByWithdrawDateBefore(threshold);

        for (UserWithdraw withdraw : withdraws) {
            Long userId = withdraw.getUserId();
            userRoleRepository.deleteByUserId(userId);
            userRepository.deleteById(userId);
            withdrawRepository.delete(withdraw);
        }
    }
}
  • 매일 오전 5시, 7일 지난 탈퇴 요청들을 조회해 삭제
  • 연관된 UserRole 데이터도 제거

5. 테스트 전략

  • UserWithdraw에 요청된 유저가 7일 경과 시 삭제되는지 확인
  • 삭제 시 User, UserRole, UserWithdraw 전부 사라지는지 확인
  • 이미 삭제된 유저에 대해 중복 삭제되지 않는지 확인

6. 회고 및 확장 가능성

좋았던 점

  • 유저 데이터 삭제를 안전하게 지연시켜 실수나 악의적 요청에 대응 가능
  • @Scheduled 기반 처리로 외부 트리거 없이도 자동 운영 가능
  • 테이블 인덱스를 명시해 삭제 대상 조회 성능도 고려

확장 고려

  • 탈퇴 사유 별 분석이 필요하다면 reason 필드를 정규화하거나 별도 관리 필요
  • 삭제 전 백업 또는 Kafka 이벤트 발행을 추가하면 감사 및 보관 체계까지 확장 가능
  • 개인정보 보호법 대응 위해 탈퇴 시 masking, anonymizing 절차를 추가 고려할 수 있음

마무리

유저 탈퇴는 단순한 delete 문 하나로 끝나지 않는다.
정책, 보안, 복구 등을 고려한 지연 삭제 프로세스는 필수다.

우리는 이번 구현을 통해 지연 삭제, 자동 스케줄링, 보관 이력이라는 세 가지 핵심을 갖춘 구조를 완성했다.
다음 글에서는 탈퇴와 함께 Kafka 이벤트를 발행하는 Outbox 연동까지 소개할 예정이다.


728x90