본문 바로가기
스프링 부트3

비동기 프로그래밍과 스프링 부트 3

by 굿센스굿 2024. 12. 5.
반응형

 

스프링 부트 3은 최신 자바 기술과 스프링 프레임워크를 기반으로 비동기 프로그래밍의 강력한 도구를 제공합니다. 특히, 비동기 처리는 높은 성능과 확장성을 필요로 하는 현대 애플리케이션에서 필수적인 요소로 자리 잡았습니다. 이 글에서는 비동기 프로그래밍의 개념, 스프링 부트 3에서 이를 구현하는 방법, 그리고 실무에서 활용 가능한 세 가지 예시를 통해 이해를 돕고자 합니다.


1. 비동기 프로그래밍이란?

비동기 프로그래밍은 프로그램이 특정 작업을 기다리지 않고 다음 작업을 진행할 수 있도록 하는 프로그래밍 방식입니다.

비동기의 주요 특징:

  1. 논블로킹(Non-blocking): 실행 중인 작업이 완료될 때까지 대기하지 않고 다른 작업을 계속 처리합니다.
  2. 효율성: 자원을 효율적으로 사용하여 많은 작업을 동시에 처리할 수 있습니다.
  3. 확장성: 고부하 환경에서 뛰어난 성능을 발휘합니다.

예를 들어, 웹 애플리케이션에서 API 호출이나 데이터베이스 쿼리와 같은 작업은 완료까지 시간이 걸리기 때문에 비동기로 처리하면 전체 응답 속도를 향상시킬 수 있습니다.


2. 스프링 부트 3에서 비동기 프로그래밍

스프링 부트 3은 자바의 CompletableFuture, 프로젝트 리액터(Reactor), 그리고 스프링의 비동기 애너테이션을 통해 비동기 처리를 간단히 구현할 수 있습니다.

주요 비동기 도구 및 기술:

  1. @Async 애너테이션
    스프링에서 제공하는 간단한 비동기 처리 방식입니다. 메서드에 붙이면 별도의 쓰레드 풀에서 작업을 실행합니다.
  2. Reactor 기반의 리액티브 프로그래밍
    Mono와 Flux를 활용하여 리액티브 스트림을 구현하며, 스프링 WebFlux와 함께 비동기 웹 애플리케이션을 설계할 때 유용합니다.
  3. CompletableFuture
    자바 표준 라이브러리에서 제공하는 비동기 처리 객체로, 복잡한 비동기 작업의 조합을 쉽게 구현할 수 있습니다.

3. 비동기 프로그래밍의 예시

예시 1: @Async를 이용한 비동기 메서드

비동기 메서드를 구현하려면 @EnableAsync 설정이 필요합니다.

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.initialize();
        return executor;
    }
}

비동기 메서드 구현:

@Service
public class AsyncService {
    @Async
    public CompletableFuture<String> processTask() {
        try {
            Thread.sleep(3000); // 3초 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("작업 완료");
    }
}

비동기 호출:

@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    @GetMapping("/async")
    public CompletableFuture<String> getAsyncResponse() {
        return asyncService.processTask();
    }
}

결과적으로, /async 엔드포인트를 호출하면 3초 후에 "작업 완료" 응답이 반환되며, 서버는 다른 작업을 동시에 처리할 수 있습니다.


예시 2: WebClient와 리액티브 비동기 처리

스프링 WebFlux의 WebClient를 사용하여 외부 API를 호출합니다.

@RestController
public class WebClientController {
    private final WebClient webClient = WebClient.create();

    @GetMapping("/reactive")
    public Mono getReactiveResponse() {
        return webClient.get()
                        .uri("https://jsonplaceholder.typicode.com/posts/1")
                        .retrieve()
                        .bodyToMono(String.class);
    }
}

여기서 Mono는 단일 결과를 처리하는 비동기 객체로, 호출 완료 시 자동으로 응답을 반환합니다.


예시 3: CompletableFuture로 병렬 작업 처리

두 가지 독립적인 작업을 병렬로 처리한 후 결과를 조합합니다.

@Service
public class ParallelService {
    public CompletableFuture<Integer> task1() {
        return CompletableFuture.supplyAsync(() -> {
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            return 10;
        });
    }

    public CompletableFuture<Integer> task2() {
        return CompletableFuture.supplyAsync(() -> {
            try { Thread.sleep(3000); } catch (InterruptedException e) {}
            return 20;
        });
    }
}

결과 조합:

@RestController
public class ParallelController {
    @Autowired
    private ParallelService parallelService;

    @GetMapping("/parallel")
    public CompletableFuture<Integer> getParallelResponse() {
        return parallelService.task1().thenCombine(parallelService.task2(), Integer::sum);
    }
}

이 코드는 두 작업의 결과를 더한 값을 반환하며, 병렬 처리로 인해 작업 시간은 더 짧아집니다.


4. 비동기 프로그래밍의 장점과 유의점

장점:

  • 응답 시간 단축: 사용자 경험 개선
  • 자원 효율성: 쓰레드 블로킹 최소화
  • 확장성: 많은 요청 처리 가능

유의점:

  • 디버깅 어려움: 비동기 작업 간의 의존성 문제
  • 쓰레드 관리: 쓰레드 풀이 너무 크거나 작으면 성능에 악영향
  • 예외 처리: 비동기 작업의 예외는 별도로 관리해야 함

스프링 부트 3의 비동기 프로그래밍은 현대 애플리케이션의 성능을 극대화할 수 있는 강력한 도구입니다. 위의 예제를 바탕으로 비동기 작업을 성공적으로 구현해 보세요!

반응형