Skip to content

필사 모드: 실시간 금융 데이터 파이프라인 구축: Kafka, Flink 스트리밍 아키텍처 실전 가이드

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며

금융 시장에서 **데이터의 속도는 곧 경쟁력**이다. 주식 호가 데이터, FX 환율, 채권 수익률, 파생상품 가격 등 초당 수십만 건의 이벤트가 발생하며, 이를 밀리초 단위로 수집하고 처리하여 의사결정에 반영해야 한다. 배치 처리 기반의 전통적인 ETL 파이프라인으로는 이러한 실시간 요구사항을 충족할 수 없다.

Apache Kafka와 Apache Flink는 실시간 금융 데이터 파이프라인의 핵심 구성 요소다. Kafka는 고처리량, 내구성 있는 메시지 브로커로서 시장 데이터의 수집과 전달을 담당하고, Flink는 상태 기반 스트림 처리 엔진으로서 복잡한 이벤트 처리(CEP), 윈도우 집계, 이상 탐지를 수행한다.

이 글에서는 실시간 금융 데이터 파이프라인의 **아키텍처 설계부터 Kafka 프로듀서 구현, Flink 스트림 처리, CDC 연동, 이상 거래 탐지, 저지연 최적화, 장애 복구 전략**까지 실전 코드와 함께 다룬다.

**주의**: 이 글의 코드 예제는 교육 목적이며, 프로덕션 환경에서는 보안, 규정 준수, 성능 튜닝 등 추가 고려가 필요합니다.

금융 데이터 파이프라인 아키텍처

전체 아키텍처 개요

실시간 금융 데이터 파이프라인은 다음과 같은 계층 구조를 가진다.

| 계층 | 구성 요소 | 역할 | 지연 시간 목표 |

| ----------- | --------------------------- | ---------------------------------- | -------------- |

| 수집 계층 | Kafka Producer, FIX Gateway | 시장 데이터, 주문 이벤트 수집 | 1ms 이하 |

| 메시징 계층 | Apache Kafka | 이벤트 버퍼링, 라우팅, 내구성 보장 | 2-5ms |

| 처리 계층 | Apache Flink | 스트림 처리, 윈도우 집계, CEP | 10-50ms |

| 저장 계층 | TimescaleDB, Apache Iceberg | 시계열 저장, 분석용 데이터 레이크 | 100ms 이하 |

| 서빙 계층 | Redis, gRPC API | 실시간 대시보드, 알림 서비스 | 5ms 이하 |

데이터 흐름

거래소/데이터 벤더 --> Kafka Producer --> Kafka Cluster --> Flink Job --> 분석/알림

| |

CDC Connector TimescaleDB

| |

Legacy DB Iceberg Lake

핵심 설계 원칙은 다음과 같다.

1. **이벤트 소싱(Event Sourcing)**: 모든 상태 변경을 불변 이벤트로 기록

2. **역압(Backpressure) 전파**: 처리 속도에 맞춰 수집 속도를 자동 조절

3. **Exactly-Once 시맨틱스**: 장애 시에도 데이터 정확성 보장

4. **스키마 진화(Schema Evolution)**: Avro/Protobuf 기반 스키마 레지스트리 활용

Kafka 시장 데이터 수집

Kafka 프로듀서 구현

시장 데이터를 수집하여 Kafka 토픽으로 전송하는 프로듀서를 구현한다. 저지연과 내구성을 동시에 만족시키기 위해 `acks`, `linger.ms`, `batch.size` 등을 세밀하게 튜닝한다.

from confluent_kafka import Producer, KafkaError

from confluent_kafka.serialization import SerializationContext, MessageField

from confluent_kafka.schema_registry import SchemaRegistryClient

from confluent_kafka.schema_registry.avro import AvroSerializer

Avro 스키마 정의

