@Async 비동기간 발생할 수 있는 문제

Kevin·2025년 4월 24일
0

Spring

목록 보기
26/27
post-thumbnail

서론

이번에 제목에서처럼 @Async 어노테이션을 통해서 비동기를 구현 했을 때 발생했던 문제들을 공유 하고자 글을 작성 하게 되었다.

비동기로 로직을 구현 할 때 아래 문장을 유념한다면, 아래의 문제들을 겪지 않을 것이다.

호출 로직과 비동기 로직은 별도 쓰레드에서 수행된다.

사실 너무 당연한 내용이지만, 구현에 급급하여 진행 하다보면 나의 경우에는 놓치게 되는 일이 생겼던 것 같다.

아래의 로직을 살펴보면서, 발생했던 이유들을 하나씩 살펴보자.


본론

호출 로직

    @Override
    public void getDTOList(Map<String, Object> paramMap) {
        dtoAsyncService.insertDTO(paramMap);
  }

비동기 로직

@Service
@Async
public class DTOAsyncServiceImpl {

    public void insertAdminLog(Map<String, Object> paramMap) {
				HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        Long userId = UserDetailsHelper.getId();
        
        // ...
    }
}

위 코드는 실제 문제가 발생했던 코드를 축약 시켜놓은 버젼이다.

위 코드에서 어떠한 부분이 문제들을 발생시켰는지에 대해서 알아보자.


1. HttpServletRequest 관련 문제

HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

호출 로직과 비동기 로직은 별도 쓰레드에서 수행된다.

문제가 발생했던 이유를 살펴보기 전에 위 문구를 다시 한번 유념하자.

해당 코드는 Client의 IP를 확인 하기 위해 작성 되었다.

이 때 비동기 메서드는 별도의 스레드에서 실행 되기 때문에, RequestContextHolder는 현재 스레드에 바인딩된 HttpServletRequest를 알 수 없다.

그렇기 때문에 내가 의도했던 Client의 IP를 얻을 수 없었다.

해당 문제는 호출자에서 IP를 구한 후 비동기 메서드 인자로 넘겨주어 해결 하였다.


2. SecurtyContext 관련 문제

Long userId = UserDetailsHelper.getId();

UserDetailsHelper.getId() 메서드는 내부적으로 SecurityContextHolder에 접근해 인증 객체의 id를 가져오는 역할을 한다.

SecurityContextHolder는 기본적으로 ThreadLocal 방식으로 동작하기 때문에, 새로운 스레드에서는 기존의 인증 정보(SecurityContext)가 존재하지 않게 된다.

그렇기에 Client의 인증 정보를 가져오지 못하는 문제가 발생한다.

ThreadLocal은 자바에서 스레드별로 독립적인 변수를 관리하기 위한 유틸리티 클래스를 의미한다.

이를 통해 각 사용자와 연결 된 스레드에서 인증 객체를 별도로 저장하는 것이다.

→ 해당 이유를 통해 각 사용자마다 각자의 인증 객체를 메모리로부터 가져올 수 있다.


핵심적인 것은 호출자 쓰레드와 비동기 로직은 별도 쓰레드에서 수행된다는 것이다.

위 문구를 다시 상기 하며, 글을 마무리 하겠다.

profile
Hello, World! \n

0개의 댓글