오늘은 기존 Spring Cloud MSA 프로젝트를 Kafka 기반의 이벤트 처리 구조로 확장하는 실습을 진행했다. 이전까지는 Kafka Connect를 이용하여 데이터베이스 간 데이터 동기화를 실습했다면, 오늘은 실제 Microservice 간 이벤트를 Kafka를 통해 전달하는 구조를 구현하기 시작했다. 또한 RabbitMQ 기반 설정을 제거하고 Kafka 환경으로 변경하는 과정과 Kafka 메시지를 직접 확인하는 방법까지 함께 학습하였다.
기존 프로젝트에서는 Spring Cloud Bus를 사용하기 위해 RabbitMQ를 사용하고 있었지만, Kafka를 이용한 이벤트 기반 통신을 위해 RabbitMQ 의존성을 제거하였다.
application.yml에 Kafka Producer 설정을 추가하였다.
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
Kafka Broker의 주소를 지정하고 Producer가 메시지를 직렬화하여 전송할 수 있도록 Serializer를 설정하였다.
Kafka 실습 이전에 기존 서비스들이 정상적으로 동작하는지 확인하였다.
확인한 내용은 다음과 같다.
JWT Secret 값은 Gateway와 User Service에서 동일하게 사용되어야 정상적으로 토큰 검증이 가능하다.
실습 도중 Order Service가 정상적으로 실행되었음에도 API Gateway에서 다음과 같은 오류가 발생하였다.
503 Service Unavailable
Unable to find instance for ORDER-SERVICE
처음에는 Order Service 자체가 실행되지 않은 것으로 생각했지만 실제로는 서비스는 실행되고 있었다.
확인한 내용은 다음과 같다.
eureka:
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
register-with-eureka: true
fetch-registry: true
이후 Eureka Dashboard를 통해 서비스 등록 여부를 확인하면서 Gateway가 실제 서비스를 조회하는 구조를 다시 이해할 수 있었다.
MSA에서는 서비스가 실행된 것만으로 통신이 가능한 것이 아니라 반드시 Eureka에 등록되고 Gateway가 해당 정보를 조회해야 한다는 점을 다시 확인하였다.
Kafka Connect가 unhealthy 상태로 표시되는 문제도 함께 확인하였다.
처음에는 Kafka Connect가 정상적으로 동작하지 않는 것으로 생각했지만 로그를 확인한 결과 실제 Connector는 정상적으로 동작하고 있었다.
로그에서 확인한 내용
반면 Health Check에서는
curl: command not found
오류가 발생하였다.
즉,
Kafka Connect 프로그램
↓
정상 실행
Docker Health Check
↓
curl 명령 없음
↓
unhealthy 표시
라는 사실을 확인하였다.
Docker에서 표시하는 unhealthy 상태가 반드시 프로그램 오류를 의미하는 것은 아니라는 점을 배웠다.
기존 Connector 설정을 확인해보니 Source Connector는 다음 테이블만 감시하고 있었다.
table.whitelist = mydb1.users
즉
mydb1.users
↓
Kafka Topic
↓
mydb2.my_topic_users
만 동작하는 구조였다.
따라서 Order Service에서 생성되는
mydb1.orders
테이블은 Kafka로 전달되지 않았다.
Kafka Connect는 지정한 테이블만 읽어 Topic으로 전달하기 때문에 새로운 테이블을 사용하려면 Connector 설정 역시 변경해야 한다는 점을 이해하였다.
Kafka Broker에 실제 메시지가 전달되는지 확인하기 위해 Console Consumer를 사용하였다.
사용한 명령어
kafka-console-consumer \
--bootstrap-server localhost:9092 \
--topic example-catalog-topic \
--from-beginning
옵션 의미
| 옵션 | 설명 |
|---|---|
| bootstrap-server | Kafka Broker 주소 |
| topic | 조회할 Topic |
| from-beginning | Topic의 처음부터 모든 메시지 조회 |
이를 통해 Producer가 전송한 이벤트가 실제 Kafka Topic에 저장되는지 직접 확인할 수 있었다.
오늘 실습의 핵심은 서비스 간 직접 호출을 Kafka 이벤트 기반 구조로 변경하는 것이다.
기존 구조
Order Service
│
▼
Catalog Service 직접 호출
Kafka 기반 구조
Order Service
│
▼
Kafka Producer
│
▼
Kafka Topic
│
▼
Catalog Service Consumer
이벤트 기반 구조에서는 Order Service가 Catalog Service를 직접 호출하지 않아도 되므로 서비스 간 결합도가 낮아지고 장애 전파를 줄일 수 있다.
또한 Producer와 Consumer가 서로 독립적으로 동작할 수 있어 확장성이 향상된다.
오늘 가장 헷갈렸던 부분이기도 했다.
MariaDB
↓
Kafka Connect
↓
Kafka Topic
Order Service
↓
Kafka Producer
↓
Kafka Topic
↓
Catalog Service Consumer
이 둘은 모두 Kafka를 사용하지만 목적이 완전히 다르다는 점을 이해하였다.
오늘은 Kafka Connect를 이용한 데이터 동기화와 Spring Kafka를 이용한 서비스 간 이벤트 처리가 서로 다른 기술이라는 점을 명확하게 이해할 수 있었다.
또한 MSA에서는 단순히 서비스를 실행하는 것이 아니라 Service Discovery, API Gateway, Kafka Broker, Producer, Consumer가 모두 정상적으로 연결되어야 전체 시스템이 동작한다는 점을 다시 체감하였다.
앞으로는 Catalog Service와 Order Service 사이를 Kafka 이벤트 기반으로 연결하여 주문 이벤트가 발생하면 자동으로 상품 재고가 감소하는 구조를 구현하게 된다. 이번 실습을 통해 이벤트 기반 아키텍처가 기존 REST 호출 방식보다 서비스 간 결합도를 크게 낮출 수 있다는 점을 이해할 수 있었고, 앞으로 구현할 Kafka Consumer의 동작 방식도 더욱 기대된다.