MARKET_DATA_SCHEMA = """

{

"type": "record",

"name": "MarketData",

"namespace": "com.finance.marketdata",

"fields": [

{"name": "symbol", "type": "string"},

{"name": "price", "type": "double"},

{"name": "volume", "type": "long"},

{"name": "bid", "type": "double"},

{"name": "ask", "type": "double"},

{"name": "timestamp", "type": "long", "logicalType": "timestamp-millis"},

{"name": "exchange", "type": "string"},

{"name": "event_type", "type": {"type": "enum", "name": "EventType",

"symbols": ["TRADE", "QUOTE", "BBO"]}}

]

}

"""

class MarketDataProducer:

def __init__(self, bootstrap_servers: str, schema_registry_url: str):

self.schema_registry = SchemaRegistryClient(

{"url": schema_registry_url}

)

self.avro_serializer = AvroSerializer(

self.schema_registry, MARKET_DATA_SCHEMA

)

self.producer = Producer({

"bootstrap.servers": bootstrap_servers,

"acks": "all", # 모든 ISR 복제본 확인 (내구성)

"linger.ms": 1, # 1ms 배치 대기 (지연 vs 처리량 균형)

"batch.size": 16384, # 16KB 배치 크기

"compression.type": "lz4", # LZ4 압축 (속도 우선)

"max.in.flight.requests.per.connection": 5,

"enable.idempotence": True, # 멱등성 보장

"retries": 3,

"retry.backoff.ms": 100,

"partitioner": "murmur2", # 심볼 기반 파티셔닝

})

def send_market_data(self, symbol: str, data: dict):

"""시장 데이터를 Kafka로 전송"""

try:

self.producer.produce(

topic="market-data-raw",

key=symbol.encode("utf-8"),

value=self.avro_serializer(

data,

SerializationContext("market-data-raw", MessageField.VALUE)

),

callback=self._delivery_callback,

timestamp=int(time.time() * 1000),

)

self.producer.poll(0) # 비동기 콜백 처리

except KafkaError as e:

print(f"Kafka produce error: {e}")

def _delivery_callback(self, err, msg):

if err:

print(f"Delivery failed for {msg.key()}: {err}")

프로덕션에서는 메트릭 수집 (Prometheus counter 등)

def flush(self):

self.producer.flush(timeout=5)

토픽 설계 전략

금융 데이터의 토픽 설계에서 파티셔닝 전략은 성능에 직접적인 영향을 미친다.

| 전략 | 파티션 키 | 장점 | 단점 |

| --------------- | ------------ | ------------------- | --------------------- |

| 심볼 기반 | 종목 코드 | 동일 종목 순서 보장 | 핫 심볼 편중 |

| 거래소 기반 | 거래소 ID | 거래소별 독립 처리 | 심볼 순서 미보장 |

| 해시 기반 | 복합 키 해시 | 균등 분산 | 순서 보장 어려움 |

| 타임스탬프 기반 | 시간 버킷 | 시간 순서 처리 | 파티션 수 예측 어려움 |

프로덕션에서는 **심볼 기반 파티셔닝**을 기본으로 사용하되, 핫 심볼(예: AAPL, TSLA 등 거래량 상위 종목)에 대해서는 서브 파티셔닝을 적용한다.

Flink 스트림 처리

스트림 처리 프레임워크 비교

| 특성 | Apache Flink | Spark Structured Streaming | Kafka Streams | Apache Storm |

| ---------------- | ---------------------------- | -------------------------- | ----------------------------- | -------------------- |

| 처리 모델 | 네이티브 스트림 | 마이크로 배치 | 네이티브 스트림 | 네이티브 스트림 |

| 지연 시간 | 밀리초 수준 | 100ms-수초 | 밀리초 수준 | 밀리초 수준 |

| 상태 관리 | RocksDB 기반 대규모 상태 | Spark 메모리/디스크 | 로컬 RocksDB | 외부 저장소 필요 |

| Exactly-Once | 체크포인트 기반 지원 | 체크포인트 기반 지원 | 트랜잭션 기반 | At-Least-Once |

| SQL 지원 | Flink SQL (완전한 SQL) | Spark SQL (풍부한 생태계) | KSQL (제한적) | 미지원 |

