AM 기반 MSA 기술 열번째 정규수업

오리·2026년 8월 3일

LG AM Inspire camp 5기

목록 보기
27/35

오늘 학습한 내용

오늘은 지난 수업에서 놓친 내용을 보충하면서 MSA 환경에서 필요한 데이터 동기화, 장애 대응, 분산 추적, 모니터링 구조를 학습했다

주요 학습 내용은 다음과 같다

  • Kafka를 이용한 서비스 간 데이터 동기화
  • 여러 Order Service 인스턴스의 데이터를 하나의 DB에 저장하는 구조
  • Trace ID와 Span ID를 이용한 분산 요청 추적
  • Zipkin을 이용한 호출 흐름 및 장애 위치 확인
  • Micrometer, Prometheus, Grafana를 이용한 마이크로서비스 모니터링
  • Docker를 이용한 Prometheus와 Grafana 실행
  • API Gateway 라우팅 설정 오류 해결
  • CQRS와 Event Sourcing의 개념
  • CDC와 Kafka Connect의 관계 및 차이점

1. Kafka를 이용한 데이터 동기화

Orders Service와 Catalog Service의 데이터 동기화

사용자가 상품을 주문하면 주문 데이터만 저장되는 것이 아니라 Catalog Service의 상품 재고도 함께 감소해야 한다

서비스를 직접 호출하는 구조에서는 Order Service가 Catalog Service의 API를 호출하여 재고를 수정할 수 있다

Order Service
    │
    │ REST API 호출
    ▼
Catalog Service
    │
    ▼
상품 재고 감소

하지만 이 방식은 두 서비스가 직접 연결되므로 결합도가 높아진다

Catalog Service에 장애가 발생하거나 응답이 느려지면 Order Service의 주문 처리까지 영향을 받을 수 있다

Kafka를 사용하면 다음과 같은 비동기 구조를 만들 수 있다

Order Service
    │
    │ 주문 정보 발행
    ▼
Kafka Topic
    │
    │ 주문 정보 구독
    ▼
Catalog Service
    │
    ▼
상품 재고 감소

Order Service는 Kafka Producer 역할을 수행하고, Catalog Service는 Kafka Consumer 역할을 수행한다

강의 자료에서는 Order Service가 주문 정보를 Kafka Topic에 전송하고 Catalog Service가 이를 소비하여 재고를 감소시키는 방식으로 데이터 동기화를 구현한다


Kafka Producer와 Consumer

Producer

Producer는 Kafka Topic에 메시지를 발행하는 역할을 한다

OrderController
    ↓
KafkaProducer
    ↓
Kafka Topic

주문이 생성되면 주문 데이터 중 필요한 값을 Kafka 메시지로 전송한다

예시 메시지는 다음과 같은 내용을 포함할 수 있다

{
  "productId": "CATALOG-001",
  "qty": 2,
  "unitPrice": 10000,
  "totalPrice": 20000
}

Consumer

Consumer는 Kafka Topic에 저장된 메시지를 가져와 처리한다

Kafka Topic
    ↓
KafkaConsumer
    ↓
CatalogRepository
    ↓
재고 감소

Catalog Service는 전달받은 productId로 상품을 조회하고 주문 수량만큼 재고를 감소시킨다

기존 재고: 100
주문 수량: 2
변경 재고: 98

2. Multiple Orders Service의 데이터 동기화 문제

MSA에서는 하나의 서비스를 여러 인스턴스로 실행할 수 있다

Order Service #1 — 60001
Order Service #2 — 60002

Eureka와 Load Balancer를 사용하면 요청이 여러 Order Service 인스턴스로 분산된다

User Service
    │
    ├── Order Service #1
    └── Order Service #2

하지만 각 Order Service가 자신의 로컬 DB를 사용한다면 주문 데이터가 서로 다른 DB에 저장될 수 있다

Order Service #1 → DB #1
Order Service #2 → DB #2

