Spring Security을 사용하여 로그인을 구현하던 중, StackOverflowError 예외가 발생했다.
어떤 객체가 toString()을 반복적으로 호출하면서 무한 재귀에 빠진 것으로 보인다.
오류 메시지를 보면 singletonInstance가 반복적으로 toString()을 호출하면서 예외가 발생한 것을 확인할 수 있다.
당연하지만... 내 코드에서는 toString()을 명시적으로 호출하지 않았다는 점이다.
우선,
AuthenticationManager를 설정한 유일한 지점을 확인해보기로 했다.
아래 코드는 @Configuration 클래스 안에 정의된 것으로, 서버가 시작될 때 한 번 실행되며, 요청이 들어올 때마다 실행되는 코드는 아니다.

Parameter 0 of constructor in
com.newsfeed.testagram.domain.login.controller.LoginController
required a bean of type 'org.springframework.security.authentication.AuthenticationManager'
that could not be found.
Action:
Consider defining a bean of type
'org.springframework.security.authentication.AuthenticationManager'
in your configuration.
타깃 VM에서 연결 해제되었습니다. 주소: '127.0.0.1', 전송: '소켓'
종료 코드 1(으)로 완료된 프로세스
예상대로 이건 필수적으로 필요한 메서드이므로, 추가하는 것이 맞다.
직접 소스코드를 들어가보기로 했다.
총 2개의 메서드에서 singletoneintance를 사용한다.
제일 의심스러운 부분이다.
하지만, 이것도 곧 아니라는 걸 알게되었다. sigletonInstance 가 null 일 경우에만 생성해주는 경우이다.
우리는 null 이 아니라 무한 호출에 빠졌고, 디버그에서 singleton이 null 이라는 건 보지못했다.
포기하지 않고 계속 들어가도 toString 이 자기 자신을 호출하는 부분을 찾지 못했다.

StackOverflow 글을 찾아보기로 했다.
StackOverflowError in Spring Security AuthenticationManager
이글에서 해답을 알아냈다.
문제는 아마도 LoginController에서 다음 코드로 인증을 시도할 때 발생했을 것입니다. AuthenticationManager.authenticate(..)는 내부적으로 UserDetailsService.loadUserByUsername(..)를 호출해서 해당 사용자가 존재하는지를 확인합니다. 따라서 해결 방법은, UserDetailsService 인터페이스를 구현한 새로운 Service 클래스를 생성하고, 그 안에서 loadUserByUsername(..) 메서드를 구현하는 것입니다. 이렇게 해야 Spring Security가 데이터베이스에서 사용자를 검증할 수 있습니다.
이 글을 통해 알게 된 중요한 사실은,
나는 지금까지 UserDetailsService의 구현체를 만들지 않았다는 점이다.
글 속 설명에 따르면,
AuthenticationManager.authenticate()는 내부적으로
UserDetailsService.loadUserByUsername()를 호출해서 사용자의 존재 여부를 확인한다고 한다.
이 부분이 결정적인 힌트가 되었다.
그렇다면 나도 UserDetailsService의 구현체를 직접 만들어
스프링 시큐리티가 로그인 시 이 구현체를 참조하게 하면
지금 발생한 문제를 해결할 수 있지 않을까?

진짜로 해결이 되었다.
UserDetailsService라는 인터페이스를 누군가 이미 구현해둔 개인적인 유틸 클래스나 커스텀 코드인 줄 알았다.
이게 예시로 만든 클레스가 아니라 전부 내부적으로 작동하는 클레스 였다는 걸 알았다.
즉, 내 문제는 Filter > AuthenticationManager > AuthenticationProvider > ?(이부분) 이 문제였던 것 이였다.