| 배포 모델 | 독립 클러스터 | Spark 클러스터 | 라이브러리 (JVM 내장) | 독립 클러스터 |

| 이벤트 타임 처리 | 워터마크 기반 고급 지원 | 워터마크 지원 | 제한적 지원 | 제한적 |

| 적합 용도 | 저지연 CEP, 금융 실시간 처리 | 대규모 배치+스트림 통합 | Kafka 네이티브 마이크로서비스 | 레거시 스트림 시스템 |

금융 도메인에서는 **밀리초 수준 지연, 대규모 상태 관리, 이벤트 타임 처리**가 필수적이므로 Apache Flink가 가장 적합하다.

Flink SQL로 실시간 시장 데이터 처리

Flink SQL을 사용하면 복잡한 스트림 처리 로직을 선언적으로 표현할 수 있다.

-- Kafka 소스 테이블 정의

CREATE TABLE market_data_raw (

symbol STRING,

price DOUBLE,

volume BIGINT,

bid DOUBLE,

ask DOUBLE,

event_time TIMESTAMP(3),

exchange STRING,

event_type STRING,

-- 워터마크: 최대 5초 지연 허용

WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND

) WITH (

'connector' = 'kafka',

'topic' = 'market-data-raw',

'properties.bootstrap.servers' = 'kafka-broker:9092',

'properties.group.id' = 'flink-market-processor',

'format' = 'avro-confluent',

'avro-confluent.url' = 'http://schema-registry:8081',

'scan.startup.mode' = 'latest-offset'

);

-- 1분 VWAP (거래량 가중 평균 가격) 집계

CREATE TABLE vwap_1m (

symbol STRING,

window_start TIMESTAMP(3),

window_end TIMESTAMP(3),

vwap DOUBLE,

total_volume BIGINT,

trade_count BIGINT,

high_price DOUBLE,

low_price DOUBLE,

PRIMARY KEY (symbol, window_start) NOT ENFORCED

) WITH (

'connector' = 'jdbc',

'url' = 'jdbc:postgresql://timescaledb:5432/market',

'table-name' = 'vwap_1m'

);

INSERT INTO vwap_1m

SELECT

symbol,

window_start,

window_end,

SUM(price * volume) / SUM(volume) AS vwap,

SUM(volume) AS total_volume,

COUNT(*) AS trade_count,

MAX(price) AS high_price,

MIN(price) AS low_price

FROM TABLE(

TUMBLE(TABLE market_data_raw, DESCRIPTOR(event_time), INTERVAL '1' MINUTE)

)

WHERE event_type = 'TRADE'

GROUP BY symbol, window_start, window_end;

윈도우 집계와 분석

윈도우 유형별 활용

금융 데이터 분석에서 윈도우 전략은 분석 목적에 따라 다르게 선택한다.

| 윈도우 유형 | 설명 | 금융 활용 예시 |

| --------------- | -------------------- | ----------------------------- |

| Tumbling Window | 고정 크기, 비중첩 | 1분/5분/1시간 OHLCV 봉 생성 |

| Sliding Window | 고정 크기, 중첩 허용 | 이동 평균(MA), 변동성 계산 |

| Session Window | 활동 기반 동적 크기 | 트레이더 세션별 거래 분석 |

| Global Window | 커스텀 트리거 | 장 시작/종료 이벤트 기반 집계 |

Flink Java로 슬라이딩 윈도우 이동 평균 구현