이 경우 특정 인스턴스에서 조회했을 때 다른 인스턴스가 저장한 주문 데이터를 찾지 못하는 문제가 발생한다

강의 자료에서는 여러 Order Service가 주문 정보를 DB에 직접 저장하지 않고 Kafka Topic으로 전송한 뒤, Kafka Sink Connector가 하나의 MariaDB에 저장하는 구조를 사용한다

Order Service #1 ─┐
                  ├── Kafka Topic
Order Service #2 ─┘
                         │
                         ▼
               Kafka Sink Connector
                         │
                         ▼
                    MariaDB

이 구조를 사용하면 어떤 Order Service 인스턴스가 주문을 처리하더라도 최종 데이터는 하나의 DB에 저장된다


Kafka 메시지의 Schema와 Payload

Kafka Connect에서 JDBC Sink Connector로 데이터를 저장하려면 메시지에 Schema와 Payload 형식을 사용할 수 있다

{
  "schema": {
    "type": "struct",
    "fields": [
      {
        "type": "string",
        "field": "order_id"
      },
      {
        "type": "string",
        "field": "user_id"
      }
    ]
  },
  "payload": {
    "order_id": "ORDER-001",
    "user_id": "USER-001"
  }
}

Schema

데이터의 구조와 타입을 설명한다

order_id → String
qty → int
total_price → int

Payload

실제 저장할 데이터가 포함된다

order_id: ORDER-001
qty: 2
total_price: 20000

강의 자료에서도 주문 메시지를 Schema와 Payload로 나누어 Kafka Sink Connector가 DB 테이블에 저장할 수 있도록 구성한다


3. 마이크로서비스의 연쇄 장애

MSA에서는 하나의 요청이 여러 서비스로 전달될 수 있다

Client
   ↓
User Service
   ↓
Order Service
   ↓
Catalog Service

만약 Catalog Service에서 장애가 발생하면 Order Service 호출이 실패할 수 있다

Order Service가 실패하면 이를 호출한 User Service도 실패한다

Catalog Service 장애
        ↓
Order Service 실패
        ↓
User Service 실패
        ↓
Client에게 500 오류

이렇게 한 서비스의 장애가 다른 서비스까지 연쇄적으로 전파되는 현상을 연쇄 장애 또는 Cascading Failure라고 한다

강의 자료에서는 User Service, Order Service, Catalog Service가 연속적으로 호출되는 상황에서 하위 서비스의 오류가 전체 요청 실패로 이어지는 구조를 설명한다


4. Circuit Breaker와 Resilience4j

Circuit Breaker는 장애가 발생한 서비스를 반복적으로 호출하지 않도록 차단하는 패턴이다

전기 회로의 차단기처럼 일정 비율 이상의 실패가 발생하면 호출을 중단한다

정상 상태
Client → Order Service

장애 발생 반복
Client → Order Service → 실패

Circuit Open
Client → Fallback

Circuit Breaker 상태

CLOSED

정상 상태로 요청을 그대로 전달한다

Client → Service

OPEN

실패율이 임계치를 초과하여 요청을 차단한다

Client → Circuit Breaker → Fallback

HALF_OPEN

일정 시간이 지난 후 일부 요청만 보내 서비스가 복구됐는지 확인한다

일부 테스트 요청 성공
    ↓
CLOSED로 전환

테스트 요청이 다시 실패하면 OPEN 상태로 돌아간다


Fallback

Fallback은 호출 대상 서비스에 문제가 있을 때 대신 수행하는 처리다

예를 들어 주문 서비스가 응답하지 않을 때 전체 사용자 조회 요청을 실패시키지 않고 빈 주문 목록을 반환할 수 있다

{
  "userId": "USER-001",
  "orders": []
}

이 방식은 완전한 기능은 제공하지 못하더라도 전체 시스템 장애를 줄이는 데 도움이 된다

