Async Annotation을 사용하여 여러 multipartfile을 s3에 처리하는 기능을 포함하고 있는 Company Entity 생성 함수를 비동기로 실행하도록 처리하였습니다.
@Async
override fun companySignup(req: CompanySignupRequest, emailCheckCode: String, companyIntroduction: CompanyIntroductionRequest) {
if (checkEmail(req.companyContact.email, emailCheckCode)) {
...
}
그러나 test하던 중 API 자체는 200이 떴으나, 입력 값이 제대로 들어가지 않음을 확인할 수 있었습니다.
(Company는 User를 상속받고 있었으며, Inheritance 전략으로 Joined를 사용하였으나 위 API를 통해서 생성된 Company Entity는 User table에는 제대로 입력되었으나, Company 테이블에 제대로 데이터가 삽입되지 않았습니다. )
(아마도 Jpa autiditng하는 과정에서 getCurrentAuditor → SecurityContext를 참조하지 못하여 발생한 문제인 것 같습니다.)
에러가 발생한 곳 로그를 확인해보니, 아래와 같은 메시지를 throw 하고 있었습니다.
2022-10-05 22:23:59.562 INFO 1 --- [ XNIO-1 task-3] o.springdoc.api.AbstractOpenApiResource : Init duration for springdoc-openapi is: 198 ms
2022-10-05 22:25:01.905 ERROR 1 --- [AsyncExecutor-3] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected exception occurred invoking async method: public void ...
java.lang.NullPointerException: Cannot invoke "org.springframework.security.core.Authentication.getCredentials()" because the return value of "org.springframework.security.core.context.SecurityContext.getAuthentication()" is null
@Async 선언으로 병렬처리를 통한 비동기 작업을 수행할 때 기본적으로 threadHolder에 저장 후 사용하는 SecurityContext를 활용할 수 없어 이러한 문제가 발생했습니다.
하지만 이번 에러 같은 경우는 계정 생성 API이기 때문에 SecurityContext를 불러올 이유가 딱히 없어 굳이 SecurityContext를 전파시키는 방법으로 해결하지 않았습니다.
@Configuration
@EnableJpaAuditing
class AuditorAwareConfiguration: AuditorAware<Long> {
override fun getCurrentAuditor(): Optional<Long> {
====
//수정 전
SecurityContextHolder.getContext().authentication.credentials?.let {
//수정 후
SecurityContextHolder.getContext().authentication?.credentials?.let {
====
if (it == "") return Optional.empty()
return Optional.of(it.toString().toLong())
}?: return Optional.empty()
}
}
throw된 에러는 NPE이기 때문에 kotlin에서 java lib를 사용할 때 nullable 처리만 제대로 해주었다면 발생하지 않았을 문제입니다.