public class MovingAverageJob {

public static DataStream<PriceAlert> computeMovingAverage(

DataStream<MarketData> marketStream) {

return marketStream

.keyBy(MarketData::getSymbol)

.window(SlidingEventTimeWindows.of(

Time.minutes(5), // 윈도우 크기: 5분

Time.seconds(30) // 슬라이드 간격: 30초

))

.process(new ProcessWindowFunction<MarketData, PriceAlert,

String, TimeWindow>() {

@Override

public void process(String symbol,

Context context,

Iterable<MarketData> elements,

Collector<PriceAlert> out) {

double sum = 0;

long count = 0;

double maxPrice = Double.MIN_VALUE;

double minPrice = Double.MAX_VALUE;

for (MarketData data : elements) {

sum += data.getPrice();

count++;

maxPrice = Math.max(maxPrice, data.getPrice());

minPrice = Math.min(minPrice, data.getPrice());

}

double ma = sum / count;

double volatility = (maxPrice - minPrice) / ma;

// 변동성이 임계값을 초과하면 알림 생성

if (volatility > 0.02) {

out.collect(new PriceAlert(

symbol,

ma,

volatility,

context.window().getEnd(),

"HIGH_VOLATILITY"

));

}

}

});

}

}

CDC 연동

Debezium CDC 커넥터 설정

레거시 금융 시스템의 데이터베이스 변경 사항을 실시간으로 캡처하여 Kafka로 전송한다. Debezium은 데이터베이스 트랜잭션 로그를 읽어 행 수준 변경을 이벤트로 변환한다.

debezium-postgres-connector.yaml

apiVersion: kafka.strimzi.io/v1beta2

kind: KafkaConnector

metadata:

name: finance-cdc-connector

labels:

strimzi.io/cluster: kafka-connect-cluster

spec:

class: io.debezium.connector.postgresql.PostgresConnector

tasksMax: 3

config:

데이터베이스 연결

database.hostname: postgres-finance.internal

database.port: '5432'

database.user: cdc_reader

database.password: 'VAULT_SECRET_REF'

database.dbname: finance_core

database.server.name: finance-cdc

CDC 설정

plugin.name: pgoutput

slot.name: flink_cdc_slot

publication.name: finance_pub

캡처 대상 테이블

table.include.list: >

public.orders,

public.transactions,

public.positions,

public.account_balances

스키마 및 변환

key.converter: io.confluent.connect.avro.AvroConverter

value.converter: io.confluent.connect.avro.AvroConverter

key.converter.schema.registry.url: http://schema-registry:8081

value.converter.schema.registry.url: http://schema-registry:8081

스냅샷 모드

snapshot.mode: initial

snapshot.locking.mode: none

하트비트 (WAL 보존 방지)

heartbeat.interval.ms: '10000'

토픽 라우팅

transforms: route

transforms.route.type: org.apache.kafka.connect.transforms.RegexRouter

transforms.route.regex: 'finance-cdc.public.(.*)'

transforms.route.replacement: 'cdc.$1'

장애 복구

errors.tolerance: all

errors.deadletterqueue.topic.name: cdc-dlq

errors.deadletterqueue.context.headers.enable: true

CDC 데이터 처리 주의사항

CDC를 금융 시스템에 적용할 때 반드시 고려해야 할 사항이 있다.

- **트랜잭션 경계 보존**: Debezium의 `provide.transaction.metadata` 옵션을 활성화하여 트랜잭션 단위로 이벤트를 그룹화

- **스키마 변경 대응**: DDL 변경 시 커넥터 재시작 없이 스키마 진화를 처리하도록 스키마 레지스트리와 호환성 모드 설정

- **WAL 디스크 사용량 모니터링**: 커넥터 장애 시 WAL 파일이 누적되어 디스크 고갈 위험이 있으므로, 하트비트와 슬롯 모니터링 필수

- **순서 보장**: 동일 레코드의 변경 순서가 보장되어야 하므로, 테이블 PK를 메시지 키로 사용

이상 거래 탐지

Flink CEP를 활용한 이상 패턴 탐지

Complex Event Processing(CEP)를 사용하면 시계열 이벤트에서 특정 패턴을 탐지할 수 있다. 다음은 급격한 가격 변동과 비정상적인 거래량을 탐지하는 Flink 잡이다.

Flink PyFlink를 활용한 이상 거래 탐지

from pyflink.datastream import StreamExecutionEnvironment

from pyflink.table import StreamTableEnvironment, EnvironmentSettings

from pyflink.table.expressions import col, lit, call

env = StreamExecutionEnvironment.get_execution_environment()

env.set_parallelism(4)