강의 자료에서는 실패한 하위 서비스를 계속 호출하지 않고 Fallback Method로 대체 응답을 반환하는 구조를 제시한다


Resilience4j의 주요 기능

Resilience4j는 장애 대응을 위한 경량 라이브러리다

  • CircuitBreaker: 장애 서비스 호출 차단
  • Retry: 실패한 요청 재시도
  • RateLimiter: 일정 시간 동안 요청 수 제한
  • Bulkhead: 서비스별 자원 격리
  • TimeLimiter: 요청 제한 시간 설정
  • Cache: 결과 캐싱

모든 요청에 Retry를 무조건 적용하면 장애가 발생한 서비스에 더 많은 부하를 줄 수 있으므로 Circuit Breaker와 함께 조건을 신중하게 설정해야 한다


5. Trace ID와 Span ID

MSA에서는 로그가 서비스별로 분산되어 있다

User Service 로그
Order Service 로그
Catalog Service 로그

이 때문에 여러 로그가 동일한 요청에서 발생한 것인지 확인하기 어렵다

이를 해결하기 위해 Trace ID와 Span ID를 사용한다


Trace ID

Trace ID는 하나의 요청 전체를 식별하는 ID다

사용자 요청이 여러 서비스를 통과하더라도 같은 요청이라면 동일한 Trace ID를 유지한다

User Service
Trace ID: AAA

Order Service
Trace ID: AAA

Catalog Service
Trace ID: AAA

즉, Trace ID는 하나의 요청을 처음부터 끝까지 묶어주는 식별자다


Span ID

Span ID는 하나의 요청 안에서 수행되는 개별 작업을 식별한다

Trace ID: AAA
├── Span ID: 111 — User Service
├── Span ID: 222 — Order Service
└── Span ID: 333 — Catalog Service

Trace ID는 같지만 서비스별 작업은 서로 다르기 때문에 Span ID도 각각 다르다


Trace와 Span의 관계

Trace
└── Span
    ├── Child Span
    └── Child Span

하나의 Trace 안에는 여러 Span이 존재할 수 있다

강의 자료에서도 한 요청에 같은 Trace ID가 사용되고 각 서비스 호출 단위마다 서로 다른 Span ID가 생성되는 구조를 보여준다


6. Zipkin을 이용한 분산 추적

Zipkin은 Trace ID와 Span ID 정보를 수집하고 요청의 흐름을 시각화하는 분산 추적 도구다

User Service
    ↓
Order Service
    ↓
Catalog Service

Zipkin에서는 각 서비스의 호출 순서와 처리 시간을 타임라인으로 확인할 수 있다

User Service     ███████████████
Order Service       █████████
Catalog Service        ████

이를 통해 다음 내용을 확인할 수 있다

  • 어떤 서비스가 호출됐는지
  • 호출 순서가 어떻게 되는지
  • 각 서비스에서 얼마나 시간이 걸렸는지
  • 어느 Span에서 오류가 발생했는지
  • 특정 Trace 전체가 얼마나 오래 걸렸는지

강의 자료에서는 Zipkin을 통해 User Service와 Order Service 사이의 Trace ID와 Span ID를 비교하고, Order Service에서 예외가 발생했을 때 해당 Span을 추적하는 과정을 보여준다


Trace ID, Span ID, Zipkin의 관계

항목역할
Trace ID하나의 요청 전체를 식별
Span ID요청 안의 개별 작업을 식별
ZipkinTrace와 Span을 수집하고 시각화

택배에 비유하면 다음과 같다

Trace ID = 전체 택배 운송장 번호
Span ID = 집하, 물류 이동, 배송 등 각 처리 단계
Zipkin = 배송 조회 화면

7. 마이크로서비스 모니터링

분산 추적이 특정 요청의 흐름을 분석하는 기술이라면 모니터링은 시스템 전체 상태를 지속적으로 관찰하는 기술이다

오늘 실습에서는 다음 도구를 사용했다

