핵심 인사이트 (3줄 요약)

  • 본질: Apache Flink는 두 가지 프로그래밍 계층을 제공한다. DataStream API (데이터스트림 API)는 이벤트 단위의 세밀한 스트림 처리를 위한 저수준 API이고, Table API & SQL (테이블 API)은 관계형 테이블 개념을 스트림에 적용한 고수준 선언적 API로, 두 계층은 내부적으로 동일한 DataStream 실행 엔진으로 컴파일된다.
  • 가치: SQL을 아는 데이터 엔지니어는 Table API/SQL로 스트리밍 집계·조인·윈도우를 신속하게 개발하고, 복잡한 상태 관리나 커스텀 타임스탬프 추출이 필요한 경우 DataStream API로 세밀하게 제어하는 계층화된 유연성이 Flink의 큰 장점이다.
  • 판단 포인트: Table API/SQL은 선언적이라 Flink가 최적화(술어 푸시다운, 공통 부분식 제거 등)를 자동으로 수행하지만, DataStream API는 개발자가 직접 최적화해야 하므로 운영 경험과 스트리밍 지식이 더 많이 요구된다.

Ⅰ. 개요 및 필요성

사용자 편의성 ↑          ↑ 표현력
┌────────────────────────────────────────┐
│  SQL (문자열 SQL 쿼리)                  │  ← 가장 선언적
├────────────────────────────────────────┤
│  Table API (Java/Scala/Python DSL)     │
├────────────────────────────────────────┤
│  DataStream / DataSet API              │  ← 세밀한 제어
├────────────────────────────────────────┤
│  Stateful Functions (저수준)           │  ← 가장 강력한 제어
└────────────────────────────────────────┘

2. 각 API의 사용 상황

  • SQL: "Kafka 토픽에서 5분 집계를 구하라" — BI 엔지니어, 빠른 프로토타이핑
  • Table API: SQL이지만 프로그래밍 방식으로 동적 쿼리 생성 필요 시
  • DataStream API: 커스텀 타임스탬프 추출, 복잡한 상태 로직, ML 모델 인라인 실행

📢 섹션 요약 비유

Flink의 두 계층은 "자동 변속기(Table/SQL)와 수동 변속기(DataStream API)"와 같다. 일반 운전에는 자동이 편리하지만, 험한 오프로드(복잡한 비즈니스 로직)는 수동이 더 정밀하게 제어된다.


Ⅱ. 아키텍처 및 핵심 원리

1. DataStream API 핵심 구조

// Java DataStream API 예시
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

// 1. Source: Kafka에서 데이터 읽기
DataStream<String> stream = env.fromSource(
    KafkaSource.<String>builder()
        .setBootstrapServers("kafka:9092")
        .setTopics("events")
        .build(),
    WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(5)),
    "KafkaSource"
);

// 2. Transformation
DataStream<Tuple2<String, Integer>> result = stream
    .map(json -> parseEvent(json))          // map: 1:1 변환
    .filter(e -> e.getAmount() > 100)        // filter: 필터링
    .keyBy(e -> e.getUserId())               // keyBy: 키 분할
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))  // 윈도우
    .sum(1);                                 // 집계

// 3. Sink: 결과 저장
result.addSink(new ElasticsearchSink<>(...));

env.execute("UserActivityAggregation");

2. Table API & SQL 핵심 구조

// Java Table API + SQL 예시
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

// DataStream → Table 변환
Table eventTable = tableEnv.fromDataStream(
    stream,
    Schema.newBuilder()
        .columnByExpression("rowtime", "CAST(ts AS TIMESTAMP_LTZ(3))")
        .watermark("rowtime", "rowtime - INTERVAL '5' SECOND")
        .build()
);

// SQL로 5분 텀블링 윈도우 집계
Table result = tableEnv.sqlQuery(
    "SELECT user_id, TUMBLE_END(rowtime, INTERVAL '5' MINUTE) as window_end, " +
    "       SUM(amount) as total_amount " +
    "FROM events " +
    "GROUP BY user_id, TUMBLE(rowtime, INTERVAL '5' MINUTE)"
);

// Table → DataStream 변환 (다시 저수준으로)
DataStream<Row> outputStream = tableEnv.toDataStream(result);

3. 연산자 비교

연산DataStream APITable API / SQL
필터.filter(pred)WHERE condition
변환.map(func)SELECT expr
집계.reduce() / .aggregate()GROUP BY ... SUM()
조인.connect().flatMap()JOIN ... ON
윈도우.window(TumblingEventTime...)TUMBLE(), HOP(), SESSION()
상태ValueState, MapState 직접 사용내부 자동 처리

📢 섹션 요약 비유

DataStream API는 "재료를 직접 손질하고 요리하는 셰프"이고, Table API/SQL은 "레시피 카드(SQL)대로 로봇이 자동으로 조리하는 방식"이다. 셰프는 더 창의적이지만 기술이 필요하고, 로봇은 빠르고 표준화되어 있다.


Ⅲ. 비교 및 연결

표준 SQL에 없는 스트리밍 전용 구문들:

-- 텀블링 윈도우 집계
SELECT TUMBLE_START(event_time, INTERVAL '10' MINUTE) AS window_start,
       user_id, COUNT(*) AS cnt
