본문 바로가기
Framework/Spring Boot

[Spring Boot] Apache Kafka 연동: MSA의 대동맥, 이벤트 주도 비동기 통신

반응형

배달 앱에서 고객이 치킨을 '결제'했다고 상상해 봅시다. 백엔드 시스템 내부에서는 어마어마한 일들이 순차적으로 벌어집니다. 1. 주문 시스템에 접수 -> 2. 라이더 배차 시스템 호출 -> 3. 고객에게 카카오톡 알림 발송 -> 4. 쿠폰 소진 처리. 만약 결제 서버가 이 4개의 서버에게 일일이 HTTP(REST API)로 전화를 걸어 "야, 결제됐어! 일해!"라고 동기식(Synchronous)으로 통신하면 어떻게 될까요? 만약 카카오톡 알림 서버가 장애로 3초간 멈춰있다면, 결제 서버도 그 3초 동안 응답을 기다리며 멈춰있게 되고 결국 사용자의 화면은 하얗게 굳어버립니다. 하나의 서버 장애가 전체 시스템의 장애로 퍼지는 '도미노 붕괴(강한 결합)' 현상입니다. 이 끔찍한 의존성을 칼같이 끊어내기 위해 도입된 구원자가 바로 Apache Kafka(아파치 카프카)메시지 큐(Message Queue) 아키텍처입니다.

이번 포스팅에서는 "글로벌 IT 기업들이 사랑하는 궁극의 비동기 아키텍처!" Spring Boot와 카프카의 연동 원리 및 이벤트 주도(EDA) 통신에 대하여 다뤄보겠습니다.

📫 1. 카프카(Kafka) 핵심 용어 3총사: 토픽, 프로듀서, 컨슈머

카프카는 거대한 '비동기 우체국'입니다. HTTP(REST) 방식이 상대방에게 직접 '전화'를 거는 것이라면, 카프카는 상대방의 우체통에 '편지(Message)'를 던져놓고 나는 내 할 일을 하러 가는 방식입니다.

  • Topic (토픽 = 우체통): 카프카 내부에 만들어지는 데이터를 분류하는 논리적 방(우체통)입니다. 예: payment-create-topic
  • Producer (생산자 = 편지 보내는 놈): 메시지(데이터)를 만들어서 토픽에 쏘아 보내는(Publish) 서버입니다. (예: 결제 완료 서버)
  • Consumer (소비자 = 편지 읽는 놈): 토픽에 웅크리고 대기하다가 메시지가 들어오면 즉시 꺼내서 읽고 처리하는(Subscribe) 서버입니다. (예: 알림 서버, 주문 서버)

가장 강력한 점은 알림 서버가 죽어있더라도, 결제 서버는 알 바 없이 카프카에 메시지를 쏠 수 있다는 것입니다. 카프카가 메시지를 안전하게 디스크에 보관하고 있다가, 알림 서버가 고쳐져서 다시 살아나면 그때 밀려있던 메시지들을 와다다 꺼내어 처리하게 됩니다. 완벽한 '결합도 낮춤(Decoupling)''안전성'이 확보됩니다.


🚀 2. Spring Boot 프로듀서(Producer) 세팅: 메시지 쏘기

스프링 부트는 카프카와의 통신을 위해 아주 추상화가 잘 된 KafkaTemplate이라는 무기를 제공합니다.

// 의존성 추가: implementation 'org.springframework.kafka:spring-kafka'

@Service
@RequiredArgsConstructor
public class PaymentProducer {

    // 🌟 스프링이 제공하는 카프카 통신 템플릿
    private final KafkaTemplate<String, String> kafkaTemplate;
    
    // 카프카의 방(우체통) 이름 지정
    private static final String TOPIC = "payment-success-topic";

    public void completePayment(Long orderId, int amount) {
        // 1. DB에 결제 완료 저장... (빠르게 처리)
        
        // 2. 다른 서버들을 기다리지 않고 카프카 우체통에 "결제됐어!" 라는 메시지(JSON)만 툭 던지고 퇴근합니다!
        String message = String.format("{\"orderId\":%d, \"amount\":%d}", orderId, amount);
        kafkaTemplate.send(TOPIC, message);
        
        log.info("카프카 토픽으로 비동기 메시지 전송 완료: {}", message);
    }
}

👂 3. Spring Boot 컨슈머(Consumer) 세팅: 메시지 듣기

이제 메시지를 받아먹을 '알림 서버(Notification Server)'의 코드를 보겠습니다. @KafkaListener 어노테이션 하나만 달아두면, 메시지가 우체통에 들어오는 순간 스프링이 귀신같이 알아채고 이 메서드를 팍! 하고 실행시켜 줍니다.

@Service
@Slf4j
public class NotificationConsumer {

    // 🌟 이 어노테이션이 붙은 메서드는 24시간 내내 해당 토픽을 감시(Listen)하고 있습니다.
    // groupId는 컨슈머들의 소속 그룹을 의미하며, 메시지를 중복해서 처리하지 않도록(분산 처리) 도와주는 핵심 키입니다.
    @KafkaListener(topics = "payment-success-topic", groupId = "notification-group")
    public void consumePaymentMessage(String message) {
        log.info("📩 카프카로부터 새 메시지 수신: {}", message);
        
        // JSON 파싱 후 유저에게 카카오톡 발송 로직 처리...
        // 3초가 걸려도, 결제 서버에게는 전혀 영향을 주지 않습니다! (비동기의 마법)
    }
}

🎯 4. 마무리 및 다음 단계

지금까지 하나의 장애가 전체 서버를 터뜨리는 REST API 동기 통신의 끔찍한 한계를 부수고, '내가 할 일만 하고 편지는 우체통에 던져버린다'는 이벤트 주도(Event-Driven) 마이크로서비스 생태계의 절대 대동맥, Apache Kafka와 Spring Boot 연동의 원리에 대해 다루어 보았습니다. 이제 여러분의 서버 아키텍처는 어떠한 폭우(트래픽) 속에서도 유연하게 견디는 진정한 MSA로 진화했습니다.

서버 내부 아키텍처는 완벽하게 짰습니다. 그런데 이제 협업을 해야 할 프론트엔드 개발자가 다가와 묻습니다. "백엔드 개발자님, 회원가입 API 주소는 뭐고, 파라미터는 뭘 넘겨야 하죠? 리턴 JSON 양식 좀 워드나 엑셀 파일로 정리해서 주실래요?" 만약 코드가 수정될 때마다 엑셀 문서를 수동으로 고치고 있다면 엄청난 노가다와 휴먼 에러가 발생합니다. 코드와 문서가 일치하지 않아 프론트엔드와 매일 멱살을 잡고 싸워야 하죠. 이어지는 24단계 포스팅에서는 "코드를 짜면 그 즉시 화려하고 인터랙티브(Interactive)한 API 문서 웹페이지가 1초 만에 자동 생성된다!" 백엔드 개발자의 퇴근을 앞당기는 기적의 툴, Swagger(Springdoc)와 REST Docs의 완벽 연동 실무 노하우에 대해 아주 뼈 때리게 파헤쳐 보겠습니다!

반응형