아키텍처+MSA

[아키텍처/Kafka] Kafka로 강한 결합 탈출하기: 회원가입 비동기 처리 미니 프로젝트

깃짱 2025. 11. 5. 17:00
반응형

🌏 소스 코드

https://github.com/gitchan-Study/kafka-playground

 

GitHub - gitchan-Study/kafka-playground

Contribute to gitchan-Study/kafka-playground development by creating an account on GitHub.

github.com

 

 

🌏 인트로

이 포스팅에서는 Spring Boot와 Kafka를 사용하여 "회원가입"이라는 하나의 이벤트가 발생했을 때, "쿠폰 발급"과 "이메일 발송"이라는 두 가지 후속 작업이 어떻게 독립적으로 처리될 수 있는지 보여주는 예제 프로젝트에 대해 설명하겠습니다.

미니 프로젝트로 진행한 만큼 카프카를 원래 사용하는 목적이라면 대용량 트래픽을 감당하기 위한 것이 대부분이라 여러 대의 서버로 사용하지만, 개념만 이해를 하기 위해서 로컬에서 구성할 수 있도록 만들었습니다.

기본적인 카프카 개념이나 설정에 대해서 이해할 수 있도록 최대한 쉽고 간단한 설계로 설명하려고 합니다.

🌏 Kafka

✅ 카프카 쓰는 이유

전통적인 방식으로는 회원가입 로직 안에 쿠폰 발급 코드와 이메일 발송 코드가 모두 포함되어야 합니다.

@Transactional
public void register(User user) {
    userRepository.save(user); // 회원 저장
    couponService.issueWelcomeCoupon(user); // 쿠폰 발급
    emailService.sendWelcomeEmail(user); // 이메일 발송
}

이 경우, 만약 이메일 시스템에 장애가 발생하면 회원가입 전체 프로세스가 실패할 수 있습니다. 이를 강한 결합(Tightly Coupled) 이라고 합니다.

  • 쿠폰 서버나 이메일 서버 중 하나라도 장애가 발생하면 전체 회원가입이 실패합니다.
  • 각 기능이 강하게 결합되어 유지보수가 어렵습니다.
  • 새로운 후속 기능(예: 알림톡 발송)을 추가하려면 register() 내부를 매번 수정해야 합니다.

이 프로젝트는 Kafka라는 메시지 브로커를 중간에 두어 이 문제를 해결하는 것을 목표로 합니다.

[회원가입 API] --> [Kafka Topic: user-registered] --> [CouponConsumer]
                                               \\
                                                --> [EmailConsumer]
  • 회원가입 시스템은 "회원가입이 완료되었다"는 이벤트만 Kafka에 던져주고 자신의 역할을 끝냅니다.
  • 쿠폰 시스템과 이메일 시스템은 Kafka를 구독하고 있다가, 해당 이벤트가 발생하면 각자 독립적으로 자신의 작업을 수행합니다.

이를 통해 각 시스템의 의존성이 제거되어 느슨한 결합(Loosely Coupled) 을 가진 유연하고 안정적인 아키텍처를 만들 수 있습니다.

✅ 개념

Kafka는 대용량의 실시간 데이터를 안정적으로 처리하기 위한 분산 이벤트 스트리밍 플랫폼입니다. 이 프로젝트에서는 여러 시스템이 서로 직접 통신하지 않고, Kafka를 통해 메시지(이벤트)를 주고받도록 하는 메시지 브로커의 역할로 사용되었습니다.

  • Producer: 메시지를 생산하여 Kafka에 보내는 역할 (이 프로젝트에서는 UserController)
  • Consumer: Kafka로부터 메시지를 받아서 소비(처리)하는 역할 (이 프로젝트에서는 CouponConsumer, EmailConsumer)
  • Topic: 메시지가 저장되는 공간의 이름 (이 프로젝트에서는 user-registered)
  • Zookeeper: Kafka 클러스터의 상태를 관리하고 조정하는 역할

✅ 설정 방법 (Docker를 이용한 로컬 설정)

로컬 환경에서 Kafka와 Zookeeper를 가장 쉽게 실행하는 방법은 Docker를 사용하는 것입니다. 프로젝트 루트의 docker-compose.yml 파일이 이 역할을 담당합니다.

version: '3'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.0.1
    container_name: zookeeper
    ports:
      - "2181:2181"
    # ...

  kafka:
    image: confluentinc/cp-kafka:7.0.1
    container_name: kafka
    ports:
      - "9092:9092"
    depends_on:
      - zookeeper
    # ...

docker compose up -d 명령어 하나로 내 컴퓨터에 Kafka와 Zookeeper가 격리된 환경에서 실행됩니다.

🌏 프로젝트 설명

✅ 로컬 실행 환경 구성

이 프로젝트를 실행하면 다음과 같이 여러 애플리케이션이 각자의 위치와 포트에서 실행됩니다.

애플리케이션 실행 위치 포트 번호 역할

Spring Boot App 내 PC (macOS) localhost:8080 API 서버, Kafka Producer
Kafka Broker Docker 컨테이너 localhost:9092 메시지 중개
Zookeeper Docker 컨테이너 localhost:2181 Kafka 클러스터 관리

✅ 회원가입 API 실행 흐름

 

사용자가 회원가입 API를 호출했을 때, 전체 시스템이 동작하는 흐름은 다음과 같습니다.

 

 

  1. 요청: 사용자가 id와 password를 담아 /register API를 호출합니다.
  2. 처리 및 발행: UserController는 사용자 정보를 내부 Map에 저장하고, UserRegisteredEvent 객체를 생성하여 user-registered 토픽으로 이벤트를 발행(Produce)합니다.
  3. 독립적인 소비
    • coupon-group ID를 가진 CouponConsumer가 토픽으로부터 이벤트를 받아 쿠폰 발급 로직을 수행합니다.
    • email-group ID를 가진 EmailConsumer가 동일한 토픽으로부터 이벤트를 받아 이메일 발송 로직을 수행합니다.
    • groupId가 다르기 때문에 두 Consumer는 같은 이벤트를 각각 한 번씩 소비할 수 있습니다.

 

https://engineerinsight.tistory.com/435

 

[아키텍처/Kafka] 비동기 메시징 시스템: 개념 + Consumer Group을 통한 병렬 처리의 원리를 간단히 알

🌏 비동기 메시징 시스템이란?✅ 먼저, 비동기(Asynchronous)란?동기: 요청을 보내고 응답을 받을 때까지 기다림비동기: 요청을 보내고 응답을 기다리지 않고 다른 일 진행메시징 시스템은 일단 메

engineerinsight.tistory.com

 

 

 

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!

 

반응형