spring boot 비동기 처리Spring 비동기 처리Spring @Async 예제Spring Boot 비동기 APISpring 비동기 응답

[Spring Boot] 비동기 처리와 메시징이란

KUKJIN LEE
KUKJIN LEE
2024년 12월 19일
284

애플리케이션 규모가 커지고 서비스 요청량이 증가함에 따라 동기적 요청/응답 방식은 시스템 부하, 응답 지연, 처리량 한계 등의 문제를 야기할 수 있습니다. 이때 비동기 처리(Asynchronous Processing)메시징(Messaging) 아키텍처는 이러한 한계를 극복하고, 유연하고 확장성 있는 시스템을 구축할 수 있습니다.

 

비동기 처리(Asynchronous Processing)란?

비동기 처리는 클라이언트 요청에 대해 서버가 즉시 처리 결과를 반환하는 대신, 백그라운드에서 처리가 완료될 때까지 기다리지 않고 바로 응답을 종료하거나, 이후에 결과를 전달받는 방식입니다.

  • 성능 및 확장성 향상: 요청을 동기적으로 처리할 때 발생하는 스레드 블로킹을 줄여, 더 많은 요청을 동시에 처리할 수 있습니다.

  • 자원 효율성 극대화: 스레드나 CPU와 같은 시스템 자원을 효율적으로 활용하여 병렬 처리 성능을 개선합니다.

  • 유연한 시스템 구성: 프런트엔드, 백엔드, 데이터 처리 파이프라인 등 다양한 계층 간의 결합도를 낮춰, 각 서비스나 컴포넌트별로 독립적으로 개발, 배포, 확장이 가능합니다.

 

메시징(Messaging)이란?

메시징은 시스템 컴포넌트 간에 데이터를 교환하기 위해 메시지 브로커(Message Broker)나 큐(Queue)와 같은 중간 매개체를 사용하는 통신 패턴입니다. 전통적인 동기 HTTP 호출과 달리, 메시징 기반 통신은 비동기적으로 동작합니다.

  • 비동기 통신: 발신 측(Producer)이 메시지를 큐나 토픽(Topic)에 넣으면, 수신 측(Consumer)은 필요할 때 메시지를 가져가 처리합니다. 이 과정은 즉각적인 응답이 필요하지 않으므로 서비스 간 느슨한 결합을 실현합니다.

  • 내장된 버퍼링: 메시지 브로커나 큐는 자연스럽게 트래픽 스파이크를 흡수하는 버퍼 역할을 합니다. 갑작스러운 요청 급증에도 사용자가 안정적으로 메시지를 처리할 수 있습니다.

  • 장애 내성(Fault Tolerance): 한 서비스가 일시적으로 다운되더라도, 메시지는 브로커에 쌓여 유지되기 때문에 해당 서비스가 복구된 후 다시 메시지를 처리할 수 있습니다.

 

이벤트 기반 아키텍처(Event-Driven Architecture)에서의 역할

이벤트 기반 아키텍처는 특정 이벤트(데이터 변화나 상태 변경 등)가 발생했을 때 이를 트리거로 하여 다른 서비스나 컴포넌트가 동작하는 구조를 의미합니다.

  • 이벤트 발행/구독(Publish/Subscribe) 모델: 특정 이벤트가 발생하면 Producer 서비스는 해당 이벤트를 토픽에 발행(Publish)하고, 구독(Subscribe) 중인 서비스들은 이 이벤트를 비동기적으로 받아 처리합니다.

  • 데이터 흐름의 명확한 분리: 각 서비스는 이벤트를 소비(Consume)하는 비즈니스 로직에만 집중하면 되며, 이벤트 전파나 전달은 메시징 인프라에 맡기므로 구현 복잡도를 낮출 수 있습니다.

  • 확장 용이성: 새로운 서비스가 이벤트를 구독하고자 할 때, 기존 서비스 코드를 수정할 필요 없이 메시지 브로커 설정만으로 연동을 수행할 수 있어 확장이 쉽습니다.

 

예시 코드

package com.example.messaging;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
  @Bean
  public DirectExchange orderExchange() {
    return new DirectExchange("order.exchange");
  }
  @Bean
  public Queue orderQueue() {
    return new Queue("order.created.queue", true);
  }
  @Bean
  public Binding orderBinding(Queue orderQueue, DirectExchange orderExchange) {
    return BindingBuilder.bind(orderQueue).to(orderExchange).with("order.created");
  }
}
package com.example.messaging;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import java.util.UUID;

@Service
public class OrderService {
  private final RabbitTemplate rabbitTemplate;
  public OrderService(RabbitTemplate rabbitTemplate) {
    this.rabbitTemplate = rabbitTemplate;
  }
  public void createOrder(String productId, int quantity) {
    Order order = new Order();
    order.setId(UUID.randomUUID().toString());
    order.setProductId(productId);
    order.setQuantity(quantity);
    rabbitTemplate.convertAndSend("order.exchange", "order.created", order);
  }
}

코드를 살펴보면, bind()를 통해서 작동합니다. 주문 생성 후 바로 응답을 반환하지만, 실제 업데이트, 후속 처리 로직은 메시지 사용자 측에서 비동기적으로 처리됩니다. 서비스와 재고 관리 서비스 간 결합도를 낮추고, 스케일 아웃 시에도 유연하게 대응할 수 있습니다.

관련 글

[SQL 입문] 필요한 데이터만 콕 집어 가져올 수 있는 WHERE 절

사용자는 테이블에 있는 모든 데이터를 다 보고 싶어 하지는 않습니다. 오히려 특정 항목에 대한 데이터만 가져오고 싶을 때가 훨씬 많습니다. 예를 들어, 어떤 사용자의 이메일이나 ID는 알고 있는데, 그 사람이 언제 우리 앱에 가입했는지 확인하고 싶다고 가정해 봅시다...

2026년 1월 27일10

SQL에서 SELECT란 무엇인가?

데이터베이스를 거대한 서류 보관함이라고 생각하면 됩니다. 보관함 안에는 수많은 데이터가 차곡차곡 쌓입니다. SELECT는 이 보관함에서 "내가 원하는 정보를 찾아줘!"라고 요청하는 명령어입니다. 이 과정을 전문 용어로 '쿼리(Query)'라고 부릅니다. &nbs...

2026년 1월 26일10

[SQL 기초] "언제 하나씩 다 넣어?" 데이터 한 번에 넣기

개발을 하다 보면 데이터베이스(DB)에 샘플 데이터를 대량으로 넣어야 할 때가 있습니다. 메뉴 100개를 추가해야 하는데 INSERT 문을 100번 쓰고 있다면? 너무 비효율적이죠! 오늘은 SQL에서 여러 데이터를 한 번에 넣는 '다중 삽입(Multiple Inse...

2026년 1월 21일13

SQL 데이터 삽입하기

데이터가 없는 데이터베이스는 연료 없는 로켓과 같습니다. 이제 'Missions' 테이블에 새로운 행(row)을 추가하는 방법을 알아보겠습니다. "삽입(Inserting)"은 '데이터 추가'를 의미합니다. 기존 Missions 테이블 ...

2026년 1월 20일12