2026년 Apache Spark 4 완벽 가이드: 신규 기능, Structured Streaming, 면접 질문
Apache Spark 4의 핵심 신규 기능인 ANSI SQL 모드, VARIANT 데이터 타입, 실시간 스트리밍 모드, Spark Connect를 심층 분석합니다. 데이터 엔지니어링 면접을 위한 필수 질문과 답변도 함께 제공합니다.

Apache Spark 4는 버전 3.0 이후 가장 대규모의 아키텍처 개편을 단행한 릴리스입니다. 2025년 중반 출시 이후 4.1, 4.2로 빠르게 진화하며, 데이터 엔지니어가 파이프라인을 구축하고 스트리밍 워크로드를 실행하며 대규모 SQL을 처리하는 방식을 근본적으로 변화시키고 있습니다. 본 가이드에서는 핵심 변경 사항을 PySpark 및 Scala 코드와 함께 상세히 분석하고, 채용 팀에서 가장 빈번하게 출제하는 데이터 엔지니어링 면접 질문을 다룹니다.
Spark 4.0.0은 2025년 5월에 390명 이상의 기여자가 해결한 5,100건 이상의 티켓과 함께 출시되었습니다. Spark 4.1.0은 2025년 12월에 뒤이어 출시되어 실시간 모드 스트리밍과 선언적 파이프라인이 추가되었습니다. Spark 4.2 프리뷰 빌드는 2026년 2월에 시작되었습니다.
Spark 4에서 ANSI SQL 모드 기본 활성화
Spark 4.0은 spark.sql.ansi.enabled를 기본값 true로 변경했습니다. 이전에 NULL을 반환하거나 조용히 값을 절삭하던 산술 오버플로우, 잘못된 캐스팅, 0으로 나누기가 이제 모두 예외를 발생시킵니다. 목표는 PostgreSQL 및 Oracle의 동작과 일치하는 엄격한 SQL 표준 준수입니다.
실무적 영향은 ETL 파이프라인에서 가장 크게 나타납니다. 암묵적 타입 변환이나 무음 NULL 전파에 의존하던 쿼리는 명시적으로 처리하지 않는 한 런타임에 실패합니다.
# ansi_mode_migration.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, try_cast
spark = SparkSession.builder.appName("ANSIMigration").getOrCreate()
# This now throws ArithmeticException in Spark 4 ANSI mode
# SELECT CAST('abc' AS INT) -- runtime error
# Safe migration: use TRY_CAST to preserve NULL behavior
df = spark.sql("""
SELECT
TRY_CAST(raw_value AS INT) AS parsed_value,
TRY_CAST(amount AS DECIMAL(10,2)) AS safe_amount
FROM raw_events
WHERE TRY_CAST(raw_value AS INT) IS NOT NULL
""")
# Alternatively, wrap arithmetic in try_divide / try_add
result = df.withColumn(
"ratio",
col("safe_amount") / col("parsed_value") # throws on zero
)마이그레이션 체크리스트로는 모든 CAST 호출을 감사하고, NULL 안전 동작이 필요한 곳에서 TRY_CAST로 교체하며, 숫자 컬럼에 대한 명시적 오버플로우 검사를 추가하는 것이 권장됩니다.
VARIANT 데이터 타입: 반정형 데이터의 새로운 표준
Spark 4.0은 JSON과 같은 반정형 데이터를 위한 네이티브 VARIANT 컬럼 타입을 추가했습니다. 원시 JSON 문자열을 저장하고 쿼리 시점에 파싱하는 기존 방식과 달리, VARIANT는 컬럼 슈레딩을 포함한 최적화된 바이너리 포맷을 사용하여 중첩 필드 접근에서 최대 8배 빠른 읽기 성능을 제공합니다.
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder.appName("VariantDemo").getOrCreate()
import spark.implicits._
// Create a table with VARIANT column
spark.sql("""
CREATE TABLE events (
event_id BIGINT,
timestamp TIMESTAMP,
payload VARIANT
) USING PARQUET
""")
// Insert JSON data — automatically converted to VARIANT
spark.sql("""
INSERT INTO events VALUES
(1, current_timestamp(),
PARSE_JSON('{"user_id": 42, "action": "click", "metadata": {"page": "/home", "duration_ms": 320}}')),
(2, current_timestamp(),
PARSE_JSON('{"user_id": 87, "action": "purchase", "metadata": {"item_id": "SKU-9001", "amount": 49.99}}'))
""")
// Query nested fields with dot notation — uses shredding for fast access
val clicks = spark.sql("""
SELECT
event_id,
payload:user_id::INT AS user_id,
payload:action::STRING AS action,
payload:metadata.page::STRING AS page
FROM events
WHERE payload:action::STRING = 'click'
""")
clicks.show()VARIANT는 스키마 온 리드 우회 방법을 불필요하게 만듭니다. 이벤트 스키마가 빈번하게 변경되는 데이터 레이크 아키텍처에서 단일 VARIANT 컬럼이 DDL 마이그레이션 없이 스키마 변경을 흡수합니다. Spark 4.1에서는 Parquet 수준의 슈레딩이 확장되어 선택적 필드 접근의 IO가 더욱 감소했습니다.
Spark 4.1의 실시간 모드 스트리밍
Spark 4.1의 Structured Streaming에 실시간 모드(RTM)가 도입되었습니다. Spark에서 서브초 지연시간을 공식적으로 지원하는 최초의 기능입니다. 무상태 워크로드의 경우 지연시간이 한 자릿수 밀리초까지 감소하여 Apache Flink의 핵심 영역에서 직접 경쟁합니다.
이를 가능하게 하는 세 가지 아키텍처 변경이 있습니다. 연속 데이터 흐름(마이크로배치 경계 없음), 파이프라인 스케줄링(읽기/연산/쓰기 단계 오버랩), 스트리밍 셔플(배치 간 체크포인트 대신 메모리 내 상태 저장)입니다.
# real_time_streaming.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import from_json, col, window
from pyspark.sql.types import StructType, StructField, StringType, DoubleType, TimestampType
spark = SparkSession.builder \
.appName("RTMDemo") \
.config("spark.sql.streaming.mode", "realtime") \
.getOrCreate()
schema = StructType([
StructField("sensor_id", StringType()),
StructField("temperature", DoubleType()),
StructField("event_time", TimestampType())
])
# Read from Kafka with Real-Time Mode enabled
raw_stream = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "broker:9092") \
.option("subscribe", "sensor-readings") \
.option("startingOffsets", "latest") \
.load()
parsed = raw_stream \
.select(from_json(col("value").cast("string"), schema).alias("data")) \
.select("data.*")
# Write to Kafka sink — sub-second end-to-end
query = parsed \
.filter(col("temperature") > 80.0) \
.writeStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "broker:9092") \
.option("topic", "alerts") \
.option("checkpointLocation", "/tmp/checkpoints/alerts") \
.start()
query.awaitTermination()RTM 활성화에는 단일 설정 변경만 필요합니다: spark.sql.streaming.mode = realtime. 코드 재작성이 필요하지 않습니다. Spark 4.1은 Kafka 소스 및 Kafka/Foreach 싱크를 사용하는 무상태 단일 스테이지 Scala 및 PySpark 쿼리부터 지원을 시작합니다. 상태 저장 RTM 지원은 Spark 4.2에서 예정되어 있습니다.
Spark Connect와 경량 PySpark 클라이언트
Spark 4.0은 단 1.5MB 크기의 독립형 pyspark-client 패키지를 제공합니다. 전체 PySpark 배포판의 300MB 이상과 비교하면 극히 경량입니다. 이 씬 클라이언트는 Spark Connect를 통해 gRPC로 원격 Spark 클러스터와 통신하며, 노트북 환경과 CI 파이프라인이 전체 런타임을 번들링하지 않고도 Spark 작업을 실행할 수 있게 합니다.
# spark_connect_client.py
from pyspark.sql import SparkSession
# Connect to remote Spark cluster — no local JVM needed
spark = SparkSession.builder \
.remote("sc://spark-cluster.internal:15002") \
.getOrCreate()
# Full DataFrame API available through thin client
df = spark.read.parquet("s3a://data-lake/events/2026/")
aggregated = df.groupBy("country", "event_type") \
.count() \
.orderBy("count", ascending=False)
aggregated.show(20)Spark Connect는 Java 및 Swift 클라이언트도 지원합니다. spark.api.mode 설정으로 기존 임베디드 모드와 Connect 모드를 코드 변경 없이 전환할 수 있습니다. 데이터 엔지니어링 팀에게 이는 클라이언트 환경을 클러스터 업그레이드로부터 분리하는 것을 의미합니다. 서버를 업데이트해도 클라이언트는 계속 동작합니다.
Data Engineering 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
transformWithState API: 복잡한 스트리밍 로직 처리
Spark 4.0은 제한적이었던 mapGroupsWithState를 대체하여 transformWithState를 도입했습니다. 복합 상태 타입, TTL 기반 만료, 이벤트 구동 타이머를 지원하는 유연한 API입니다.
import org.apache.spark.sql.streaming._
import org.apache.spark.sql.streaming.StatefulProcessor
import java.time.Duration
case class UserEvent(userId: String, action: String, timestamp: Long)
case class SessionSummary(userId: String, actionCount: Int, durationMs: Long)
class SessionProcessor extends StatefulProcessor[String, UserEvent, SessionSummary] {
// ValueState with 30-minute TTL — auto-cleanup of idle sessions
@transient private var sessionStart: ValueState[Long] = _
@transient private var actionCount: ValueState[Int] = _
override def init(outputMode: OutputMode, timeMode: TimeMode): Unit = {
sessionStart = getHandle.getValueState[Long]("start", TTLConfig(Duration.ofMinutes(30)))
actionCount = getHandle.getValueState[Int]("count", TTLConfig(Duration.ofMinutes(30)))
}
override def handleInputRows(
key: String,
rows: Iterator[UserEvent],
timerValues: TimerValues
): Iterator[SessionSummary] = {
rows.flatMap { event =>
if (!sessionStart.exists()) {
sessionStart.update(event.timestamp)
actionCount.update(1)
Iterator.empty
} else {
val count = actionCount.get() + 1
actionCount.update(count)
// Emit summary every 10 actions
if (count % 10 == 0) {
val duration = event.timestamp - sessionStart.get()
Iterator(SessionSummary(key, count, duration))
} else Iterator.empty
}
}
}
}세 가지 상태 타입이 대부분의 스트리밍 유스케이스를 커버합니다. 키당 단일 값을 위한 ValueState, 이벤트 로그와 같은 추가 중심 패턴을 위한 ListState, 그룹화 키 내 키-값 조회를 위한 MapState입니다. TTL 기반 만료는 수동 상태 정리를 불필요하게 하며, 오래된 상태가 메모리 압박을 유발하는 장기 실행 파이프라인에서 필수적인 기능입니다.
Spark 4.1의 Spark 선언적 파이프라인(SDP)
Spark 4.1에는 Spark 선언적 파이프라인이 추가되었습니다. 엔지니어가 데이터셋과 변환을 정의하면 Spark가 실행 순서, 병렬화, 체크포인트, 재시도를 처리하는 프레임워크입니다. SDP는 SQL 중심 변환 워크플로우에서 dbt와 직접 경쟁하지만, Spark 내부에서 네이티브로 실행됩니다.
# declarative_pipeline.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, sum as _sum, current_date
spark = SparkSession.builder.appName("SDP-Demo").getOrCreate()
# Define datasets declaratively — Spark resolves dependencies
@spark.declarative.table("clean_orders")
def clean_orders():
return spark.read.table("raw_orders") \
.filter(col("status") != "cancelled") \
.filter(col("amount") > 0)
@spark.declarative.table("daily_revenue")
def daily_revenue():
return spark.read.table("clean_orders") \
.groupBy("order_date", "region") \
.agg(_sum("amount").alias("total_revenue"))
@spark.declarative.table("revenue_summary")
def revenue_summary():
return spark.read.table("daily_revenue") \
.groupBy("region") \
.agg(_sum("total_revenue").alias("grand_total")) \
.orderBy("grand_total", ascending=False)
# Execute — Spark builds the DAG, parallelizes, handles retries
spark.declarative.run()SDP는 의존성 그래프를 자동으로 관리합니다. clean_orders가 실패하면 하류 테이블만 차단됩니다. 체크포인트는 중간 결과를 영구 저장하므로, 장애 복구 시 이미 완료된 단계를 건너뜁니다. 오케스트레이션에 Airflow를 사용하는 팀에게 SDP는 파이프라인 내부 의존성을 처리하고 Airflow가 파이프라인 간 스케줄링을 관리하는 역할 분담을 제공합니다.
Spark 4 데이터 엔지니어링 면접 질문
채용 팀에서 Spark 4 관련 지식을 테스트하는 빈도가 증가하고 있습니다. 가장 자주 출제되는 질문과 핵심 답변을 정리합니다.
Spark 관련 질문에 답변할 때는 해당 동작이 적용되는 버전을 반드시 명시해야 합니다. Spark 4는 여러 기본값을 변경했으며, 특히 ANSI 모드와 스트리밍 시맨틱스에서 Spark 3.x 답변과 모순될 수 있습니다.
Q1: Spark 4에서 ANSI 모드가 기본 활성화되면 무엇이 달라집니까?
산술 오버플로우, 잘못된 캐스팅, 0으로 나누기가 NULL 반환이나 무음 래핑 대신 런타임 예외를 발생시킵니다. 마이그레이션을 위해 CAST를 TRY_CAST로 교체하고 숫자 연산에 명시적 오류 처리를 추가해야 합니다.
Q2: VARIANT 데이터 타입과 사용 시점을 설명하십시오.
VARIANT는 컬럼 슈레딩을 포함한 최적화된 바이너리 포맷으로 반정형 데이터를 저장합니다. 원시 JSON 문자열과 달리 VARIANT는 스토리지 계층에서 조건절 푸시다운과 선택적 필드 읽기를 가능하게 합니다. 스키마가 진화하는 이벤트 데이터, API 응답 로깅, 행마다 구조가 다른 컬럼에 최적입니다.
Q3: 실시간 모드와 마이크로배치 스트리밍의 차이점은 무엇입니까?
마이크로배치는 이산적 간격(보통 100ms 이상)으로 데이터를 처리합니다. RTM은 파이프라인 스케줄링을 사용한 연속 데이터 흐름으로 무상태 워크로드에서 한 자릿수 밀리초 지연시간을 달성합니다. RTM은 배치 경계를 완전히 제거하여 소스에서 연산자를 거쳐 싱크까지 단계 간 체크포인트 없이 데이터가 흐릅니다.
Q4: transformWithState란 무엇이며 왜 mapGroupsWithState를 대체합니까?
transformWithState는 복합 상태 타입(ValueState, ListState, MapState), 네이티브 TTL 기반 만료, 이벤트 구동 타이머를 지원합니다. mapGroupsWithState는 수동 타임아웃 처리를 동반한 단일 불투명 상태 객체만 지원했습니다. 새로운 API는 세션 윈도우, 중복 제거, 상태 저장 집계의 보일러플레이트를 줄여줍니다.
Q5: Spark Connect가 해결하는 문제는 무엇입니까?
Spark Connect는 gRPC를 통해 클라이언트를 Spark 런타임으로부터 분리합니다. pyspark-client 패키지는 전체 PySpark의 300MB 이상 대비 단 1.5MB입니다. 클라이언트가 로컬 JVM 없이 원격 클러스터에 연결할 수 있어 경량 노트북 환경과 독립적인 클라이언트/서버 버전 업그레이드가 가능합니다.
Spark 4 완전한 ETL 파이프라인 구축
전체 기능을 결합하면 Kafka에서 읽어 VARIANT 페이로드를 파싱하고, ETL 패턴을 적용하여 Delta Lake 테이블에 기록하는 파이프라인을 구축할 수 있습니다.
# spark4_etl_pipeline.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col, from_json, current_timestamp, parse_json
spark = SparkSession.builder \
.appName("Spark4ETL") \
.config("spark.sql.ansi.enabled", "true") \
.getOrCreate()
# Bronze: raw ingestion from Kafka
bronze = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "broker:9092") \
.option("subscribe", "user-events") \
.load() \
.select(
col("key").cast("string").alias("event_key"),
parse_json(col("value").cast("string")).alias("payload"),
col("timestamp").alias("kafka_ts")
)
# Silver: extract typed fields from VARIANT
silver = bronze.select(
col("event_key"),
col("payload:user_id").cast("int").alias("user_id"),
col("payload:action").cast("string").alias("action"),
col("payload:metadata.session_id").cast("string").alias("session_id"),
col("kafka_ts"),
current_timestamp().alias("processed_at")
).filter(
col("user_id").isNotNull() # ANSI mode makes this explicit
)
# Gold: write to Delta Lake with schema enforcement
query = silver.writeStream \
.format("delta") \
.outputMode("append") \
.option("checkpointLocation", "s3a://checkpoints/user-events/") \
.option("mergeSchema", "true") \
.toTable("gold.user_events")
query.awaitTermination()이 파이프라인은 Spark 4의 세 가지 기능을 결합합니다. 유연한 페이로드 파싱을 위한 VARIANT, 엄격한 타입 안전성을 위한 ANSI 모드, 그리고 지속적 수집을 위한 Structured Streaming입니다. 메달리온 아키텍처(Bronze/Silver/Gold)는 Spark의 읽기-변환-쓰기 모델에 정확히 매핑됩니다.
결론
- ANSI 모드는 기본 SQL 동작을 변경합니다. Spark 3.x에서 업그레이드하기 전에 모든
CAST와 숫자 연산을 감사해야 합니다 - VARIANT는 JSON 문자열 컬럼을 최적화된 바이너리 포맷으로 대체하여 중첩 필드 접근에서 최대 8배 빠른 성능을 제공합니다
- Spark 4.1의 실시간 모드는 단일 설정 변경으로 활성화되는 무상태 스트리밍에서 한 자릿수 밀리초 지연시간을 달성합니다
- Spark Connect와 1.5MB
pyspark-client는 노트북 및 CI를 클러스터 런타임으로부터 분리하여 배포를 단순화합니다 transformWithState는 복합 상태 타입과 TTL 만료를 제공하며 제한적이었던mapGroupsWithStateAPI를 대체합니다- Spark 선언적 파이프라인은 파이프라인 내부 의존성 관리를 처리하고, Airflow와 같은 오케스트레이터는 파이프라인 간 스케줄링을 보완합니다
- 면접 준비 시 ANSI 모드 마이그레이션, VARIANT 대 JSON 저장소, RTM 대 마이크로배치 트레이드오프, Spark Connect 아키텍처에 집중해야 합니다
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
태그
공유
관련 기사

2026년 데이터 엔지니어링 면접 질문 상위 25개
2026년 데이터 엔지니어링 면접에서 가장 많이 출제되는 25가지 핵심 질문과 실무 중심의 답변을 제공합니다.

2026 ETL vs ELT 완벽 비교: 데이터 파이프라인 아키텍처 설계 가이드
2026년 ETL과 ELT의 핵심 차이점, 비용 분석, 구현 패턴을 상세히 비교합니다. dbt, Airflow를 활용한 실전 데이터 파이프라인 설계 방법을 알아보세요.

Apache Spark와 Python으로 데이터 파이프라인 구축하기: 단계별 실전 가이드
PySpark을 활용한 실전 데이터 파이프라인 구축 튜토리얼입니다. DataFrame 연산, ETL 파이프라인 설계, Spark 4.0의 주요 기능을 프로덕션 수준의 코드 예제와 함께 설명합니다. 데이터 엔지니어 기술 면접 준비에도 도움이 됩니다.