필사 모드: 스트림 처리 2026 완벽 가이드 - Kafka Streams · Flink SQL · RisingWave · Materialize · Arroyo · ksqlDB · Bytewax · Decodable 심층 분석
한국어프롤로그 — "실시간이 필요해요" 라는 한 문장의 무게
2026년 어느 핀테크 팀 회의실.
PM: "실시간 사기 탐지 대시보드 만들 수 있죠?"
개발자: "실시간이 얼마나 실시간인데요?"
PM: "음... 즉시요?"
개발자: "1초요, 100밀리초요, 아니면 5분이요?"
이 짧은 대화에 2026년 스트림 처리의 모든 것이 들어있다. 한쪽에는 "Kafka에서 데이터 꺼내면 그게 실시간이지" 라는 막연한 기대가 있고, 다른 한쪽에는 "윈도, 워터마크, 정확히 한 번, 백프레셔, 상태 백업"을 매일 만지는 엔지니어의 한숨이 있다. 그리고 그 사이를 Flink, Kafka Streams, RisingWave, Materialize, Arroyo, ksqlDB, Bytewax가 채우고 있다.
2026년 현재 스트림 처리는 **세 개의 패러다임**으로 갈라진다. 프로그래밍 프레임워크(Flink, Kafka Streams, Bytewax), SQL 스트리밍 DB(RisingWave, Materialize, ksqlDB), 그리고 매니지드 SaaS(Decodable, Confluent Cloud Flink, Aiven Flink, Quix). 그리고 그 위에 **상태 관리 모델**의 두 축이 겹친다 — 임베디드 RocksDB vs Disaggregated State on S3.
이 글은 그 전체 지형을 한 번에 정리한다. Flink 2.0의 Disaggregated State Backend부터, RisingWave 2.x의 Postgres 와이어, Materialize의 Differential Dataflow, Arroyo의 Rust SQL 엔진, ksqlDB의 Confluent 정리, Bytewax의 Python 도약, 그리고 한국·일본 빅테크가 무엇을 어떻게 쓰는지까지.
1장 · 2026년 스트림 처리 지도 — Framework / Stream DB / Managed 3 패러다임
먼저 용어부터. "스트림 처리" 라는 한 단어 안에 서로 다른 세 가지가 들어 있다.
| 패러다임 | 의미 | 대표 | 사용자 |
| --- | --- | --- | --- |
| Stream Processing Framework | 코드로 토폴로지를 짠다 (DataStream, KStream) | Flink, Kafka Streams, Bytewax, Beam | 데이터 엔지니어 |
| Streaming Database | SQL로 view 정의, incremental하게 계산 | RisingWave, Materialize, ksqlDB | 분석가, 백엔드 |
| Managed Streaming SaaS | 위 두 가지를 호스팅 | Decodable, Confluent Cloud Flink, Quix | 작은 팀 |
2026년에는 경계가 흐려졌다. Flink는 Materialized Tables를 추가했고, RisingWave는 UDF로 Rust 코드를 받아들였고, Kafka Streams는 ksqlDB의 사실상 후계로 흡수되고 있다.
그래도 **선택의 출발점은 패러다임**이다. 다음 의사결정 트리를 권한다.
1. **"Kafka 토픽 사이 데이터 변환과 집계만 필요"** → Kafka Streams (Java/Scala) 또는 ksqlDB (SQL).
2. **"복잡한 윈도/조인/CEP, 비-Kafka 소스, 페타바이트"** → Flink (SQL 또는 DataStream API).
3. **"앱이 Postgres로 쿼리하면 좋겠다, Materialized View가 자동 갱신되면 좋겠다"** → RisingWave 또는 Materialize.
4. **"Python 데이터 사이언티스트가 직접 쓸 수 있어야"** → Bytewax 또는 Quix.
5. **"운영하기 싫고 SaaS로 끝내고 싶다"** → Decodable, Confluent Cloud Flink, Aiven Flink.
6. **"Rust 모노톤 워크로드, 단일 바이너리"** → Arroyo.
Flink가 망치라면 모든 문제가 못이 된다. 2026년의 베테랑은 "초당 1만 건 이벤트, 단순 집계면 ksqlDB나 Materialize로 충분하다, Flink 클러스터 운영은 후회한다" 라고 말한다.
2장 · 스트리밍 vs 배치 vs 마이크로 배치 — 지연 시간 스펙트럼
먼저 패러다임 자체부터.
| 모델 | 처리 단위 | 지연 시간 | 대표 |
| --- | --- | --- | --- |
| Batch | 큰 데이터셋 한 번 | 분~시간 | Spark batch, Hive, dbt |
| Micro-batch | 작은 배치를 주기적으로 | 초~분 | Spark Structured Streaming, Trino on Iceberg |
| True streaming | 이벤트 하나씩 | ms~초 | Flink, Kafka Streams, RisingWave |
| Continuous query | 결과 view를 incremental 갱신 | 초~분 | Materialize, RisingWave, Snowflake Dynamic Tables |
Spark Structured Streaming의 마이크로 배치는 매번 작은 배치 잡을 돈다. P99가 보통 1~10초. Flink는 진짜 record-at-a-time(또는 mini-batch 모드) 으로 P99가 100ms 이하 가능. 그래서 HFT, 게임 매칭, 사기 탐지는 Flink/Kafka Streams로, 분석 대시보드는 Materialize/RisingWave로, 일간 리포트는 Spark/Trino로 간다.
핵심은 "**1초 이내 실시간**이 비즈니스 가치 있나" 라는 질문이다. 대부분의 BI 대시보드는 5분 지연도 괜찮다. 그러면 마이크로 배치로 충분하다. 진짜 스트림 처리는 "1초 지연이 매출에 영향을 주는" 곳에서만 필요하다.
3장 · Apache Flink 2.0 — Disaggregated State와 Materialized Tables
Apache Flink는 2014년 Stratosphere 프로젝트에서 시작해, 데이터 아티잔(현 Ververica, Alibaba 인수)이 주도. 2025년 3월 출시된 **Flink 2.0**이 전환점이다. 이 글은 Flink의 SQL/Table API/DataStream API/상태/워터마크/exactly-once를 한 번에 정리한다.
Flink 2.0의 핵심 변화.
- **Disaggregated State Backend (ForSt)**: state를 broker 디스크가 아닌 S3/GCS에 두고, 로컬 cache로 hot 데이터를 둔다. Tiered storage가 state에도 왔다.
- **Async State API**: state 접근을 비동기로 만들어 P99 지연 개선.
- **Materialized Tables (FLIP-435)**: SQL로 view 정의 → 백그라운드에서 incremental refresh. RisingWave/Materialize의 영역을 정식으로 흡수.
- **Adaptive Batch Scheduler 기본화**: stream과 batch를 같은 엔진에서.
- **Java 17 기본**, Scala 2.13 마이그레이션 완료, Python 3.12 지원.
Flink 아키텍처는 4계층이다.
| 계층 | 책임 | 예 |
| --- | --- | --- |
| Flink SQL | ANSI SQL, Catalog, Hive 호환 | CREATE TABLE orders ... |
| Table API | 관계형 API (Java/Scala/Python) | table.groupBy("user").select(...) |
| DataStream API | low-level 변환 | dataStream.map().keyBy().window() |
| Stateful Functions / Process Function | 가장 낮은 레벨, 직접 state 접근 | RichFlatMapFunction |
Flink SQL로 Kafka topic을 읽고 join하는 예.
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2),
event_time TIMESTAMP_LTZ(3),
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'orders',
'properties.bootstrap.servers' = 'kafka:9092',
'scan.startup.mode' = 'earliest-offset',
'format' = 'avro-confluent',
'avro-confluent.schema-registry.url' = 'http://schema-registry:8081'
);
CREATE TABLE users (
user_id BIGINT,
segment STRING,
PRIMARY KEY (user_id) NOT ENFORCED
) WITH (
'connector' = 'jdbc',
'url' = 'jdbc:postgresql://pg:5432/app',
'table-name' = 'users'
);
-- 5분 윈도로 segment별 매출
SELECT
segment,
TUMBLE_START(event_time, INTERVAL '5' MINUTE) AS window_start,
SUM(amount) AS revenue
FROM orders o
JOIN users u ON o.user_id = u.user_id
GROUP BY segment, TUMBLE(event_time, INTERVAL '5' MINUTE);
이 한 쿼리에 Flink의 핵심이 다 들어있다 — Kafka source, JDBC lookup, watermark, tumble window, group by aggregate.
4장 · Flink DataStream API + Stateful Functions
Flink SQL이 안 되는 영역도 있다. 복잡한 state machine, custom partitioning, dynamic routing, CEP 같은 곳. 이때 DataStream API를 쓴다.
Java 예시 — 사용자별 세션 윈도와 alert.
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(30_000); // 30s checkpoint
DataStream<Event> events = env.fromSource(
KafkaSource.<Event>builder()
.setBootstrapServers("kafka:9092")
.setTopics("clicks")
.setStartingOffsets(OffsetsInitializer.earliest())
.setValueOnlyDeserializer(new EventDeserializer())
.build(),
WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner((e, ts) -> e.eventTimeMs()),
"kafka-source"
);
events
.keyBy(Event::userId)
.window(EventTimeSessionWindows.withGap(Time.minutes(10)))
.process(new ProcessWindowFunction<Event, Alert, Long, TimeWindow>() {
@Override
public void process(Long userId, Context ctx, Iterable<Event> elems, Collector<Alert> out) {
int count = 0;
for (Event e : elems) count++;
if (count > 100) out.collect(new Alert(userId, count, "abusive"));
}
})
.sinkTo(KafkaSink.<Alert>builder()
.setBootstrapServers("kafka:9092")
.setRecordSerializer(...)
.setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
.build());
env.execute("session-alert");
핵심은 `keyBy → window → process` 의 흐름. keyBy는 동일 키의 이벤트를 같은 태스크로 라우팅하고, window는 시간/세션 기준으로 묶고, process는 윈도 단위로 결과를 낸다.
State는 RichFunction을 통해 접근.
public class FraudDetector extends KeyedProcessFunction<Long, Tx, Alert> {
private transient ValueState<Double> lastAmount;
private transient ListState<Tx> recentTxs;
@Override
public void open(Configuration cfg) {
lastAmount = getRuntimeContext().getState(
new ValueStateDescriptor<>("last", Double.class));
recentTxs = getRuntimeContext().getListState(
new ListStateDescriptor<>("recent", Tx.class));
}
@Override
public void processElement(Tx tx, Context ctx, Collector<Alert> out) throws Exception {
Double prev = lastAmount.value();
if (prev != null && tx.amount() > prev * 10) {
out.collect(new Alert(tx.userId(), "amount-spike"));
}
lastAmount.update(tx.amount());
recentTxs.add(tx);
}
}
Flink는 ValueState, ListState, MapState, ReducingState, AggregatingState를 제공한다. 모두 keyed scope로, 같은 key의 이벤트끼리만 공유한다.
5장 · Flink 상태 관리, 체크포인트, exactly-once
Flink의 강점은 **exactly-once 시맨틱**이다. Kafka source + Kafka sink일 때, 장애가 나도 결과에 중복이나 누락 없이 정확히 한 번 적용된다. 어떻게?
핵심은 **Chandy-Lamport** 알고리즘 기반의 분산 스냅샷.
1. JobManager가 주기적으로 checkpoint barrier를 source에 주입.
2. Barrier가 데이터와 함께 dataflow를 따라 흐름.
3. 각 operator가 barrier를 받으면 자신의 state를 snapshot.
4. Sink가 barrier를 받으면 sink-specific commit (Kafka는 transactional producer commit).
5. 모든 operator가 끝나면 checkpoint complete.
장애 시.
1. 마지막 successful checkpoint로 모든 operator state 복원.
2. Source가 해당 checkpoint의 offset부터 다시 읽기.
3. Sink는 transactional producer이므로 committed transaction만 외부에 보임.
설정 예.
env.enableCheckpointing(60_000); // 60s
env.getCheckpointConfig().setCheckpointStorage("s3://flink-state/checkpoints");
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(30_000);
env.getCheckpointConfig().setCheckpointTimeout(300_000);
env.getCheckpointConfig().enableUnalignedCheckpoints();
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
env.setStateBackend(new EmbeddedRocksDBStateBackend());
Flink 2.0의 ForSt (Disaggregated state)는 RocksDB를 S3에 백킹하는 방식. state가 페타바이트 규모여도 broker 디스크에 묶이지 않는다.
6장 · Flink 윈도잉 — Tumbling / Sliding / Session / Custom
스트림 처리의 핵심 추상은 **윈도**다. 무한 스트림을 유한 청크로 자르는 방법.
| 윈도 종류 | 의미 | 예 |
| --- | --- | --- |
| Tumbling | 겹치지 않는 고정 크기 | 5분 단위 매출 |
| Sliding | 겹치는 고정 크기 | 30초마다 5분 평균 |
| Session | 비활동 gap 기준 | 30분 무활동이면 세션 종료 |
| Global | 전체 무한 | 전체 카운트 (state 무한 증가 위험) |
Tumbling 예 (Flink SQL).
SELECT
TUMBLE_START(event_time, INTERVAL '5' MINUTE) AS w_start,
COUNT(*) AS cnt
FROM clicks
GROUP BY TUMBLE(event_time, INTERVAL '5' MINUTE);
Sliding (HOP).
SELECT
HOP_START(event_time, INTERVAL '30' SECOND, INTERVAL '5' MINUTE) AS w_start,
AVG(latency) AS avg_latency
FROM requests
GROUP BY HOP(event_time, INTERVAL '30' SECOND, INTERVAL '5' MINUTE);
Session.
SELECT
user_id,
SESSION_START(event_time, INTERVAL '30' MINUTE) AS s_start,
COUNT(*) AS events_in_session
FROM user_events
GROUP BY user_id, SESSION(event_time, INTERVAL '30' MINUTE);
윈도 선택 기준.
- **고정 리포트(시간/일별)**: Tumbling.
- **이동 평균, 핫 메트릭**: Sliding (slide 작게).
- **사용자 행동 분석**: Session.
- **상태 머신 (불특정 구간)**: ProcessFunction + timer.
7장 · 이벤트 시간 vs 처리 시간 — 워터마크의 본질
스트림 처리에서 가장 어려운 개념. "이벤트 시간"은 이벤트가 실제 발생한 시각이고, "처리 시간"은 시스템이 그 이벤트를 본 시각이다. 둘이 다르다.
- 모바일 앱이 오프라인일 때 발생한 이벤트가 10분 후에 도착.
- 네트워크 지연으로 일부만 늦게 도착.
- 시스템이 잠시 멈췄다가 깨어남.
**워터마크(watermark)** 는 "이 시각 이전의 이벤트는 더 이상 안 온다고 가정해도 좋다" 는 시그널. 워터마크는 윈도 닫기, 결과 emit, late data 분리의 트리거다.
Flink 워터마크 전략.
WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(10))
.withTimestampAssigner((event, ts) -> event.eventTimeMs())
.withIdleness(Duration.ofMinutes(1)); // 1분 안 오면 idle 표시
`forBoundedOutOfOrderness(10s)` 의 의미. "내가 본 가장 큰 이벤트 시각이 T면, T-10s 이전의 이벤트만 처리한다. 이후 도착하면 late data."
Late data 처리.
SingleOutputStreamOperator<Result> main = stream
.keyBy(...)
.window(TumblingEventTimeWindows.of(Time.minutes(5)))
.allowedLateness(Time.minutes(2)) // 윈도 close 후 2분간 추가 emit
.sideOutputLateData(lateTag) // 2분 후 도착한 건 side output
.process(...);
DataStream<Event> late = main.getSideOutput(lateTag);
워터마크는 **정확성과 지연 사이의 트레이드오프**다. 짧으면 결과 빠르지만 late data 많음, 길면 정확하지만 결과 늦음.
8장 · CEP — Complex Event Processing
CEP는 "이벤트 패턴 매칭"이다. 예: "로그인 실패 5번 연속 → 계정 잠금", "5분 안에 결제 → 환불 → 결제 패턴 → 사기 의심".
Flink CEP 예.
Pattern<LoginEvent, ?> pattern = Pattern.<LoginEvent>begin("first")
.where(SimpleCondition.of(e -> !e.success()))
.next("repeat")
.where(SimpleCondition.of(e -> !e.success()))
.times(4)
.within(Time.minutes(5));
PatternStream<LoginEvent> ps = CEP.pattern(events.keyBy(LoginEvent::userId), pattern);
DataStream<Alert> alerts = ps.select(match -> {
LoginEvent last = match.get("repeat").get(3);
return new Alert(last.userId(), "5-fail-in-5min");
});
CEP는 Flink CEP, Esper(오랜 역사), Drools Fusion(룰 엔진 기반)이 대표. 사기 탐지, IDS, 거래 룰, IoT 알람의 핵심.
CEP의 어려움은 **상태 관리**. 5분 패턴이면 5분치 부분 매치를 다 들고 있어야 한다. 트래픽 큰 곳에서 state가 폭발한다. 해결책은 partition by key + state TTL + pattern simplification.
9장 · Kafka Streams 4.0 — 라이브러리로서의 스트림 처리
Kafka Streams는 Confluent가 Kafka 0.10(2016)에 추가한 **라이브러리**다. Flink가 "분산 클러스터에 잡 제출"이라면, Kafka Streams는 "내 Spring Boot 앱에 import해서 쓰는 라이브러리"다.
2025년 출시된 Kafka Streams 4.0의 핵심.
- **Share Groups (KIP-932)** 와 통합 — 같은 토픽을 queue 처럼 소비.
- **Async Processing API** — non-blocking I/O 친화.
- **State Store on RocksDB 7.x** — 향상된 압축, faster recovery.
- **Java 17 기본, GraalVM Native Image 실험적 지원**.
- **Kafka Streams DSL 안정화** — KTable.join, KStream-KTable join 다양화.
Kafka Streams의 두 가지 추상.
- **KStream**: 무한 record stream (insert-only).
- **KTable**: 키별로 최신 값을 가지는 changelog 기반 테이블.
기본 word count 예 (Java).
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "wc-app");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> source = builder.stream("text-input");
KTable<String, Long> counts = source
.flatMapValues(v -> Arrays.asList(v.toLowerCase().split("\\W+")))
.groupBy((k, v) -> v)
.count();
counts.toStream().to("word-count-output", Produced.with(Serdes.String(), Serdes.Long()));
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
Kafka Streams의 멋진 점은 **이게 그냥 라이브러리** 라는 것. 같은 jar를 N개 인스턴스 띄우면 자동으로 파티션을 나눠가진다 (Kafka consumer group + 내부 rebalance). 별도 클러스터 매니저 필요 없음. Spring Boot, Quarkus, Micronaut 어디든 임베드 가능.
10장 · Kafka Streams 상태 + Interactive Queries
KTable과 aggregate는 **state store**에 저장된다 (기본 RocksDB, in-memory도 가능). 그리고 그 state를 **외부에서 직접 쿼리** 할 수 있다 (Interactive Queries).
Interactive Queries 예.
ReadOnlyKeyValueStore<String, Long> store = streams.store(
StoreQueryParameters.fromNameAndType("word-counts", QueryableStoreTypes.keyValueStore())
);
Long count = store.get("kafka");
여기서 강력한 패턴 — **Streams App = 마이크로 서비스**. Kafka에서 데이터 읽고, 자체 RocksDB에 집계 결과 저장하고, REST API로 외부에 노출. 별도 OLAP DB 없이 실시간 view 제공.
대가는 분산 state의 복잡성. 어떤 키가 어떤 인스턴스에 있는지 알아야 한다. Kafka Streams는 `metadataForKey()` API로 이를 알려주고, gRPC/REST 라우팅을 직접 짜야 한다.
GlobalKTable로 작은 lookup 테이블을 모든 인스턴스에 복제할 수도 있다 (모든 파티션을 모든 인스턴스가 읽음).
Kafka Streams는 **"Kafka에 있는 데이터만"** 처리한다. 외부 JDBC, REST API와의 join은 제한적 (Kafka Connect로 sourcing 먼저 해야). 이게 Flink와의 가장 큰 차이.
11장 · ksqlDB 0.30 — SQL on Kafka의 흥망
ksqlDB는 Confluent가 2017년 공개한 "Kafka 위의 SQL 데이터베이스". 내부적으로 Kafka Streams를 사용. 한 줄로 SQL 쿼리를 던지면 백그라운드 스트리밍 잡이 돈다.
CREATE STREAM orders (
order_id BIGINT KEY,
user_id BIGINT,
amount DECIMAL(10,2)
) WITH (KAFKA_TOPIC='orders', VALUE_FORMAT='AVRO');
CREATE TABLE user_revenue AS
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
EMIT CHANGES;
SELECT * FROM user_revenue WHERE user_id = 42 EMIT CHANGES;
마지막 `EMIT CHANGES`는 "지속적으로 결과 변화를 push" 라는 뜻. 이게 streaming SQL의 본질이다.
ksqlDB 0.30 (2025)은 사실상 마지막 메이저. Confluent가 2025년 초 발표한 방향은 "**ksqlDB → Flink SQL**". 즉 Confluent Cloud의 SQL 스트리밍은 Flink로 통합되고, ksqlDB는 유지보수 모드로 간다. 새 프로젝트는 Flink SQL 권장.
그래도 ksqlDB는 여전히 많은 곳에서 가동 중이다. 마이그레이션 패스가 중요한 이유.
12장 · RisingWave 2.x — Postgres 와이어 위의 스트리밍 DB
RisingWave는 2021년 Singularity Data(현 RisingWave Labs)가 시작한 **Rust 기반 스트리밍 DB**. 2025년 출시된 RisingWave 2.0의 한 줄 요약: "**Postgres처럼 보이지만 안에서 streaming SQL이 돈다**".
핵심 특징.
- **Postgres wire protocol 100% 호환**: psql, JDBC, ORM 모두 그대로.
- **Materialized Views가 1급 시민**: `CREATE MATERIALIZED VIEW`가 incremental streaming job.
- **Rust 엔진, thread-per-core**: low overhead, P99 안정.
- **S3로 state 오프로드**: Disaggregated state from day 1.
- **Cluster + Cloud + Standalone 배포 모드**.
- **CDC source 내장**: Postgres, MySQL의 binlog 직접 읽기.
기본 사용 예.
-- Kafka source 등록
CREATE SOURCE orders_source (
order_id BIGINT,
user_id BIGINT,
amount NUMERIC,
event_time TIMESTAMPTZ
) WITH (
connector = 'kafka',
topic = 'orders',
properties.bootstrap.server = 'kafka:9092',
scan.startup.mode = 'earliest'
) FORMAT PLAIN ENCODE JSON;
-- Materialized View — 자동으로 실시간 갱신
CREATE MATERIALIZED VIEW hourly_revenue AS
SELECT
date_trunc('hour', event_time) AS hour,
SUM(amount) AS revenue,
COUNT(*) AS orders
FROM orders_source
GROUP BY 1;
-- Postgres처럼 쿼리
SELECT * FROM hourly_revenue ORDER BY hour DESC LIMIT 24;
이 마지막 `SELECT`이 마법이다. RisingWave는 hourly_revenue를 백그라운드에서 **incremental하게** 갱신하고 있고, 쿼리는 그 결과의 현재 상태를 즉시 반환한다. 그래서 OLAP DB처럼 사용자가 직접 dashboard에서 쿼리해도 된다.
RisingWave 2.x 신기능.
- **Iceberg Sink + Iceberg Source**: 데이터레이크와 직결.
- **UDF in Python / JavaScript / Rust**.
- **Subscription**: Postgres LISTEN/NOTIFY 같은 변화 구독.
- **Snowflake / BigQuery / Redshift sink**.
13장 · Materialize — Differential Dataflow의 상용화
Materialize는 2019년 Frank McSherry(Microsoft Research, Naiad/Differential Dataflow 저자)와 Arjun Narayan이 창업. 한 줄 요약: "**진짜 incremental view maintenance**".
핵심 차이.
- **Differential Dataflow 엔진**: 1996년부터 연구된 진짜 incremental 계산. delta만 전파.
- **Postgres wire**: RisingWave처럼 psql/JDBC 호환.
- **SQL 99% 커버**: Postgres 호환 dialect, recursive CTE까지.
- **Strict serializability**: 모든 쿼리가 일관된 시점의 스냅샷.
- **2024년 Cloud 출시**: 자체 호스팅도 가능하지만 사실상 SaaS 우선.
기본 예.
CREATE SOURCE orders FROM KAFKA BROKER 'kafka:9092' TOPIC 'orders'
FORMAT AVRO USING CONFLUENT SCHEMA REGISTRY 'http://schema-registry:8081'
ENVELOPE NONE;
CREATE MATERIALIZED VIEW revenue_by_segment AS
SELECT
u.segment,
SUM(o.amount) AS revenue
FROM orders o
JOIN users u ON o.user_id = u.user_id
GROUP BY u.segment;
-- 결과는 항상 최신, 마치 Postgres 테이블처럼.
SELECT * FROM revenue_by_segment;
Materialize의 차별성은 **임의의 SQL을 incremental하게 만든다**는 것. 일반적 streaming SQL은 group by aggregate, window 같은 정해진 패턴만 incremental. Materialize는 join, sub-query, recursive CTE까지 incremental.
대가는 **state 메모리**. Differential Dataflow는 보통 인메모리 (디스크 spill 옵션 추가 중). 그래서 대규모는 RisingWave가, 복잡한 SQL은 Materialize가 강점.
14장 · Arroyo — Rust로 다시 쓴 Flink SQL
Arroyo는 2023년 Micah Wylde(Lyft Streaming 출신)가 시작한 **Rust 기반 스트리밍 엔진**. 한 줄 요약: "**Flink SQL 호환 + 단일 바이너리 + 빠르다**".
핵심 특징.
- **Rust 엔진**: JVM 없이 단일 바이너리.
- **Arroyo SQL = Flink SQL 호환 subset**.
- **Web UI 우선**: 잡 생성·모니터링이 SaaS 처럼.
- **S3 state**: tiered, RocksDB 없음.
- **Kubernetes operator** 제공.
CLI로 잡 생성.
Arroyo single-node
docker run -p 8000:8000 ghcr.io/arroyosystems/arroyo:latest
Web UI에서 SQL을 입력.
CREATE TABLE events (
user_id BIGINT,
event_type TEXT,
amount FLOAT,
ts TIMESTAMP
) WITH (
connector = 'kafka',
bootstrap_servers = 'kafka:9092',
topic = 'events',
type = 'source',
format = 'json',
'event_time_field' = 'ts',
'watermark_field' = 'ts'
);
CREATE TABLE sinks WITH (connector = 'kafka', topic = 'alerts', type = 'sink', format = 'json');
INSERT INTO sinks
SELECT user_id, SUM(amount) AS total
FROM events
WHERE event_type = 'purchase'
GROUP BY user_id, TUMBLE(INTERVAL '5' MINUTE);
Arroyo는 2026년 시점 production 채택이 늘고 있다. **"Flink를 운영하기 싫고, Confluent에 묶이기도 싫은"** 팀이 자주 선택. Lyft, Reddit, Stripe 같은 곳의 일부 팀이 사용 중.
약점은 SQL coverage. Arroyo SQL은 Flink SQL의 부분집합이라 일부 함수가 없다. 빠르게 따라잡고 있지만 아직.
15장 · Bytewax — Python으로 스트림 처리
Bytewax는 2022년 Bytewax(회사)가 시작한 **Python 스트리밍 프레임워크**. 한 줄 요약: "**Flink와 비슷한데 Python으로 쓴다**".
핵심 특징.
- **Pure Python API**: 데이터 사이언티스트가 직접 쓸 수 있다.
- **Rust 코어 + PyO3**: 성능은 Rust, 사용자 코드는 Python.
- **PyData 통합**: NumPy, Pandas, scikit-learn 직접.
- **Connectors**: Kafka, Redpanda, Kinesis, S3, Iceberg, ClickHouse 등.
기본 예 — 단어 카운트.
from bytewax.dataflow import Dataflow
from bytewax.connectors.kafka import KafkaSource, KafkaSink
from bytewax import operators as op
from bytewax.connectors.stdio import StdOutSink
flow = Dataflow("word-count")
stream = op.input("kafka-in", flow, KafkaSource(
brokers=["kafka:9092"], topics=["text"]
))
words = op.flat_map("split", stream, lambda msg: msg.value.decode().split())
keyed = op.key_on("key", words, lambda w: w)
counts = op.stateful_map("count",
keyed,
lambda: 0,
lambda count, word: (count + 1, (word, count + 1))
)
op.output("stdout", counts, StdOutSink())
Bytewax의 강점은 ML 인퍼런스와의 통합. PyTorch / scikit-learn / Hugging Face 모델을 그대로 stream에 적용 가능.
from transformers import pipeline
clf = pipeline("text-classification", model="distilbert-base-uncased")
def classify(text):
return clf(text)[0]
stream = op.input(...)
labeled = op.map("classify", stream, classify)
op.output("sink", labeled, ...)
이게 Flink로 하려면 Python UDF로 호출해야 하고, 모델 로딩이 비효율적이다. Bytewax는 같은 Python 프로세스 안이라 빠르고 직관적.
약점은 **운영 성숙도**. Flink 처럼 거대한 클러스터, 수년 검증된 exactly-once를 기대하면 안 된다. 100~1000 events/sec 규모, 데이터 사이언스 파이프라인에 적합.
16장 · Apache Beam — 통합 API의 약속과 한계
Apache Beam은 2016년 Google이 Cloud Dataflow의 모델을 오픈소스화한 것. 한 줄 요약: "**Stream과 Batch를 같은 API로**".
핵심 아이디어.
- **PTransform / PCollection / Pipeline** 추상.
- **Runner 별도**: Flink Runner, Spark Runner, Dataflow Runner, Samza Runner.
- **Java / Python / Go SDK**.
from apache_beam.options.pipeline_options import PipelineOptions
with beam.Pipeline(options=PipelineOptions()) as p:
(p
| 'Read' >> beam.io.ReadFromKafka(...)
| 'Window' >> beam.WindowInto(beam.window.FixedWindows(60))
| 'Count' >> beam.combiners.Count.PerKey()
| 'Write' >> beam.io.WriteToBigQuery(...))
같은 코드를 Flink Runner로도, Dataflow Runner로도 돌릴 수 있다. **이론적으로는**.
실제로는 Runner마다 지원 기능이 다르고, 디버깅이 어렵고, native API에 비해 한 단계 추상화가 더 있어 학습 곡선이 가파르다. 2026년 시점에서 Beam은 **Google Cloud Dataflow를 쓰는 팀**이 주로 쓴다. 멀티 클라우드 약속을 진지하게 살리려는 경우는 드물다.
17장 · Decodable, Confluent Cloud Flink, Aiven Flink — Managed Flink 비교
직접 Flink 운영은 어렵다. 그래서 매니지드가 성숙했다.
| 제공자 | 엔진 | 가격 모델 | 특징 |
| --- | --- | --- | --- |
| Confluent Cloud Flink | Flink 1.20+ | Compute Unit + 데이터 처리 | Schema Registry / Kafka 통합 최고 |
| Aiven for Flink | Flink (Apache) | 인스턴스 시간 + 저장 | 멀티 클라우드, Karapace |
| Decodable | Flink + 자체 OS | 처리량 기반 | "5분만에 파이프라인", connector 풍부 |
| Ververica Platform Cloud | Flink + Ververica enterprise | 인스턴스 | Flink original 회사, on-prem 강함 |
| Striim | 자체 엔진(Flink 기반 일부) | 라이센스 | CDC + 스트리밍 결합 |
| Amazon Kinesis Data Analytics → MSF | Flink | 시간 + 처리량 | AWS MSF (Managed Service for Apache Flink) |
2026년 추천 가이드.
- **Kafka 중심 풀스택**: Confluent Cloud Flink.
- **저렴, 멀티 클라우드 OSS**: Aiven Flink.
- **빠른 시작, GUI 우선**: Decodable.
- **AWS 락인 OK**: MSF.
- **On-prem / 엔터프라이즈 지원**: Ververica Platform.
Decodable의 매력은 **YAML로 파이프라인 정의 → 즉시 실행**. 작은 팀에서 "오늘 안에 CDC 파이프라인 만들기" 같은 게 가능하다.
Decodable pipeline.yaml
sources:
- name: orders-kafka
type: kafka
bootstrap_servers: kafka:9092
topic: orders
pipelines:
- name: enrich-orders
sql: |
SELECT o.*, u.segment
FROM orders o
JOIN users u ON o.user_id = u.user_id
sinks:
- name: enriched-snowflake
type: snowflake
table: enriched_orders
18장 · Quix, Pulsar Functions, Memphis, Redpanda Connect — 가벼운 옵션
큰 Flink가 과한 경우의 옵션들.
**Quix Streams** — Python 라이브러리. Bytewax보다 더 가볍고, Kafka에 특화.
from quixstreams import Application
app = Application(broker_address="kafka:9092")
events = app.dataframe(topic=app.topic("events"))
events["amount_x2"] = events["amount"] * 2
events.filter(lambda row: row["amount"] > 100).to_topic(app.topic("alerts"))
app.run()
**Apache Pulsar Functions** — Pulsar 내장. 가벼운 per-message transform.
Pulsar Function (Python)
def process(input, context):
return input.upper()
배포는 `pulsar-admin functions create` 한 줄.
**Memphis.dev / Superstream** — Kafka 호환 OSS 큐, 스트리밍 기능 일부 내장.
**Redpanda Connect** (구 Benthos) — 강력한 YAML 기반 ETL. 200+ connector.
input:
kafka:
addresses: [kafka:9092]
topics: [events]
pipeline:
processors:
- mapping: |
root = this
root.processed_at = now()
root.amount_usd = this.amount * 0.93
output:
kafka:
addresses: [kafka:9092]
topic: events-processed
Redpanda Connect는 "stream processor" 보다 "stream ETL"에 가깝다. 윈도/조인은 약하지만 변환·라우팅·enrichment는 끝내준다.
19장 · Estuary Flow — CDC + 스트리밍의 융합
Estuary Flow는 2023년부터 CDC 스트리밍을 다루는 SaaS. Kafka 같은 메시지 브로커 없이 직접 source-to-sink 스트리밍을 한다.
특징.
- **Gazette** (오픈소스 log broker, Kafka 호환은 아님)을 백본으로.
- **Captures**: Postgres binlog, MongoDB oplog, MySQL, MSSQL CDC.
- **Materializations**: BigQuery, Snowflake, ClickHouse, Postgres sink.
- **Derivations**: SQL/TypeScript로 변환 정의.
captures:
${PREFIX}/source-postgres:
endpoint:
connector:
image: ghcr.io/estuary/source-postgres:dev
config:
address: pg:5432
database: app
user: estuary
password: secret
bindings:
- resource: { table: orders, namespace: public }
target: ${PREFIX}/orders
여기서 `${PREFIX}` 는 Estuary가 내부 변수로 치환하는 환경변수다 (이 패턴 자체가 YAML 내부니까 MDX 파싱은 안전).
Estuary의 정체성은 "Kafka 없는 스트리밍". CDC → 변환 → DW까지 단일 SaaS로. ClickHouse + ELT 백엔드를 빨리 만드는 팀에 인기.
20장 · 백프레셔 — 스트리밍이 어려운 이유 #1
**백프레셔(backpressure)** 는 "소비자가 못 따라가면 어떡할 거냐"의 문제다. 스트림 처리의 80%가 결국 이걸 잘 다루는 것.
문제 시나리오. Kafka에 초당 10만 건 들어옴 → Flink가 변환 → Elasticsearch에 sink. ES가 느려져서 초당 5만건만 받음. 그러면?
- Flink operator chain이 전반에 걸쳐 막힘.
- 결국 Kafka source가 fetch를 멈춤 → 컨슈머 lag 증가.
- 메모리/디스크가 점점 차서 OOM 위험.
대응 패턴.
1. **자연스러운 backpressure (pull-based)**: Flink/Kafka Streams는 기본적으로 pull-based로 backpressure를 자연스럽게 전파.
2. **버퍼링**: Kafka 자체가 거대한 버퍼. 데이터 유실 없이 컨슈머가 캐치업할 시간 줌.
3. **Async I/O**: Flink AsyncFunction으로 외부 호출 동시화.
4. **Drop / Sample**: 진짜 못 따라가면 일부 이벤트 drop (모니터링·메트릭은 OK, 결제는 NO).
5. **Scale out**: 파티션 늘리고 컨슈머 늘림.
Flink의 백프레셔는 **Web UI의 backpressure 탭**에서 색깔로 보인다 (LOW/HIGH). HIGH가 며칠 지속되면 그 operator가 병목.
21장 · Schema 진화와 Schema Registry
스트리밍에서 **schema 호환성**은 운영의 핵심. Avro/Protobuf + Schema Registry가 표준 패턴.
Confluent Schema Registry에 schema 등록
curl -X POST http://schema-registry:8081/subjects/orders-value/versions \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{
"schema": "{ \"type\": \"record\", \"name\": \"Order\", \"fields\": [{\"name\":\"order_id\",\"type\":\"long\"}] }"
}'
호환성 모드 설정
curl -X PUT http://schema-registry:8081/config/orders-value \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"compatibility": "BACKWARD"}'
호환성 모드.
- **BACKWARD**: 새 schema로 old data 읽기 가능 (필드 추가 가능, 삭제는 default 있을 때만).
- **FORWARD**: old schema로 new data 읽기 가능 (필드 삭제 가능, 추가는 ignore).
- **FULL**: 양방향.
- **NONE**: 호환성 검사 안 함 (위험).
권장: **BACKWARD** 기본. Producer는 새 schema로 쓰고, Consumer는 점진적 업그레이드.
22장 · 한국 빅테크 스트림 처리 채택 사례
한국 빅테크의 실제 사례 (공개된 기술 블로그·컨퍼런스 발표 기준).
**쿠팡** — Kafka + Flink + ksqlDB. 실시간 가격 결정, 이상 거래 탐지, 검색 클릭 추적. Disaggregated state 도입으로 페타바이트 state 운영. 2024년 Coupang Engineering Blog에서 Flink-on-Kubernetes 사례 공유.
**NAVER 검색** — 자체 스트림 처리 (Kafka Streams 기반) + Flink SQL. 검색 트래픽 실시간 통계, A/B 테스트 메트릭. 자체 Schema Registry.
**카카오 페이/뱅크** — Flink + Kafka. 거래 실시간 분석, 사기 탐지, 정산 스트림. 2025년 if(kakao)에서 Flink CEP 사기 패턴 사례 발표.
**NCsoft** — Kafka Streams (Java) + Flink. 실시간 게임 매칭, 로그 분석, 어뷰징 탐지. 게임 트래픽 특성상 P99 50ms 이하 요구.
**배달의민족 (우아한형제들)** — Kafka + Flink + Elasticsearch. 실시간 배달 위치, 주문 상태 머신, 이상 패턴. 2024년 우아콘에서 Flink-on-K8s 운영 공유.
**카카오 모빌리티** — Kafka Streams + 자체 처리. 실시간 매칭, 위치 트래킹, 동적 가격.
**토스** — Kafka + Flink + Druid. 실시간 결제 모니터링, BI 대시보드. 2025년 SLASH에서 Materialize 평가 사례 공유 (전환은 아직).
**NAVER Z (제페토)** — Kafka + Flink. 가상 공간 이벤트 처리, 실시간 행동 분석.
**SK텔레콤** — Kafka + Spark Structured Streaming → Flink로 점진 마이그레이션. 통화 품질·기지국 메트릭.
23장 · 일본 빅테크 스트림 처리 채택 사례
**Mercari** — Kafka + Flink (GCP Dataflow도 일부). 실시간 사기 탐지 (CEP 적극), 추천, 상품 검색 인덱싱. 2025년 Mercari Engineering Blog에서 Flink CEP 사기 패턴 사례.
**LINE Yahoo** — 자체 Kafka + Flink + ksqlDB. 실시간 푸시 알림 라우팅, 트렌딩 검출, 광고 입찰. 2024년 LY Tech Conf에서 Flink-on-K8s 사례.
**Rakuten** — Kafka + Spark Streaming → Flink + RisingWave 검토 중. 실시간 가격, 추천. 글로벌 멀티 리전.
**SmartNews** — Kafka + Flink. 실시간 뉴스 트렌딩, 사용자 행동 추적. 광고 입찰에 CEP.
**CyberAgent** — Kafka + Flink. 광고 입찰, 동영상 시청 분석, AbemaTV 실시간 시청 메트릭.
**ZOZO** — Kafka + Flink. 상품 검색 인덱싱, 추천, 사용자 행동.
**DeNA** — Kafka + Spark Streaming + Flink. 게임 어뷰징, 결제 모니터링, 마케팅 분석.
**LINE Pay** — Flink + Kafka. 거래 모니터링, 사기 탐지.
**Mixi** — Kafka + Flink (특히 모바일 게임 분야). 실시간 이벤트 카운터, 매칭.
**NTT Communications** — Apache Beam + Dataflow. IoT 텔레메트리.
24장 · 운영 패턴 — 클러스터, 모니터링, 디버깅
스트리밍 잡의 운영은 OLTP·OLAP와 다르다. 24/7 돌고, state가 누적되고, 한 번 잘못되면 backfill이 끔찍하다.
기본 가이드.
1. **분리된 클러스터 / 분리된 잡**: 한 잡 OOM이 다른 잡에 옮지 않도록.
2. **체크포인트 빈도**: 30s ~ 1m이 일반적. 짧으면 부하, 길면 복구 느림.
3. **State TTL 필수**: 안 그러면 keyed state가 무한 누적.
4. **메트릭**: Prometheus + Grafana로 Flink JobManager·TaskManager 메트릭. lag, checkpoint 시간, backpressure, state 크기.
5. **알람**: 컨슈머 lag, 체크포인트 실패, restart 카운트, late events.
6. **Backfill 전략**: state-free 잡은 Kafka 처음부터 재처리. state-ful은 savepoint 활용.
7. **Blue/green 배포**: 새 버전을 별도 컨슈머 그룹으로 띄우고, 결과 비교 후 cutover.
Flink-on-Kubernetes는 표준이 됐다. Flink Kubernetes Operator (Apache OSS)가 안정화되어 Helm chart 한 줄로 시작 가능.
helm repo add flink-operator-repo https://downloads.apache.org/flink/flink-kubernetes-operator-1.10.0/
helm install flink-kubernetes-operator flink-operator-repo/flink-kubernetes-operator
FlinkDeployment CRD로 잡 정의.
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
name: my-streaming-job
spec:
image: flink:2.0
flinkVersion: v2_0
taskManager:
replicas: 4
resource: { memory: "4096m", cpu: 2 }
job:
jarURI: s3://my-jobs/my-job.jar
parallelism: 8
upgradeMode: savepoint
25장 · 비용 모델 — Stream Processing의 진짜 가격
스트림 처리의 비용은 **항상 켜져 있다**는 데서 온다. 배치는 끝나면 0이지만 스트리밍은 24/7.
비용 구성.
- **컴퓨트**: TaskManager 인스턴스. 보통 가장 큰 비용.
- **상태 저장**: RocksDB local SSD 또는 S3 (ForSt).
- **Kafka inter-AZ 전송**: 위에서 본 Kafka 비용.
- **체크포인트 S3 PUT/GET**.
- **모니터링·로그 비용 (Datadog, NewRelic)**.
비용 절감 패턴.
1. **state TTL로 RAM 줄이기**.
2. **Disaggregated state (ForSt, RisingWave)로 SSD 절약**.
3. **Auto-scaling**: Flink Adaptive Scheduler로 부하에 따라 TM 수 조절.
4. **Spot/Preemptible 인스턴스**: stateless operator는 spot OK.
5. **Confluent KIP-848 / Share Groups로 컨슈머 효율화**.
6. **WarpStream + Flink**: state는 ForSt, 데이터는 WarpStream → S3 비용만.
Confluent Cloud Flink는 Compute Pool 단위로 과금하고, 사용 안 하면 0으로 축소(scale to zero). 작은 팀에는 적합하지만 hot 잡 비용은 일반적으로 OSS Flink + EKS보다 비싸다.
26장 · 마무리 — 2026년 스트림 처리 추천
긴 글이었다. 마지막으로 한 줄 가이드.
- **Kafka 중심 + Java 팀 + Stream 처리 시작**: **Kafka Streams + ksqlDB** (소형) 또는 **Flink SQL** (중대형).
- **복잡한 CEP / 큰 state / 멀티 소스**: **Apache Flink 2.0** (DataStream + SQL).
- **앱이 Postgres로 쿼리 / Materialized View 필요**: **RisingWave 2.x** (대형) 또는 **Materialize** (복잡 SQL).
- **Python ML 통합 / 작은 규모**: **Bytewax** 또는 **Quix Streams**.
- **운영하기 싫고 GUI 필요**: **Decodable** 또는 **Confluent Cloud Flink**.
- **단순 ETL/변환 (윈도 거의 없음)**: **Redpanda Connect (Benthos)** 또는 **Pulsar Functions**.
- **CDC + 스트리밍 통합**: **Estuary Flow** 또는 **Debezium + Flink**.
- **Rust 모노톤 단일 바이너리 선호**: **Arroyo**.
2026년에 가장 큰 트렌드는 **streaming database의 부상**이다. RisingWave와 Materialize가 "스트림 처리는 데이터 엔지니어 영역" 이라는 통념을 깨고 있다. 한 줄의 `CREATE MATERIALIZED VIEW`로 백엔드 개발자가 직접 실시간 view를 만든다. Flink는 여전히 강력하지만, "**Flink가 필요한가?**" 라는 질문을 먼저 해야 하는 시대가 왔다.
그리고 또 한 가지 — **상태가 클라우드 스토리지로 이동** 중이다. ForSt, RisingWave의 S3 backed state, Arroyo의 디자인 모두 같은 방향. broker가 stateless에 가까워지고, 데이터는 S3에. 이건 Kafka에서 시작된 disaggregation 트렌드가 stream processor까지 온 것.
긴 글의 끝. 2026년의 스트림 처리는 더 이상 한 가지 정답이 아니다. **선택지가 많아졌고, 그래서 선택의 책임이 커졌다**. 부디 자기 워크로드에 맞는 도구를 고르길.
참고 자료
- [Apache Flink 2.0 Release Notes](https://nightlies.apache.org/flink/flink-docs-release-2.0/)
- [Flink FLIP-435: Materialized Tables](https://cwiki.apache.org/confluence/display/FLINK/FLIP-435%3A+Introduce+a+New+Dynamic+Table+for+Simplifying+Data+Pipelines)
- [Flink ForSt (Disaggregated State)](https://github.com/ververica/ForSt)
- [Apache Kafka Streams Documentation](https://kafka.apache.org/40/documentation/streams/)
- [KIP-932: Queues for Kafka](https://cwiki.apache.org/confluence/display/KAFKA/KIP-932%3A+Queues+for+Kafka)
- [ksqlDB Documentation](https://docs.ksqldb.io/)
- [RisingWave Documentation](https://docs.risingwave.com/)
- [Materialize Documentation](https://materialize.com/docs/)
- [Materialize: Differential Dataflow Paper](https://github.com/TimelyDataflow/differential-dataflow)
- [Arroyo Streams](https://www.arroyo.dev/)
- [Bytewax Documentation](https://bytewax.io/docs)
- [Apache Beam Documentation](https://beam.apache.org/documentation/)
- [Apache Pulsar Functions](https://pulsar.apache.org/docs/functions-overview/)
- [Quix Streams](https://quix.io/docs/quix-streams/)
- [Decodable Documentation](https://docs.decodable.co/)
- [Confluent Cloud Flink](https://docs.confluent.io/cloud/current/flink/overview.html)
- [Aiven for Apache Flink](https://aiven.io/flink)
- [Amazon MSF (Managed Service for Apache Flink)](https://docs.aws.amazon.com/managed-flink/)
- [Estuary Flow](https://docs.estuary.dev/)
- [Redpanda Connect (Benthos)](https://docs.redpanda.com/redpanda-connect/)
- [Memphis.dev / Superstream](https://memphis.dev/)
- [Confluent Schema Registry](https://docs.confluent.io/platform/current/schema-registry/index.html)
- [Streaming Systems book — Tyler Akidau](https://www.oreilly.com/library/view/streaming-systems/9781491983867/)
- [Mercari Engineering: Flink CEP](https://engineering.mercari.com/en/blog/)
- [Coupang Engineering Blog](https://medium.com/coupang-engineering)
- [LINE Yahoo Engineering](https://techblog.lycorp.co.jp/en/)
현재 단락 (1/616)
2026년 어느 핀테크 팀 회의실.