Spring Security / / 2024. 10. 29. 23:57

@Async

@Async는 Spring에서 제공하는 비동기 처리를 지원하는 애노테이션으로, 특정 메서드가 호출될 때 별도의 스레드에서 비동기적으로 실행되도록 합니다. 이를 통해 응답 시간 단축, 병렬 처리, 서버 성능 최적화와 같은 이점을 얻을 수 있습니다.

 

1. @Async란?

 

Spring의 @Async는 메서드의 작업을 메인 스레드와 독립적으로 실행하게 함으로써, 메서드가 실행되는 동안 다른 작업을 수행할 수 있도록 합니다. 비동기 처리가 필요한 경우에 사용하며, Executor나 스레드 풀과 함께 사용하여 효율적인 비동기 환경을 구성할 수 있습니다.

 

사용 예시

 

아래는 @Async를 사용하여 메서드를 비동기적으로 호출하는 예시입니다.

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

    @Async
    public void asyncMethod() {
        System.out.println("비동기 작업 시작: " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000); // 2초 지연
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("비동기 작업 완료");
    }
}

 

설명

 

@Async가 붙은 asyncMethod()는 메인 스레드와는 별도의 스레드에서 실행됩니다.

이 메서드를 호출하면 바로 반환되며, 2초의 지연이 발생하더라도 메인 스레드는 이를 기다리지 않고 진행됩니다.

 

2. @Async 설정 방법

 

@Async 애노테이션을 사용하려면 다음과 같은 설정이 필요합니다.

 

2.1 @EnableAsync 활성화

 

@Async를 사용하려면 Spring 애플리케이션에 비동기 처리를 활성화해야 합니다. @EnableAsync 애노테이션을 설정 클래스에 추가합니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AppConfig {
    // 비동기 처리를 활성화하는 설정 클래스
}

 

2.2 Executor 설정 (선택 사항)

 

기본적으로 @AsyncSimpleAsyncTaskExecutor를 사용하여 스레드를 생성하지만, 성능과 효율성을 높이기 위해 커스텀 Executor를 설정할 수 있습니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "asyncExecutor")
    public TaskExecutor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("AsyncExecutor-");
        executor.initialize();
        return executor;
    }
}

 

CorePoolSize: 기본적으로 유지할 스레드 수입니다.

MaxPoolSize: 최대 스레드 수로, 대기열에 있는 작업을 처리하기 위해 추가로 생성할 수 있는 최대 스레드 수를 지정합니다.

QueueCapacity: 최대 대기열 크기를 설정하여, 대기열이 가득 차면 새로운 스레드를 생성하지 않고 요청을 대기시킵니다.

 

이렇게 커스텀 Executor를 설정하면, @Async 애노테이션에서 @Async("asyncExecutor")와 같이 설정하여 사용할 수 있습니다.

 

3. @Async의 반환값과 Future

 

@Async 메서드는 일반적으로 void를 반환하지만, 결과값을 기다릴 수 있는 Future 또는 **CompletableFuture**를 사용할 수도 있습니다.

 

Future 사용 예시

 

@Async 메서드가 Future를 반환하면, 작업이 완료될 때까지 대기할 수 있습니다.

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async
    public CompletableFuture<String> asyncMethodWithReturn() {
        try {
            Thread.sleep(2000); // 2초 지연
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("비동기 작업 완료");
    }
}

 

설명

 

CompletableFuture: 비동기 작업의 결과를 반환하며, get()을 통해 결과가 준비될 때까지 대기할 수 있습니다.

@Async 메서드는 반환된 Future가 완료될 때까지 작업을 유지합니다.

 

Future 결과 확인

 

메인 스레드에서 Future.get()을 호출하면 비동기 작업의 결과를 기다릴 수 있습니다.

public class Main {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> result = asyncService.asyncMethodWithReturn();
        System.out.println("비동기 작업 결과: " + result.get()); // 결과를 기다림
    }
}

 

4. @Async의 주요 활용 예

 

4.1 파일 처리, 이메일 전송 등 비동기 작업

 

@Async는 파일 처리, 이메일 전송, 푸시 알림 등 서버의 응답을 기다리지 않아도 되는 작업에서 유용하게 사용할 수 있습니다.

 

4.2 대규모 데이터 처리

 

대규모 데이터를 배치 작업으로 처리할 때, 작업을 여러 스레드로 나누어 처리하여 성능을 향상할 수 있습니다.

 

4.3 비동기 API 호출

 

외부 API를 호출할 때, 비동기로 실행하면 서버의 응답 시간이 단축되고 사용자가 빠르게 응답을 받을 수 있습니다.

 

5. @Async 사용 시 주의사항

 

5.1 트랜잭션 전파

 

@Async 메서드가 트랜잭션 내에서 호출될 경우, 트랜잭션 전파가 제대로 이루어지지 않을 수 있습니다. 트랜잭션이 필요한 작업은 비동기 방식 대신 트랜잭션 전파 속성을 명시하는 것이 좋습니다.

 

5.2 예외 처리

 

@Async 메서드에서 발생한 예외는 메인 스레드에서 잡히지 않으므로, AsyncUncaughtExceptionHandler를 통해 처리해야 합니다.

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executor;

@Configuration
public class AsyncExceptionConfig {

    @Bean
    public AsyncUncaughtExceptionHandler asyncExceptionHandler() {
        return (ex, method, params) -> {
            System.out.println("비동기 작업에서 예외 발생: " + ex.getMessage());
        };
    }
}

 

5.3 비동기 메서드 접근 제한

 

@Async 메서드는 프록시 패턴을 사용하여 비동기로 실행되기 때문에, 동일 클래스 내에서 호출하면 비동기 방식이 적용되지 않습니다. 이 경우, 외부에서 @Async 메서드를 호출하거나, 다른 서비스에 분리하여 호출해야 합니다.

 

6. @Async와 Spring Security

 

Spring Security와 함께 사용할 때, @Async 메서드에서는 SecurityContext가 유지되지 않아, 비동기 메서드 내에서 인증 정보가 사라질 수 있습니다. 이를 해결하기 위해 DelegatingSecurityContextCallable과 함께 사용할 수 있습니다.

import org.springframework.security.concurrent.DelegatingSecurityContextCallable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncSecurityService {

    @Async
    public void asyncMethodWithSecurityContext() {
        Callable<String> task = () -> {
            // SecurityContextHolder의 인증 정보 접근
            return SecurityContextHolder.getContext().getAuthentication().getName();
        };

        DelegatingSecurityContextCallable<String> securedTask = new DelegatingSecurityContextCallable<>(task);
        executor.submit(securedTask);
    }
}

 

결론

 

@Async는 Java에서 스레드 풀과 비동기 작업을 간단히 구현할 수 있게 해주는 강력한 도구로, Spring 환경에서 비동기 작업을 효율적으로 처리합니다. 서버의 리소스를 최적화하고, 비동기 요청 처리 속도를 높이려면 적절한 스레드 풀을 설정하고 @Async와 함께 사용하면 좋습니다. 또한, Spring Security와 함께 사용할 경우 인증 정보 전파에도 유용하게 사용할 수 있습니다.

'Spring Security' 카테고리의 다른 글

hasAuthority와 그 외의 다양한 표현식  (0) 2024.10.30
CSRF(Cross-Site Request Forgery)  (2) 2024.10.30
DelegatingSecurityContextCallable  (0) 2024.10.29
Callable과 Runnable  (0) 2024.10.29
Tomcat Executor  (0) 2024.10.29
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유