비동기 프로그래밍의 두 가지 접근법
비동기 프로그래밍은 현대 애플리케이션에서 성능을 최적화하고 사용자 경험을 향상하기 위해 필수적인 기술이다. Java에서는 비동기 프로그래밍을 지원하는 여러 도구가 있으며, 그중에서도 Spring Framework의 @Async 어노테이션과 Java 표준 라이브러리의 CompletableFuture가 많이 사용된다.
그러면 어떤 경우에 @Async를 사용하고 CompletableFuture를 사용해야 할까?
해당 포스트를 통해 @Async와 CompletableFuture의 차이점과 각각의 사용 사례를 구체적으로 살펴보자.
@Async - Spring의 간편한 비동기 처리
@Async는 Spring Framework에서 제공하는 어노테이션으로, 메서드를 비동기적으로 실행할 수 있게 해 준다. 주로 Spring 애플리케이션에서 간단하게 비동기 작업을 수행할 때 사용되며, 다음과 같은 특징을 가지고 있다.
- @Async 어노테이션을 추가하면 해당 메서드는 별도의 스레드에서 비동기적으로 실행된다.
- void, Future, CompletableFuture 등을 리턴 타입으로 사용할 수 있다.
- 기본적으로 Spring의 TaskExecutor를 사용하여 스레드를 관리한다. 필요에 따라 커스터마이징 할 수 있다.
- 비동기 메서드에서 발생한 예외는 호출자에게 직접 전달되지 않으며, Future나 CompletableFuture를 사용하여 예외를 처리할 수 있다.
사용 예제
Spring Framework에서 @Async 어노테이션을 사용하여 비동기 메서드를 실행하려면 몇 가지 설정이 필요하다. 아래에서는 @Async를 사용하기 위해 필요한 설정과 단계별로 어떻게 설정하는지에 대해 설명한다.
1. @EnableAsync 어노테이션 추가
@Async 어노테이션을 사용하려면, Spring 애플리케이션에서 비동기 처리를 활성화해야 한다. 이를 위해 @EnableAsync 어노테이션을 사용한다. 이 어노테이션은 주로 애플리케이션의 메인 클래스나 설정 클래스에 추가된다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class ShibaHolicApplication {
public static void main(String[] args) {
SpringApplication.run(ShibaHolicApplication.class, args);
}
}
2. 비동기 메서드 정의
@Async 어노테이션을 사용하여 비동기 메서드를 정의할 수 있다. 비동기 메서드는 @Async 어노테이션을 추가하고, void, Future, CompletableFuture 등의 리턴 타입을 가질 수 있다.
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public void asyncMethod() {
// 비동기적으로 실행될 코드
System.out.println("Async method execution");
}
@Async
public CompletableFuture<String> asyncMethodWithReturn() {
// 비동기적으로 실행될 코드
return CompletableFuture.completedFuture("Hello, Shiba!");
}
}
3. TaskExecutor 커스터마이징 (선택 사항)
기본적으로 Spring은 SimpleAsyncTaskExecutor를 사용하여 비동기 작업을 처리한다. 하지만 필요에 따라 커스텀 TaskExecutor를 정의하여 스레드 풀 크기, 큐 크기 등을 설정할 수 있다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("ShibaHolic-");
executor.initialize();
return executor;
}
}
4. 예외 처리
비동기 메서드에서 발생한 예외는 호출자에게 직접 전달되지 않는다. 예외를 처리하려면 Future나 CompletableFuture를 사용하여 예외를 처리할 수 있다.
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncMethodWithException() {
return CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Exception in async method");
}
return "Hello, World!";
}).exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return "Error occurred";
});
}
}
CompletableFuture - Java 표준의 강력한 비동기 처리
CompletableFuture는 Java 8에서 도입된 java.util.concurrent 패키지의 클래스이다. 비동기 작업을 수행하고 그 결과를 처리하는 데 사용되며, 다음과 같은 특징을 갖는다.
- 다양한 비동기 작업을 체이닝 하고 조합할 수 있는 유연한 API를 제공한다.
- CompletableFuture는 비동기 작업의 결과를 나타내는 객체로, 작업이 완료되면 결과를 얻을 수 있다.
- handle, exceptionally, whenComplete 등의 메서드를 사용하여 예외를 처리할 수 있다.
- thenApply, thenAccept, thenCompose 등의 메서드를 사용하여 비동기 작업을 체이닝 할 수 있다.
사용 예제
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public CompletableFuture<String> computeAsync() {
return CompletableFuture.supplyAsync(() -> {
// 비동기적으로 실행될 코드
return "Hello, World!";
});
}
public static void main(String[] args) {
CompletableFutureExample example = new CompletableFutureExample();
CompletableFuture<String> future = example.computeAsync();
future.thenAccept(result -> {
// 비동기 작업이 완료된 후 실행될 코드
System.out.println(result);
});
// 예외 처리
future.exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return null;
});
}
}
차이점
사용 목적
@Async - Spring 애플리케이션에서 간단하게 비동기 메서드를 실행하고자 할 때 사용.
CompletableFuture - Java 표준 라이브러리를 사용하여 복잡한 비동기 작업을 체이닝 하고 조합할 때 사용.
설정 및 사용법
@Async - Spring 설정이 필요하며, 어노테이션을 통해 간단하게 비동기 메서드를 정의.
CompletableFuture - Java 표준 API를 사용하며, 더 많은 유연성과 기능을 제공.
스레드 관리
@Async - Spring의 TaskExecutor를 사용하여 스레드를 관리.
CompletableFuture - 기본적으로 ForkJoinPool.commonPool()을 사용하지만, 커스텀 Executor를 지정할 수 있음.
결론
이 두 가지 도구는 각각의 장단점이 있으므로, 상황에 맞게 선택하여 사용하는 것이 좋다. Spring 기반의 애플리케이션에서는 @Async를 사용하는 것이 더 간편할 수 있으며, 순수 Java 애플리케이션이나 더 복잡한 비동기 로직이 필요한 경우에는 CompletableFuture를 사용하는 것이 좋다.
'Spring' 카테고리의 다른 글
XSS 공격 방지를 위한 필수 도구 Jsoup clean 사용법과 예시 (0) | 2024.09.24 |
---|---|
Controller에서 의존성 주입 대상 필드 값이 NULL이 되는 문제와 해결방안, 이유 (0) | 2024.02.17 |
Spring Data Redis를 활용해서 레디스 기본 명령어 사용하는 방법 (0) | 2024.02.12 |
세션을 생성하는 getSession()에 대해 알아보기 (0) | 2023.10.22 |
[Spring] 동일한 패키지 클래스 생성으로 라이브러리 직접 수정하기 (0) | 2023.10.07 |
댓글