Spring Boot Application
        │
        ▼
Micrometer
        │
        ▼
/actuator/prometheus
        │
        ▼
Prometheus
        │
        ▼
Grafana

강의 자료에서는 Micrometer가 생성한 메트릭을 Prometheus가 수집하고 Grafana가 이를 시각화하는 구조를 사용한다


8. Micrometer

Micrometer는 JVM 기반 애플리케이션의 메트릭을 수집할 수 있도록 지원하는 라이브러리다

Spring Boot Actuator와 함께 사용하면 다음 정보를 측정할 수 있다

  • HTTP 요청 횟수
  • HTTP 요청 처리 시간
  • JVM 메모리 사용량
  • CPU 사용량
  • 실행 중인 스레드 수
  • Garbage Collection 정보
  • 애플리케이션 실행 시간

Micrometer는 Prometheus뿐만 아니라 여러 모니터링 시스템에서 사용할 수 있는 공통 메트릭 API 역할을 한다

강의 자료에서는 Timer와 @Timed를 이용하여 특정 API의 호출 횟수와 처리 시간을 측정하는 방법을 소개한다


Actuator 메트릭 노출

Prometheus가 메트릭을 가져가려면 Spring Boot 애플리케이션에서 /actuator/prometheus를 노출해야 한다

management:
  endpoints:
    web:
      exposure:
        include:
          - health
          - info
          - metrics
          - prometheus

필요한 의존성은 다음과 같다

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

정상적으로 설정되면 다음 주소에서 메트릭을 확인할 수 있다

http://localhost:8001/actuator/prometheus

Gateway를 거치는 구조라면 다음과 같이 접근할 수 있다

http://localhost:8000/user-service/actuator/prometheus

9. Prometheus

Prometheus는 메트릭을 수집하고 시계열 데이터베이스에 저장하는 모니터링 도구다

Prometheus의 중요한 특징은 Pull 방식이다

Prometheus
    │
    │ GET /actuator/prometheus
    ▼
User Service

서비스가 Prometheus로 직접 전송하는 것이 아니라 Prometheus가 일정 주기마다 서비스의 메트릭 엔드포인트를 호출한다

강의 자료에서는 Prometheus가 /actuator/prometheus에서 메트릭을 수집하고 이를 Table 또는 Graph 형태로 조회하는 과정을 설명한다


prometheus.yml

Docker 컨테이너에서 Windows 호스트의 Gateway에 접근하기 위해 host.docker.internal을 사용했다

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets:
          - "localhost:9090"

  - job_name: "user-service"
    metrics_path: "/user-service/actuator/prometheus"
    static_configs:
      - targets:
          - "host.docker.internal:8000"

주요 설정

scrape_interval: 15s

15초마다 메트릭을 수집한다

job_name: "user-service"

수집 대상의 이름이다

metrics_path: "/user-service/actuator/prometheus"

메트릭을 가져올 URL 경로다

targets:
  - "host.docker.internal:8000"

Prometheus 컨테이너가 접근할 대상 주소다


10. Docker로 Prometheus와 Grafana 실행

Docker Compose 구성

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    restart: unless-stopped

실행 명령은 다음과 같다

docker compose up -d

Docker Compose version 경고

다음과 같은 경고가 발생했다

the attribute `version` is obsolete

Docker Compose v2에서는 다음 설정이 더 이상 필요하지 않다

version: "3.8"

따라서 services:부터 작성하면 된다


11. Prometheus Target 상태 확인

Prometheus의 수집 대상 상태는 다음 주소에서 확인할 수 있다

http://localhost:9090/targets

정상 상태는 다음과 같다

State: UP

수집 실패 상태는 다음과 같다

State: DOWN

오늘은 다음 오류가 발생했다

connection refused
http://host.docker.internal:8000/user-service/actuator/prometheus

이는 Prometheus 자체의 문제가 아니라 8000번 포트의 API Gateway가 실행되지 않았다는 뜻이었다