env.enable_checkpointing(10000) # 10초 체크포인트

t_env = StreamTableEnvironment.create(env)

이상 거래 탐지 SQL

anomaly_detection_sql = """

WITH price_stats AS (

SELECT

symbol,

price,

volume,

event_time,

-- 지난 5분 이동 평균/표준편차

AVG(price) OVER w AS price_ma,

STDDEV_POP(price) OVER w AS price_std,

AVG(volume) OVER w AS volume_ma

FROM market_data_raw

WINDOW w AS (

PARTITION BY symbol

ORDER BY event_time

RANGE BETWEEN INTERVAL '5' MINUTE PRECEDING AND CURRENT ROW

)

),

anomalies AS (

SELECT

symbol,

price,

volume,

event_time,

price_ma,

price_std,

volume_ma,

-- Z-Score 기반 이상 점수

ABS(price - price_ma) / NULLIF(price_std, 0) AS price_zscore,

CAST(volume AS DOUBLE) / NULLIF(volume_ma, 0) AS volume_ratio,

CASE

WHEN ABS(price - price_ma) / NULLIF(price_std, 0) > 3.0

THEN 'PRICE_SPIKE'

WHEN CAST(volume AS DOUBLE) / NULLIF(volume_ma, 0) > 5.0

THEN 'VOLUME_SURGE'

WHEN ABS(price - price_ma) / NULLIF(price_std, 0) > 2.0

AND CAST(volume AS DOUBLE) / NULLIF(volume_ma, 0) > 3.0

THEN 'COMBINED_ANOMALY'

ELSE 'NORMAL'

END AS anomaly_type

FROM price_stats

)

SELECT * FROM anomalies

WHERE anomaly_type <> 'NORMAL'

"""

알림 싱크 테이블 (Kafka 토픽으로 전송)

t_env.execute_sql("""

CREATE TABLE anomaly_alerts (

symbol STRING,

price DOUBLE,

volume BIGINT,

event_time TIMESTAMP(3),

anomaly_type STRING,

price_zscore DOUBLE,

volume_ratio DOUBLE

) WITH (

'connector' = 'kafka',

'topic' = 'anomaly-alerts',

'properties.bootstrap.servers' = 'kafka-broker:9092',

'format' = 'json'

)

""")

이상 탐지 임계값 기준

| 이상 유형 | 탐지 조건 | 심각도 | 대응 조치 |

| ------------------------------ | ------------------------- | -------- | -------------------------- |

| 급격한 가격 변동(Price Spike) | Z-Score 3.0 초과 | Critical | 즉시 알림, 자동 거래 중단 |

| 거래량 급증(Volume Surge) | 평균 대비 5배 초과 | Warning | 모니터링 강화, 담당자 알림 |

| 복합 이상(Combined) | 가격 Z 2.0 + 거래량 3배 | High | 포지션 리스크 재평가 |

| 스프레드 확대(Spread Widening) | Bid-Ask 스프레드 2배 초과 | Warning | 유동성 모니터링 |

저지연 최적화

메시지 브로커 비교 (금융 관점)

| 특성 | Apache Kafka | Solace PubSub+ | TIBCO EMS | 29West (Informatica) |

| --------- | -------------------------- | ------------------------ | ---------------- | -------------------- |

| 지연 시간 | 2-10ms | 마이크로초 수준 | 밀리초 수준 | 마이크로초 수준 |

| 처리량 | 초당 수백만 메시지 | 초당 수백만 메시지 | 초당 수만 메시지 | 초당 수백만 메시지 |

| 내구성 | 디스크 기반 로그 | 메모리+디스크 | 디스크 기반 | 메모리 기반 |

| 프로토콜 | 자체 프로토콜, REST | AMQP, MQTT, REST | JMS, AMQP | 멀티캐스트 UDP |

| 비용 | 오픈소스 | 상용 라이선스 | 상용 라이선스 | 상용 라이선스 |

| 적합 용도 | 이벤트 소싱, 범용 스트리밍 | 하이브리드 클라우드 금융 | 레거시 금융 통합 | 초저지연 트레이딩 |

