feat: 회원 탈퇴(자기 계정 삭제) — Play 정책 대응

- DELETE /api/auth/me: 본인 계정과 모든 데이터 영구 삭제
- 게시판(글/댓글/추천/이미지)·가계부 전체·포인트·결제기록·세션 → 회원 순 삭제
- WithdrawMapper(게시판/포인트/결제/세션) + BackupMapper(가계부) 재사용
- AuthServiceTest 생성자 보강

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 16:56:23 +09:00
co-authored by Claude Opus 4.8
parent be15f5b85d
commit 8142b8b518
5 changed files with 159 additions and 1 deletions
@@ -38,6 +38,8 @@ public class AuthService {
private final RedisTemplate<String, Object> redisTemplate;
private final com.sb.web.admin.service.AppSettingService appSettingService;
private final com.sb.web.auth.mapper.AuthSessionMapper authSessionMapper;
private final com.sb.web.auth.mapper.WithdrawMapper withdrawMapper;
private final com.sb.web.account.mapper.BackupMapper backupMapper;
/** 구글 OAuth 클라이언트 ID (ID 토큰 aud 검증용). 미설정 시 구글 로그인 비활성. */
@org.springframework.beans.factory.annotation.Value("${app.google-client-id:}")
@@ -289,6 +291,50 @@ public class AuthService {
}
}
/**
* 회원 탈퇴 — 사용자가 소유한 모든 데이터를 삭제하고 회원을 제거한다.
* (게시판 글/댓글/추천 · 가계부 전체 · 포인트 · 결제기록 · 세션 → 회원)
*/
@Transactional
public void withdraw(Long memberId, String token) {
Member member = memberMapper.findById(memberId);
if (member == null) {
throw new ApiException(HttpStatus.NOT_FOUND, "회원을 찾을 수 없습니다.");
}
// 1) 게시판 — 표/댓글/글/이미지 (참조 순서 고려)
withdrawMapper.deletePostVotesByMember(memberId);
withdrawMapper.deleteCommentVotesByMember(memberId);
withdrawMapper.deleteCommentVotesOnMyPosts(memberId);
withdrawMapper.deleteCommentVotesOnMyComments(memberId);
withdrawMapper.deleteCommentsOnMyPosts(memberId);
withdrawMapper.deleteCommentsByAuthor(memberId);
withdrawMapper.deletePostVotesOnMyPosts(memberId);
withdrawMapper.deletePostTagsOnMyPosts(memberId);
withdrawMapper.deletePostsByAuthor(memberId);
withdrawMapper.deleteBoardImagesByMember(memberId);
// 2) 가계부(계정) 데이터 (복구 삭제와 동일 순서)
backupMapper.deleteEntryTags(memberId);
backupMapper.deleteEntries(memberId);
backupMapper.deleteRecurrings(memberId);
backupMapper.deleteBudgets(memberId);
backupMapper.deleteBudgetIncome(memberId);
backupMapper.deleteQuickEntries(memberId);
backupMapper.deleteInvestTrades(memberId);
backupMapper.deleteInvestHoldings(memberId);
backupMapper.deleteCategories(memberId);
backupMapper.deleteTags(memberId);
backupMapper.deleteWallets(memberId);
// 3) 포인트 / 결제 / 세션
withdrawMapper.deletePointHistory(memberId);
withdrawMapper.deleteIapPurchases(memberId);
withdrawMapper.deleteAuthSessions(memberId);
// 4) 회원 삭제
memberMapper.deleteById(memberId);
// 5) 현재 세션(Redis) 정리
logout(token);
log.info("[withdraw] member {} ({}) 및 데이터 전체 삭제", memberId, member.getLoginId());
}
/** 만료된 백업 세션 정리 (매일 새벽 4시) */
@org.springframework.scheduling.annotation.Scheduled(cron = "0 0 4 * * *")
public void cleanupExpiredSessions() {