Prometheus 정상
        │
        ▼
API Gateway:8000 연결 실패

connection refused는 해당 주소까지 접근은 가능하지만 그 포트에서 실행 중인 프로그램이 없다는 의미다


12. API Gateway 시작 실패 문제

API Gateway를 실행하는 과정에서 다음 오류가 발생했다

Binding to target GatewayProperties failed

routes[2].uri = null
routes[2].predicates = []

이는 세 번째 Gateway Route에 uri와 predicates가 없다는 의미다

설정 파일을 확인해보니 다음과 같이 중복된 빈 라우트가 있었다

- id: user-service-actuator
- id: user-service-actuator
  uri: lb://USER-SERVICE
  predicates:
    - Path=/user-service/actuator/**

첫 번째 줄도 하나의 독립적인 Route로 인식된다

routes[2].id = user-service-actuator
routes[2].uri = null
routes[2].predicates = []

따라서 불완전한 첫 번째 Route를 삭제해야 했다

- id: user-service-actuator
  uri: lb://USER-SERVICE
  predicates:
    - Path=/user-service/actuator/**
    - Method=GET
  filters:
    - RemoveRequestHeader=Cookie
    - RewritePath=/user-service/(?<segment>.*), /$\{segment}

Actuator Route를 일반 User Route보다 앞에 두는 이유

일반 User Service Route에는 JWT 인증 필터가 적용되어 있었다

- id: user-service
  uri: lb://USER-SERVICE
  predicates:
    - Path=/user-service/**
  filters:
    - AuthorizationHeaderFilter

Prometheus는 매번 JWT를 발급받아 메트릭을 수집하지 않는다

따라서 Actuator 전용 Route를 별도로 만들고 인증 필터를 적용하지 않는 것이 필요하다

/user-service/actuator/**
    → 인증 필터 없음

/user-service/**
    → AuthorizationHeaderFilter 적용

Actuator Route를 일반 Route보다 앞에 두면 /user-service/actuator/prometheus 요청이 인증 Route에 들어가지 않는다


13. Grafana

Grafana는 Prometheus가 저장한 메트릭을 시각화한다

Prometheus
    │
    ▼
Grafana Dashboard

접속 주소는 다음과 같다

http://localhost:3000

기본 계정은 다음과 같다

ID: admin
PW: admin

Grafana에서는 Prometheus를 Data Source로 등록한 후 JVM, Prometheus, Spring Cloud Gateway 등의 Dashboard를 가져와 사용할 수 있다

강의 자료에서는 JVM Micrometer Dashboard와 Spring Cloud Gateway Dashboard를 통해 메모리, 요청 횟수, 처리 시간 등의 정보를 확인하는 과정을 보여준다


14. Zipkin과 Prometheus의 차이

둘 다 MSA 관찰을 위한 도구지만 목적이 다르다

구분ZipkinPrometheus
주요 목적요청 단위의 분산 추적시스템 전체 메트릭 수집
중심 데이터Trace, SpanCPU, 메모리, 요청 횟수
주요 질문이 요청은 어디에서 실패했는가현재 시스템 상태는 정상인가
데이터 형태호출 흐름, 처리 시간시계열 메트릭
시각화Zipkin UIPrometheus, Grafana

예를 들어 특정 회원 조회 요청이 실패했다면 Zipkin으로 해당 Trace를 확인한다

전체 User Service의 요청 실패율이나 응답 시간이 증가했는지 확인하려면 Prometheus와 Grafana를 사용한다


15. CQRS

CQRS는 Command Query Responsibility Segregation의 약자다

명령과 조회의 책임을 분리하는 패턴이다

Command = 데이터 변경
Query = 데이터 조회

Command

INSERT
UPDATE
DELETE

예시

주문 생성
주문 취소
배송 상태 변경

Query

SELECT

예시

주문 목록 조회
상품 상세 조회
사용자 주문 이력 조회

CQRS 구조

               Command
Client ─────────────────────▶ Write Model
                                │
                                │ Event
                                ▼
                           Read Model
Client ◀───────────────────── Query

쓰기와 조회를 분리하면 각각의 목적에 맞는 DB 구조를 사용할 수 있다

Write DB
- 정합성 중심
- 정규화 구조

Read DB
- 빠른 조회 중심
- 비정규화 가능
- 검색 최적화

CQRS에서 중요한 점

CQRS는 단순히 Controller를 Command와 Query로 나누는 것이 핵심이 아니다

중요한 것은 읽기와 쓰기의 요구사항이 실제로 다른지 판단하는 것이다

CQRS를 적용하면 구조가 복잡해진다

  • Write Model과 Read Model 관리
  • 데이터 동기화 필요
  • 이벤트 처리 실패 대응
  • 중복 이벤트 처리
  • 최종적 일관성 고려

따라서 조회 트래픽이 많거나 읽기와 쓰기의 확장 요구가 명확히 다른 시스템에서 적용하는 것이 적절하다


16. Event Sourcing

Event Sourcing은 현재 상태만 저장하지 않고 상태를 변경한 모든 이벤트를 저장하는 패턴이다

기존 방식은 현재 상태만 저장한다

주문 상태 = 배송중

Event Sourcing은 변경 이력을 모두 저장한다

OrderCreated
PaymentCompleted
OrderPacked
DeliveryStarted

현재 상태는 이벤트를 순서대로 재생하여 계산한다

OrderCreated
    ↓
PaymentCompleted
    ↓
DeliveryStarted
    ↓
현재 상태: 배송중

Event Sourcing에서 중요한 점

Event Sourcing에서는 이벤트가 데이터의 원본이다

따라서 이벤트를 나중에 수정하거나 삭제하면 과거 상태를 정확하게 재현하기 어렵다

중요하게 고려해야 할 내용은 다음과 같다

  • 이벤트의 순서 보장
  • 이벤트 중복 처리 방지
  • 이벤트 스키마 버전 관리
  • 이벤트 재처리
  • Snapshot 사용
  • 실패한 이벤트 처리
  • 오래된 이벤트와 최신 코드 간 호환성

Event Sourcing은 감사 이력과 상태 복원이 중요한 금융, 결제, 주문 시스템에서 유용하지만 모든 서비스에 적용하면 복잡성이 매우 커질 수 있다


17. CQRS와 Event Sourcing의 관계

CQRS와 Event Sourcing은 함께 사용될 수 있지만 같은 개념은 아니다

CQRS
= 읽기와 쓰기의 책임 분리

Event Sourcing
= 상태 변경 이벤트를 저장

함께 사용하는 경우 다음과 같은 구조가 된다

Command
   ↓
Event Store
   ↓
Kafka
   ↓
Projection
   ↓
Read DB
   ↓
Query

Command가 실행되면 이벤트가 저장되고, 이벤트를 구독한 Projection이 Read DB를 갱신한다


18. Event Driven Architecture와 Event Sourcing의 차이

Kafka를 사용한다고 무조건 Event Sourcing은 아니다

Order Service
    ↓
Kafka
    ↓
Catalog Service

이 구조는 이벤트 기반 아키텍처다

서비스가 이벤트를 발행하고 다른 서비스가 이를 구독해 처리한다

그러나 이벤트를 시스템의 유일한 원본 데이터로 영구 보관하고 이를 재생해 상태를 복원하는 구조가 아니라면 Event Sourcing이라고 부르기 어렵다

Kafka 이벤트 발행
≠
자동으로 Event Sourcing

19. CDC

CDC는 Change Data Capture의 약자다

데이터베이스에서 발생한 변경을 감지하는 기술이다

INSERT
UPDATE
DELETE

DB 종류에 따라 변경 로그를 읽는다

DB변경 로그
MySQL, MariaDBbinlog
PostgreSQLWAL
OracleRedo Log

예를 들어 MariaDB의 주문 테이블에 새로운 행이 추가되면 CDC가 binlog를 읽고 변경 이벤트를 생성할 수 있다

MariaDB
    │
    │ binlog
    ▼
CDC Connector
    │
    ▼
Kafka Topic

CDC는 애플리케이션 코드가 직접 Kafka 메시지를 보내지 않아도 DB 변경을 이벤트로 전달할 수 있다는 장점이 있다


20. Kafka Connect

Kafka Connect는 Kafka와 외부 시스템을 연결하는 프레임워크다

Source Connector

외부 시스템의 데이터를 Kafka로 가져온다

MariaDB
    ↓
Source Connector
    ↓
Kafka

Sink Connector

Kafka 데이터를 외부 시스템으로 보낸다

Kafka
    ↓
Sink Connector
    ↓
MariaDB

강의에서 여러 Order Service의 주문 데이터를 하나의 MariaDB에 저장한 구조는 JDBC Sink Connector를 사용한 예다


21. CDC와 Kafka Connect의 유사점과 차이점

둘 다 데이터 동기화 파이프라인에서 함께 사용되지만 역할은 다르다

구분CDCKafka Connect
의미DB 변경 감지 방식Kafka 데이터 연동 프레임워크
핵심 역할무엇이 변경됐는지 감지데이터를 Kafka 안팎으로 이동
주요 대상INSERT, UPDATE, DELETEDB, 파일, 검색엔진 등
Kafka 필수 여부필수 아님Kafka를 중심으로 동작
대표 예시Debezium CDCJDBC Source, JDBC Sink

둘을 함께 사용하면 다음과 같은 구조가 된다

MariaDB
    │
    │ binlog 변경 감지
    ▼
Debezium CDC Connector
    │
    ▼
Kafka Topic
    │
    ▼
JDBC Sink Connector
    │
    ▼
다른 DB

Kafka Connect의 모든 Source Connector가 CDC는 아니다

JDBC Source Connector가 일정 주기로 쿼리를 실행할 수도 있다

SELECT *
FROM orders
WHERE updated_at > 마지막_수집_시간

이 방식도 DB 데이터를 Kafka로 가져오지만 DB 로그를 읽는 전형적인 로그 기반 CDC와는 다르다

반대로 Debezium은 binlog나 WAL을 읽어 변경 자체를 감지하므로 CDC에 해당한다


22. 오늘의 핵심 정리

Kafka 데이터 동기화

Producer
→ Kafka Topic
→ Consumer

서비스끼리 직접 호출하지 않고 이벤트를 통해 데이터를 동기화할 수 있다


Multiple Orders Service

Order Service #1 ─┐
                  ├── Kafka → Sink Connector → 하나의 DB
Order Service #2 ─┘

여러 서비스 인스턴스에서 생성한 데이터를 하나의 저장소에 모을 수 있다


Trace와 Zipkin

Trace ID = 요청 전체
Span ID = 요청 안의 개별 작업
Zipkin = 요청 흐름 시각화

모니터링

Micrometer
→ /actuator/prometheus
→ Prometheus
→ Grafana

Micrometer는 메트릭을 생성하고, Prometheus는 수집하며, Grafana는 시각화한다


CQRS와 Event Sourcing

CQRS
= 읽기와 쓰기 분리

Event Sourcing
= 모든 상태 변경을 이벤트로 저장

둘은 함께 사용할 수 있지만 반드시 함께 적용해야 하는 것은 아니다


CDC와 Kafka Connect

CDC
= DB에서 무엇이 바뀌었는지 감지

Kafka Connect
= 데이터를 Kafka 안팎으로 이동

CDC는 방식이고 Kafka Connect는 이를 실행할 수 있는 프레임워크다


profile
IT 전공자의 발버둥

0개의 댓글