Kafka 저지연 튜닝 체크리스트

1. **OS 레벨**: 페이지 캐시 최적화, `vm.swappiness=1`, 전용 디스크 (NVMe SSD)

2. **JVM 레벨**: G1GC 또는 ZGC 사용, 힙 사이즈 6-8GB, GC 로그 모니터링

3. **Kafka 브로커**: `num.io.threads` 증가, `log.flush.interval.messages=1` (내구성 우선 시)

4. **네트워크**: TCP 소켓 버퍼 크기 조정 (`socket.send.buffer.bytes`, `socket.receive.buffer.bytes`)

5. **프로듀서**: `linger.ms=0-1`, `batch.size` 적정화, `compression.type=lz4`

6. **컨슈머**: `fetch.min.bytes=1`, `fetch.max.wait.ms` 최소화

Flink 성능 최적화

flink-conf.yaml - 금융 워크로드 최적화

taskmanager.memory.process.size: 8192m

taskmanager.memory.managed.fraction: 0.4

taskmanager.numberOfTaskSlots: 4

상태 백엔드 (대규모 상태를 위한 RocksDB)

state.backend: rocksdb

state.backend.rocksdb.memory.managed: true

state.backend.incremental: true

체크포인트 설정

execution.checkpointing.interval: 10s

execution.checkpointing.mode: EXACTLY_ONCE

execution.checkpointing.min-pause: 5s

execution.checkpointing.timeout: 60s

execution.checkpointing.max-concurrent-checkpoints: 1

네트워크 버퍼

taskmanager.network.memory.fraction: 0.1

taskmanager.network.memory.min: 128mb

taskmanager.network.memory.max: 1gb

워터마크 설정

pipeline.auto-watermark-interval: 200ms

재시작 전략

restart-strategy: exponential-delay

restart-strategy.exponential-delay.initial-backoff: 1s

restart-strategy.exponential-delay.max-backoff: 60s

restart-strategy.exponential-delay.backoff-multiplier: 2.0

장애 복구와 데이터 정합성

장애 시나리오별 대응 전략

| 장애 유형 | 영향 | 복구 전략 | RTO/RPO |

| ---------------------- | ---------------- | ----------------------------------------- | -------------------------------- |

| Kafka 브로커 장애 | 파티션 리더 변경 | ISR 자동 리더 선출, min.insync.replicas=2 | RTO 수초, RPO 0 |

| Flink TaskManager 장애 | 처리 중단 | 체크포인트에서 자동 복구 | RTO 10-30초, RPO 체크포인트 간격 |

| 네트워크 단절 | 데이터 유실 위험 | 프로듀서 재시도, 컨슈머 오프셋 롤백 | 네트워크 복구 시간 의존 |

| 스키마 레지스트리 장애 | 직렬화 실패 | 로컬 캐시 활용, 다중 인스턴스 | RTO 수초 |

| CDC 슬롯 유실 | 변경 이벤트 누락 | 스냅샷 재실행, 이벤트 보정 | RTO 수분-수시간 |

Exactly-Once 시맨틱스의 현실

이론적으로 Exactly-Once 처리를 보장하지만, 실제로는 다음과 같은 엣지 케이스가 존재한다.

- **프로듀서 타임아웃**: 브로커에서 메시지를 받았지만 ACK가 프로듀서에 도달하지 못하면, 프로듀서가 재전송하여 중복이 발생할 수 있다. 이를 방지하기 위해 `enable.idempotence=true`를 설정한다.

- **Flink 체크포인트 실패**: 체크포인트가 완료되기 전 장애가 발생하면, 마지막 성공한 체크포인트로 롤백된다. 이 경우 체크포인트 간격만큼의 데이터가 재처리된다.

- **백프레셔 누적**: 다운스트림 처리 속도가 느리면 Kafka 컨슈머의 lag이 증가하고, 극단적인 경우 메모리 부족으로 잡이 실패할 수 있다. Flink의 크레딧 기반 흐름 제어가 이를 완화하지만, 근본적으로는 처리 용량을 확보해야 한다.