FROM events
GROUP BY TUMBLE(event_time, INTERVAL '10' MINUTE), user_id;

-- 슬라이딩 윈도우 (HOP)
SELECT HOP_START(event_time, INTERVAL '5' MINUTE, INTERVAL '1' HOUR) AS win_start,
       AVG(score) AS avg_score
FROM events
GROUP BY HOP(event_time, INTERVAL '5' MINUTE, INTERVAL '1' HOUR);

-- 스트림-스트림 인터벌 조인
SELECT a.user_id, b.product_id
FROM clicks a JOIN purchases b
ON a.user_id = b.user_id
AND b.event_time BETWEEN a.event_time AND a.event_time + INTERVAL '30' MINUTE;

2. API 간 상호 변환

// Table → DataStream (복잡한 로직 처리 후 Table로 복귀)
DataStream<Row> ds = tableEnv.toDataStream(table);
DataStream<Row> processed = ds.process(new ComplexProcessFunction());
Table backToTable = tableEnv.fromDataStream(processed);

📢 섹션 요약 비유

Flink의 두 API 전환은 "번역가(Table API/SQL)와 원어민 대화(DataStream API)를 필요에 따라 섞어 쓰는 것"이다. 표준 대화는 번역기로 충분하지만, 세밀한 감정 표현은 원어민과 직접 소통해야 한다.


Ⅳ. 실무 적용 및 기술사 판단

1. API 선택 가이드

요구사항권장 API
Kafka 이벤트 SQL 집계/필터Table API / SQL
5분 윈도우 집계 대시보드SQL (TUMBLE 윈도우)
커스텀 타임스탬프 파싱DataStream API
ML 모델 인라인 호출DataStream API
CDC(Change Data Capture) 처리Table API (Upsert 커넥터)
복잡한 패턴 매칭 (CEP)DataStream API + Flink CEP 라이브러리

2. 주요 커넥터 (Source/Sink)

커넥터방향비고
Apache Kafka ConnectorSource + SinkExactly-Once 지원
JDBC ConnectorSource + SinkMySQL, PostgreSQL
Elasticsearch ConnectorSink검색 인덱스 업데이트
Hadoop FileSystemSource + SinkHDFS, S3
Apache HBase ConnectorSource + Sink랜덤 읽기/쓰기

📢 섹션 요약 비유

DataStream API는 "맞춤 양복", Table API/SQL은 "기성복"이다. 기성복(SQL)이 대부분 상황에 잘 맞고 빠르지만, 특별한 체형(복잡한 비즈니스 로직)에는 맞춤 양복(DataStream)이 필요하다.


Ⅴ. 기대효과 및 결론

1. 기대효과

효과설명
개발 생산성SQL 알면 스트리밍 앱 빠르게 개발 가능
최적화 자동화Table API/SQL은 Flink 옵티마이저가 자동 최적화
유연성두 API 혼용으로 선언적+절차적 처리 결합
생태계 통합JDBC, Kafka, ES 등 다양한 커넥터

2. 결론

Flink의 DataStream API와 Table API/SQL은 상호 보완적인 두 층의 프로그래밍 모델이다. 기술사 답안에서는 두 API의 추상화 수준 차이, 내부적으로 동일한 실행 엔진으로 컴파일된다는 통합성, 그리고 스트리밍 SQL의 고유 구문(TUMBLE, HOP, SESSION 윈도우)을 서술하는 것이 핵심이다.

📢 섹션 요약 비유

Flink의 두 API는 "같은 공장의 두 입구"다. 자동화 생산 라인(Table API/SQL)으로 들어가면 로봇이 알아서 처리하고, 수작업 라인(DataStream API)으로 들어가면 세밀하게 직접 제어한다. 두 라인의 결과물은 같은 공장 창고에 모인다.


📌 관련 개념 맵

개념관계설명
Flink 아키텍처실행 환경DataStream/Table API의 실행 기반
윈도우 연산핵심 활용TUMBLE/HOP/SESSION 윈도우
Watermark연동 개념Table API의 WATERMARK 정의와 연동
Kafka 커넥터Source/Sink가장 많이 사용되는 스트리밍 소스
CEP (Complex Event Processing)확장 기능DataStream API 위의 패턴 감지 라이브러리

📈 관련 키워드 및 발전 흐름도

[Flink RDD]
    │
    ▼
[DataStream API]
    │
    ▼
[Table API/SQL]
    │
    ▼
[통합 스트리밍]
    │
    ▼
[Kappa 아키텍처]

Flink의 스트리밍 API가 저수준 RDD에서 고수준 SQL까지 통합되며 카파 아키텍처로 수렴하는 흐름이다.

👶 어린이를 위한 3줄 비유 설명

Flink에는 두 가지 요리 방법이 있어요. Table API/SQL은 "레시피대로 요리하는 방법"(편리하고 빠름)이고, DataStream API는 "셰프가 직접 창의적으로 요리하는 방법"(어렵지만 자유로움)이에요. 같은 Flink 주방(실행 엔진)에서 요리하지만, 어떤 방법으로 요청하느냐가 다를 뿐이고 최종 음식(결과 데이터)은 같은 품질이랍니다!