Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class ErrorCode {
public static final String USER_NOT_FOUND = "USER_NOT_FOUND";
public static final String USER_ALREADY_DELETED = "USER_ALREADY_DELETED";
public static final String USER_NOT_WITHDRAWN = "USER_NOT_WITHDRAWN";
public static final String RECOVERY_PERIOD_EXPIRED = "RECOVERY_PERIOD_EXPIRED";

// Worker domain
public static final String WORKER_NOT_FOUND = "WORKER_NOT_FOUND";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.example.paycheck.domain.user.entity.User;
import com.example.paycheck.domain.user.enums.UserType;
import com.example.paycheck.domain.user.repository.UserRepository;
import com.example.paycheck.domain.user.scheduler.UserHardDeleteScheduler;
import com.example.paycheck.domain.user.service.UserHardDeleteService;
import com.example.paycheck.domain.user.service.UserService;
import com.example.paycheck.domain.user.service.UserWithdrawService;
Expand All @@ -26,6 +27,7 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;

/**
* 인증 플로우를 조율하는 서비스
Expand Down Expand Up @@ -119,6 +121,21 @@ private AuthDto.LoginResponse buildLoginResponse(User user) {
.build();
}

/**
* 탈퇴 후 복구 가능 기간(30일) 이내인지 검증한다.
* Hard-delete 스케줄러 지연/장애로 30일을 초과한 계정이 남아있어도 복구/재가입을 차단한다.
*/
private void assertWithinRecoveryPeriod(User user) {
LocalDateTime cutoff = LocalDateTime.now()
.minusDays(UserHardDeleteScheduler.RETENTION_DAYS);
if (user.getDeletedAt().isBefore(cutoff)) {
throw new BadRequestException(
ErrorCode.RECOVERY_PERIOD_EXPIRED,
"복구 가능 기간(30일)이 지난 계정입니다."
);
}
}

/**
* 탈퇴한 카카오 계정 복구 (탈퇴 취소)
* - User.deletedAt = null로 되돌림
Expand All @@ -144,6 +161,7 @@ public AuthDto.LoginResponse restoreWithKakao(String kakaoAccessToken) {
"탈퇴 상태가 아닌 계정입니다."
);
}
assertWithinRecoveryPeriod(user);

user.restore();

Expand Down Expand Up @@ -174,6 +192,7 @@ public AuthDto.LoginResponse purgeAndRegisterWithKakao(AuthDto.KakaoRegisterRequ
"이미 가입된 카카오 계정입니다."
);
}
assertWithinRecoveryPeriod(existing);
userHardDeleteService.hardDeleteUser(existing.getId());
// JPA 기본 flush 순서(INSERT → DELETE)로 인해 같은 트랜잭션에서
// 신규 가입(INSERT)이 기존 사용자 DELETE보다 먼저 실행되면
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@RequiredArgsConstructor
public class UserHardDeleteScheduler {

private static final int RETENTION_DAYS = 30;
public static final int RETENTION_DAYS = 30;

private final UserRepository userRepository;
private final UserHardDeleteService userHardDeleteService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/health", "/h2-console/**").permitAll()
.requestMatchers("/api/auth/kakao/login", "/api/auth/kakao/register", "/api/auth/refresh", "/api/auth/dev/login").permitAll()
.requestMatchers(
"/api/auth/kakao/login",
"/api/auth/kakao/register",
"/api/auth/kakao/restore",
"/api/auth/kakao/purge-and-register",
"/api/auth/refresh",
"/api/auth/dev/login"
).permitAll()
.requestMatchers("/swagger-ui/**", "/api-docs/**", "/swagger-ui.html").permitAll()
.anyRequest().authenticated()
)
Expand Down
Loading