데이터 손실 방지 패턴

[Producer] [Kafka] [Flink]

| -- produce(msg) --> | |

| <-- ack (success) -- | |

| | -- consume(msg) --> |

| | [process]

| | [checkpoint]

| | <-- commit offset -- |

| | |

| === 브로커 장애 === | |

| -- produce(msg) --> X (실패) |

| -- retry --> | (새 리더) |

| <-- ack (success) -- | |

핵심은 **프로듀서의 멱등성 + Kafka의 ISR 복제 + Flink의 체크포인트**가 삼중으로 데이터 정합성을 보장하는 것이다.

운영 체크리스트

배포 전 점검

- Kafka 클러스터 최소 3 브로커, `replication.factor=3`, `min.insync.replicas=2`

- Flink 체크포인트 저장소를 S3/HDFS 등 외부 스토리지로 설정

- 스키마 레지스트리 호환성 모드: `BACKWARD` (기본) 또는 `FULL`

- 데드 레터 큐(DLQ) 토픽 생성 및 모니터링 설정

- 네트워크 지연 측정: 프로듀서-브로커, 브로커-컨슈머 간 RTT 측정

모니터링 지표

| 지표 | 설명 | 경고 임계값 |

| ------------------- | --------------------------------------- | -------------------------- |

| Consumer Lag | 컨슈머가 프로듀서 대비 뒤처진 메시지 수 | 10,000 이상 |

| End-to-End Latency | 프로듀서 전송부터 Flink 처리 완료까지 | 100ms 초과 |

| Checkpoint Duration | Flink 체크포인트 소요 시간 | 체크포인트 간격의 50% 초과 |

| Kafka ISR Shrink | ISR에서 빠진 복제본 수 | 1 이상 |

| GC Pause Time | JVM GC 멈춤 시간 | 200ms 초과 |

| Backpressure Ratio | Flink 태스크의 역압 비율 | 0.5 초과 |

부하 테스트

프로덕션 배포 전, 예상 피크 트래픽의 2-3배 부하로 테스트해야 한다. 금융 시장에서 블랙 스완 이벤트 발생 시 평소 대비 10배 이상의 트래픽이 발생할 수 있으므로, 충분한 여유 용량을 확보해야 한다.

참고자료

- [Apache Kafka Documentation - Design](https://kafka.apache.org/documentation/#design)

- [Apache Flink - Use Cases](https://flink.apache.org/what-is-flink/use-cases/)

- [Confluent - How a Tier-1 Bank Tuned Apache Kafka for p99 Latency for Trading](https://www.confluent.io/blog/tier-1-bank-ultra-low-latency-trading-design/)

- [Debezium Documentation - PostgreSQL Connector](https://debezium.io/documentation/reference/stable/connectors/postgresql.html)

- [Kai Waehner - Top Trends for Data Streaming with Apache Kafka and Flink in 2026](https://www.kai-waehner.de/blog/2025/12/10/top-trends-for-data-streaming-with-apache-kafka-and-flink-in-2026/)

- [DZone - Designing Low-Latency Market Data Systems](https://dzone.com/articles/real-time-market-data-processing-designing-systems)

- [Confluent - Apache Flink Stream Processing Use Cases](https://www.confluent.io/blog/apache-flink-stream-processing-use-cases-with-examples/)

- [Onehouse - Flink vs Kafka Streams vs Spark Structured Streaming Comparison](https://www.onehouse.ai/blog/apache-spark-structured-streaming-vs-apache-flink-vs-apache-kafka-streams-comparing-stream-processing-engines)

현재 단락 (1/405)

금융 시장에서 **데이터의 속도는 곧 경쟁력**이다. 주식 호가 데이터, FX 환율, 채권 수익률, 파생상품 가격 등 초당 수십만 건의 이벤트가 발생하며, 이를 밀리초 단위로 수집...

작성 글자: 0원문 글자: 14,470작성 단락: 0/405