회원 탈퇴 기능을 소프트 삭제 방식으로 구현하기 위해, MemberStatus Enum 클래스를 추가하여 멤버의 상태를 ACTIVE 또는 DELETED로 구분했습니다. 이 후, 이미 회원 탈퇴한 사용자가 재가입하려는 경우 예외처리를 위해 회원가입 API에서 UserDetails
객체를 사용하여 멤버의 상태를 검증하려고 했습니다.
if (userDetails.getMemberStatus().equals(MemberStatus.DELETED)) {
throw new MemberException("이미 탈퇴한 사용자입니다.");
}
하지만 회원가입 API에서 UserDetailsImpl
객체를 이용해 상태를 확인하려는 과정에서 403 Forbidden 에러가 발생했고, 회원 상태 정보를 가져오려는 userDetails.getMemberStatus()
에서 null 포인터 예외가 발생했습니다.
UserDetailsImpl
객체의 문제 추적:
MemberStatus
가 Member
클래스에서 초기화되지 않거나 UserDetailsImpl
클래스에서 상태를 제대로 받지 못해 userDetails.getMemberStatus()
가 null이 되는 것으로 추정했습니다.UserDetailsImpl
클래스의 메서드를 여러 차례 수정하고 디버깅했지만, 변화가 없었습니다.permitAll()
설정 확인:
WebSecurityConfig
의 설정을 재점검했습니다. 아래와 같이 회원가입과 로그인 경로는 permitAll()
로 설정되어 인증 없이 접근 가능하도록 되어 있었습니다.http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers("/api/auth/**").permitAll() // 회원가입과 로그인 경로 인증 없이 접근 허용
.anyRequest().authenticated() // 그 외 경로 인증 요구
);
permitAll()
설정된 경로에서는 인증을 요구하지 않기 때문에 Spring Security가 UserDetails
객체를 생성하지 않습니다. 이로 인해 UserDetailsImpl
의 객체가 null이었고, userDetails.getMemberStatus()
호출 시 null 포인터 예외가 발생했던 것입니다.최종 해결 방안:
UserDetails
객체 없이 MemberStatus
를 확인하기 위해, 회원가입 시에는 단순히 중복된 이메일이 있는지 확인하는 방식으로 탈퇴한 회원의 재가입을 막기로 했습니다.permitAll()
설정과 UserDetails
객체 생성의 관계이번 문제를 통해 Spring Security에서 permitAll()
설정이 된 경로에서는 인증이 필요하지 않아 UserDetails
객체가 null로 처리된다는 점을 알게 되었습니다. 인증 없이 접근 가능한 경로에서는 SecurityContext
에 사용자 정보가 설정되지 않으므로, 인증 객체가 생성되지 않습니다. 따라서 회원가입, 로그인과 같은 인증이 필요 없는 경로에서는 UserDetails
가 null임을 염두에 두어야 합니다.
단순히 회원 상태를 검증하려는 의도로 UserDetails
를 참조했지만, 인증 절차가 생략된 경로에서는 UserDetails
객체가 생성되지 않는다는 점을 미처 고려하지 못했습니다. 이번 경험을 통해 Spring Security의 설정이 어떻게 SecurityContext
와 UserDetails
생성에 영향을 미치는지 이해하게 되었으며, 앞으로는 인증이 필요 없는 경로에서 UserDetails
접근을 시도하지 않도록 주의해야겠습니다.