- Authors
- Name
- 들어가며
- Feature Store가 필요한 이유
- Feature Store 아키텍처
- Feast 설치와 기본 구성
- 피처 정의와 엔티티 관리
- 실시간 피처 서빙 구현
- 배치 피처 파이프라인
- Feature Store 비교 (Feast vs Tecton vs Hopsworks)
- 피처 모니터링과 데이터 드리프트 감지
- 운영 시 주의사항과 트러블슈팅
- 실패 사례와 복구 절차
- 마치며
- 참고자료

들어가며
ML 모델의 성능은 데이터 품질에 달려 있다. 아무리 정교한 모델 아키텍처를 사용하더라도, 학습과 서빙에서 피처가 일관되지 않으면 프로덕션 성능은 오프라인 실험과 동떨어진다. 이 문제를 Training-Serving Skew라 부르며, Feature Store는 이 문제를 근본적으로 해결하는 핵심 인프라다.
이 글에서는 Feature Store의 아키텍처 설계 원칙부터 Feast를 활용한 실시간 피처 서빙 구현, 배치 파이프라인 구성, 주요 Feature Store 솔루션 비교, 데이터 드리프트 모니터링, 그리고 실전 운영에서 마주치는 장애 사례와 복구 절차까지 포괄적으로 다룬다.
이전 글에서 Feast의 기본 파이프라인 구축을 다뤘다면, 이번에는 실시간 서빙 최적화, 피처 버전 관리, 모니터링 전략 등 운영 관점에 초점을 맞춘다.
Feature Store가 필요한 이유
Training-Serving Skew 문제
ML 시스템에서 가장 흔하면서도 디버깅하기 어려운 문제가 Training-Serving Skew다. 학습 시에는 SQL로 배치 계산한 피처를 사용하고, 서빙 시에는 애플리케이션 코드로 실시간 계산하면 미묘한 차이가 발생한다.
# 학습 시 (오프라인) - SQL로 피처 계산
train_features = pd.read_sql("""
SELECT user_id,
AVG(purchase_amount) AS avg_purchase_30d,
COUNT(*) AS purchase_count_30d,
MAX(purchase_amount) AS max_purchase_30d
FROM transactions
WHERE event_timestamp BETWEEN '2026-01-01' AND '2026-02-01'
GROUP BY user_id
""", conn)
# 서빙 시 (온라인) - 애플리케이션 코드로 피처 계산
# 30일 윈도우 계산 로직이 미묘하게 다를 수 있음!
def get_user_features(user_id):
txns = redis_client.lrange(f"user_txns:{user_id}", 0, -1)
amounts = [float(t['amount']) for t in txns]
return {
'avg_purchase_30d': sum(amounts) / len(amounts),
'purchase_count_30d': len(amounts),
'max_purchase_30d': max(amounts)
}
위 코드에서 오프라인 SQL은 정확히 2026-01-01부터 2026-02-01까지의 데이터를 사용하지만, 온라인 코드는 Redis에 저장된 데이터의 범위에 의존한다. 윈도우 기준, NULL 처리, 타임존 변환 등에서 불일치가 발생하면 모델 정확도가 5~15% 하락하는 것은 드문 일이 아니다.
Feature Store가 해결하는 핵심 과제
Feature Store는 단일 피처 정의(Single Source of Truth) 원칙으로 다음 문제들을 해결한다.
- Training-Serving 일관성: 오프라인과 온라인에서 동일한 피처 정의를 사용
- 피처 재사용: 팀 간 피처 공유로 중복 개발 방지
- Point-in-Time 정확성: 시간 기준으로 정확한 피처 값을 조인하여 데이터 유출(data leakage) 방지
- 피처 디스커버리: 중앙 레지스트리에서 기존 피처 검색 및 활용
- 거버넌스: 피처 소유자, 데이터 계보(lineage), 접근 제어 관리
Feature Store 아키텍처
Feature Store는 크게 세 가지 핵심 컴포넌트로 구성된다.
1. 오프라인 스토어 (Offline Store)
대용량 히스토리컬 피처 데이터를 저장하며, 모델 학습과 배치 추론에 사용된다. 주로 데이터 레이크(S3, GCS)나 데이터 웨어하우스(BigQuery, Redshift, Snowflake)에 저장한다.
2. 온라인 스토어 (Online Store)
실시간 추론을 위한 저지연 피처 조회를 담당한다. 엔티티 키 기반으로 최신 피처 값을 millisecond 단위 지연으로 제공한다. Redis, DynamoDB, Bigtable 등이 사용된다.
3. 피처 레지스트리 (Feature Registry)
피처의 메타데이터를 관리하는 중앙 카탈로그다. 피처 이름, 데이터 타입, 소유자, 생성일, 데이터 소스 정보 등을 추적한다.
+-------------------+ +--------------------+ +------------------+
| Data Sources | | Feature Store | | Consumers |
| | | | | |
| - Kafka | ----> | Offline Store | ----> | Model Training |
| - Database | | (BigQuery/S3) | | (Batch) |
| - Stream | | | | |
| - Data Lake | ----> | Online Store | ----> | Model Serving |
| | | (Redis/DynamoDB) | | (Real-time) |
| | | | | |
| | | Feature Registry | ----> | Feature |
| | | (Metadata) | | Discovery |
+-------------------+ +--------------------+ +------------------+
Feast 설치와 기본 구성
설치
Feast는 pip로 간편하게 설치할 수 있다. 사용할 온라인/오프라인 스토어 백엔드에 따라 의존성을 선택한다.
# 기본 설치
pip install feast
# Redis 온라인 스토어 + PostgreSQL 오프라인 스토어
pip install 'feast[redis,postgres]'
# AWS 환경 (DynamoDB + Redshift)
pip install 'feast[aws]'
# GCP 환경 (Datastore + BigQuery)
pip install 'feast[gcp]'
프로젝트 초기화
# 프로젝트 생성
feast init my_feature_store
cd my_feature_store
# 프로젝트 구조 확인
tree .
# .
# |-- feature_store.yaml
# |-- features.py
# |-- __init__.py
feature_store.yaml 구성
project: ml_recommendation
provider: local
registry:
registry_type: sql
path: postgresql://feast:feast@localhost:5432/feast_registry
cache_ttl_seconds: 60
online_store:
type: redis
connection_string: 'localhost:6379,password=feast_secret'
redis_type: redis
offline_store:
type: postgres
host: localhost
port: 5432
database: feast_offline
db_schema: public
user: feast
password: feast_secret
entity_key_serialization_version: 2
이 설정은 PostgreSQL을 오프라인 스토어와 레지스트리로, Redis를 온라인 스토어로 사용하는 구성이다. 프로덕션 환경에서는 Redis Cluster 모드나 AWS ElastiCache를 권장한다.
피처 정의와 엔티티 관리
엔티티(Entity) 정의
엔티티는 피처가 연결되는 비즈니스 객체를 나타낸다. 사용자, 상품, 주문 등이 엔티티가 된다.
# features/entities.py
from feast import Entity, ValueType
# 사용자 엔티티
user = Entity(
name="user_id",
value_type=ValueType.INT64,
description="서비스 사용자 고유 식별자",
tags={"team": "recommendation", "owner": "ml-platform"},
)
# 상품 엔티티
item = Entity(
name="item_id",
value_type=ValueType.INT64,
description="상품 고유 식별자",
tags={"team": "recommendation", "owner": "ml-platform"},
)
피처 뷰(Feature View) 정의
피처 뷰는 특정 데이터 소스에서 파생되는 피처 그룹을 정의한다. 스키마, 엔티티, TTL(Time-to-Live) 등을 명시한다.
# features/user_features.py
from datetime import timedelta
from feast import FeatureView, Field
from feast.types import Float32, Int64, String
from feast.infra.offline_stores.contrib.postgres_offline_store.postgres_source import (
PostgreSQLSource,
)
# 데이터 소스 정의
user_activity_source = PostgreSQLSource(
name="user_activity_source",
query="""
SELECT
user_id,
avg_purchase_amount_30d,
purchase_count_30d,
max_purchase_amount_30d,
last_login_days_ago,
preferred_category,
event_timestamp,
created_timestamp
FROM user_activity_features
""",
timestamp_field="event_timestamp",
created_timestamp_column="created_timestamp",
)
# 피처 뷰 정의
user_activity_fv = FeatureView(
name="user_activity_features",
entities=[user],
ttl=timedelta(days=1), # 온라인 스토어에서 1일 이상 된 피처는 stale 처리
schema=[
Field(name="avg_purchase_amount_30d", dtype=Float32),
Field(name="purchase_count_30d", dtype=Int64),
Field(name="max_purchase_amount_30d", dtype=Float32),
Field(name="last_login_days_ago", dtype=Int64),
Field(name="preferred_category", dtype=String),
],
source=user_activity_source,
online=True, # 온라인 스토어에 동기화
tags={"team": "recommendation", "version": "v2"},
)
피처 서비스(Feature Service) 정의
여러 피처 뷰를 묶어 모델별로 필요한 피처 세트를 구성한다.
# features/feature_services.py
from feast import FeatureService
# 추천 모델용 피처 서비스
recommendation_feature_service = FeatureService(
name="recommendation_v2",
features=[
user_activity_fv[["avg_purchase_amount_30d", "purchase_count_30d"]],
item_popularity_fv[["view_count_7d", "purchase_rate_7d"]],
user_item_interaction_fv,
],
tags={"model": "recommendation", "version": "v2"},
description="추천 모델 v2에서 사용하는 피처 서비스",
)
피처 레지스트리 반영
# 피처 정의를 레지스트리에 반영
feast apply
# 등록된 피처 확인
feast feature-views list
feast feature-services list
feast entities list
실시간 피처 서빙 구현
온라인 스토어 Materialization
오프라인 스토어의 최신 피처 값을 온라인 스토어로 동기화하는 과정을 Materialization이라 한다.
# 특정 기간의 피처를 온라인 스토어로 동기화
feast materialize 2026-03-01T00:00:00 2026-03-14T00:00:00
# 마지막 동기화 시점부터 현재까지 증분 동기화
feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S")
Python SDK를 이용한 실시간 피처 조회
# serving/realtime_serving.py
from feast import FeatureStore
import pandas as pd
# Feature Store 초기화
store = FeatureStore(repo_path="./my_feature_store")
# 단일 엔티티 온라인 피처 조회
entity_rows = [
{"user_id": 1001},
{"user_id": 1002},
{"user_id": 1003},
]
# Feature Service를 통한 조회 (권장)
online_features = store.get_online_features(
features=store.get_feature_service("recommendation_v2"),
entity_rows=entity_rows,
).to_dict()
print(online_features)
# 출력 예시:
# - user_id: [1001, 1002, 1003]
# - avg_purchase_amount_30d: [45000.0, 12000.0, 78000.0]
# - purchase_count_30d: [15, 3, 42]
# - view_count_7d: [120, 45, 230]
# - purchase_rate_7d: [0.12, 0.05, 0.18]
gRPC/HTTP Feature Server 구동
Feast는 Go 기반의 고성능 피처 서버를 제공한다.
# HTTP Feature Server 구동 (기본 포트 6566)
feast serve --host 0.0.0.0 --port 6566
# gRPC Feature Server 구동
feast serve --type grpc --port 6567
# HTTP API를 통한 피처 조회
import requests
response = requests.post(
"http://localhost:6566/get-online-features",
json={
"feature_service": "recommendation_v2",
"entities": {
"user_id": [1001, 1002]
},
},
)
features = response.json()
print(features)
서빙 성능 최적화
Redis 온라인 스토어 사용 시 p99 지연 시간을 10ms 이내로 유지하기 위한 핵심 전략이다.
- Connection Pooling: Redis 연결을 풀로 관리하여 연결 오버헤드 감소
- Batch 조회: 여러 엔티티 키를 한 번에 조회 (MGET 활용)
- 피처 압축: 피처 값 직렬화 시 Protocol Buffers 사용
- 캐시 TTL 최적화: 피처 특성에 따라 TTL을 차등 설정
배치 피처 파이프라인
Point-in-Time Join을 통한 학습 데이터 생성
# pipeline/training_data.py
from feast import FeatureStore
import pandas as pd
store = FeatureStore(repo_path="./my_feature_store")
# 학습 데이터의 엔티티와 타임스탬프
entity_df = pd.DataFrame({
"user_id": [1001, 1002, 1003, 1001, 1002],
"item_id": [501, 502, 503, 504, 505],
"event_timestamp": pd.to_datetime([
"2026-03-01 10:00:00",
"2026-03-02 14:30:00",
"2026-03-03 09:15:00",
"2026-03-05 16:45:00",
"2026-03-07 11:20:00",
]),
"label": [1, 0, 1, 1, 0],
})
# Point-in-Time Join으로 학습 데이터 생성
# 각 이벤트 시점에서 유효한 피처 값을 정확히 조인
training_df = store.get_historical_features(
entity_df=entity_df,
features=store.get_feature_service("recommendation_v2"),
).to_df()
print(f"학습 데이터 shape: {training_df.shape}")
print(training_df.head())
# 학습 데이터 저장
training_df.to_parquet("training_data_v2.parquet", index=False)
Point-in-Time Join은 각 이벤트 시점에서 미래 데이터가 포함되지 않도록 보장한다. 예를 들어, 2026-03-01 10:00에 발생한 이벤트에는 2026-03-01 10:00 이전에 계산된 피처 값만 조인된다. 이것이 데이터 유출을 방지하는 핵심 메커니즘이다.
Airflow를 통한 Materialization 자동화
# dags/feast_materialization_dag.py
from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.operators.python import PythonOperator
from airflow.utils.dates import days_ago
from datetime import datetime, timedelta
default_args = {
"owner": "ml-platform",
"depends_on_past": False,
"email_on_failure": True,
"email": ["ml-platform@company.com"],
"retries": 3,
"retry_delay": timedelta(minutes=5),
}
dag = DAG(
dag_id="feast_daily_materialization",
default_args=default_args,
description="Feast 일일 Materialization 파이프라인",
schedule_interval="0 2 * * *", # 매일 새벽 2시
start_date=days_ago(1),
catchup=False,
tags=["feast", "feature-store", "materialization"],
)
# 증분 Materialization
materialize_task = BashOperator(
task_id="materialize_incremental",
bash_command=(
"cd /opt/feast/my_feature_store && "
"feast materialize-incremental $(date -u +%Y-%m-%dT%H:%M:%S)"
),
dag=dag,
)
# 온라인 스토어 헬스 체크
def check_online_store_health():
from feast import FeatureStore
store = FeatureStore(repo_path="/opt/feast/my_feature_store")
# 샘플 엔티티로 조회 테스트
result = store.get_online_features(
features=["user_activity_features:avg_purchase_amount_30d"],
entity_rows=[{"user_id": 1}],
).to_dict()
assert result is not None, "온라인 스토어 조회 실패"
print("온라인 스토어 헬스 체크 통과")
health_check = PythonOperator(
task_id="online_store_health_check",
python_callable=check_online_store_health,
dag=dag,
)
materialize_task >> health_check
Feature Store 비교 (Feast vs Tecton vs Hopsworks)
주요 Feature Store 솔루션을 비교하면 다음과 같다. 각 솔루션은 설계 철학과 타깃 사용자가 다르므로, 조직의 규모와 요구사항에 맞는 선택이 중요하다.
| 항목 | Feast | Tecton | Hopsworks | Vertex AI Feature Store |
|---|---|---|---|---|
| 라이선스 | Apache 2.0 (오픈소스) | 상용 (SaaS) | AGPL v3 / 상용 | Google Cloud 관리형 |
| 배포 방식 | 셀프 호스팅 | 완전 관리형 SaaS | 셀프 호스팅 / 관리형 | GCP 관리형 |
| 온라인 스토어 | Redis, DynamoDB, PostgreSQL 등 | 내장 (DynamoDB 기반) | RonDB (sub-ms 지연) | Bigtable, BigQuery |
| 오프라인 스토어 | BigQuery, Redshift, Snowflake, PostgreSQL 등 | Spark/Databricks 기반 | Apache Hudi on HopsFS | BigQuery |
| 실시간 변환 | 미지원 (외부 파이프라인 필요) | 내장 지원 (스트리밍 변환) | 내장 지원 | 제한적 |
| 피처 변환 | Push 기반 (외부 계산 후 저장) | 내장 변환 엔진 (배치/스트리밍/실시간) | Spark/Flink 기반 변환 | BigQuery SQL 기반 |
| 스트리밍 소스 | Kafka, Kinesis (Push 방식) | Kafka, Kinesis (내장 처리) | Kafka, Spark Streaming | Pub/Sub |
| 피처 모니터링 | 커뮤니티 플러그인 | 내장 드리프트 감지 | 내장 통계/검증 | Vertex AI 모니터링 통합 |
| p99 지연 (온라인) | 5~15ms (Redis 기준) | 5~10ms | 1~5ms (RonDB) | 10~50ms |
| 비용 | 인프라 비용만 | 처리량 기반 과금 | 클러스터 비용 | GCP 리소스 과금 |
| 학습 곡선 | 보통 | 낮음 (관리형) | 높음 | 보통 (GCP 경험 필요) |
| 적합 조직 | 중소규모, 커스텀 필요 | 대규모, 실시간 중심 | 대규모, 온프레미스 | GCP 중심 조직 |
선택 가이드
- Feast: 오픈소스 우선, 기존 인프라 활용, 커스텀 구성이 필요한 팀에 적합하다. 피처 변환은 외부에서 처리하고 Feast는 저장/서빙에 집중하는 구조다.
- Tecton: 엔터프라이즈급 실시간 피처 엔지니어링이 필요하고, 관리형 서비스를 선호하는 조직에 적합하다. 변환 파이프라인을 내장하여 엔드투엔드 관리가 가능하다.
- Hopsworks: 온프레미스 배포가 필요하거나, sub-millisecond 지연이 필수인 환경에 적합하다. RonDB 기반 온라인 스토어의 성능이 탁월하다.
- Vertex AI Feature Store: GCP를 주 클라우드로 사용하는 조직에서 BigQuery 생태계와 자연스럽게 통합할 때 적합하다.
피처 모니터링과 데이터 드리프트 감지
프로덕션 환경에서 피처의 분포가 변하면 모델 성능이 하락한다. 체계적인 모니터링이 필수다.
Evidently AI를 활용한 드리프트 감지
# monitoring/drift_detection.py
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset, DataQualityPreset
from evidently.metrics import (
DataDriftTable,
DatasetDriftMetric,
ColumnDriftMetric,
)
from feast import FeatureStore
import pandas as pd
from datetime import datetime, timedelta
def detect_feature_drift(
store: FeatureStore,
feature_service_name: str,
reference_start: str,
reference_end: str,
current_start: str,
current_end: str,
drift_threshold: float = 0.25,
):
"""피처 드리프트를 감지하고 리포트를 생성한다."""
# 기준 데이터 (학습 시점)
reference_entity_df = pd.read_sql(
f"""
SELECT user_id, event_timestamp
FROM user_events
WHERE event_timestamp BETWEEN '{reference_start}' AND '{reference_end}'
LIMIT 10000
""",
conn,
)
# 현재 데이터 (서빙 시점)
current_entity_df = pd.read_sql(
f"""
SELECT user_id, event_timestamp
FROM user_events
WHERE event_timestamp BETWEEN '{current_start}' AND '{current_end}'
LIMIT 10000
""",
conn,
)
feature_service = store.get_feature_service(feature_service_name)
reference_df = store.get_historical_features(
entity_df=reference_entity_df,
features=feature_service,
).to_df()
current_df = store.get_historical_features(
entity_df=current_entity_df,
features=feature_service,
).to_df()
# Evidently 드리프트 리포트 생성
report = Report(metrics=[
DatasetDriftMetric(),
DataDriftTable(),
])
report.run(
reference_data=reference_df,
current_data=current_df,
)
# 리포트 저장
report.save_html("drift_report.html")
# 드리프트 결과 확인
result = report.as_dict()
dataset_drift = result["metrics"][0]["result"]["dataset_drift"]
if dataset_drift:
print("WARNING: 데이터 드리프트가 감지되었습니다!")
# 알림 전송 로직
send_alert(
channel="ml-alerts",
message=f"Feature drift detected in {feature_service_name}",
)
return report
# 사용 예시
store = FeatureStore(repo_path="./my_feature_store")
report = detect_feature_drift(
store=store,
feature_service_name="recommendation_v2",
reference_start="2026-02-01",
reference_end="2026-02-28",
current_start="2026-03-01",
current_end="2026-03-14",
)
피처 품질 모니터링 지표
프로덕션 피처의 건강 상태를 추적하기 위해 다음 지표들을 모니터링해야 한다.
| 지표 | 설명 | 임계값 예시 |
|---|---|---|
| Null Rate | 피처 값이 NULL인 비율 | 5% 초과 시 경고 |
| PSI (Population Stability Index) | 분포 안정성 지수 | 0.1 이상 시 주의, 0.25 이상 시 경고 |
| 피처 Freshness | 최신 피처 업데이트 시점 | TTL 초과 시 경고 |
| 서빙 지연 (p99) | 온라인 피처 조회 지연 | 50ms 초과 시 경고 |
| 피처 값 범위 | 기대 범위 이탈 여부 | 학습 시 분포의 3 시그마 초과 시 경고 |
Prometheus/Grafana 기반 서빙 지연 모니터링
# prometheus/feast_alerts.yaml
groups:
- name: feast_feature_store
rules:
- alert: FeatureServingHighLatency
expr: histogram_quantile(0.99, rate(feast_serving_request_latency_seconds_bucket[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: 'Feast 피처 서빙 p99 지연 50ms 초과'
description: '피처 서빙 p99 지연이 5분 이상 50ms를 초과했습니다.'
- alert: FeatureServingErrors
expr: rate(feast_serving_request_errors_total[5m]) > 0.01
for: 3m
labels:
severity: critical
annotations:
summary: 'Feast 피처 서빙 에러율 1% 초과'
description: '피처 서빙 에러율이 3분 이상 1%를 초과했습니다.'
- alert: FeatureFreshnessStale
expr: feast_feature_freshness_seconds > 86400
for: 10m
labels:
severity: warning
annotations:
summary: '피처 Freshness 24시간 초과'
description: '피처가 24시간 이상 업데이트되지 않았습니다. Materialization 파이프라인을 확인하세요.'
운영 시 주의사항과 트러블슈팅
1. Materialization 성능 최적화
Materialization은 오프라인 스토어에서 온라인 스토어로 데이터를 복사하는 과정이므로, 데이터 양이 많으면 시간이 오래 걸린다.
- 병렬 Materialization: 피처 뷰별로 병렬 처리하여 총 소요 시간을 단축한다
- 증분 Materialization: 전체 데이터 대신 마지막 동기화 이후 변경분만 처리한다
- 파티셔닝 활용: 오프라인 스토어의 파티셔닝을 활용하여 스캔 범위를 제한한다
2. Redis 온라인 스토어 운영
Redis를 온라인 스토어로 사용할 때 주의점이다.
- 메모리 관리: 피처 수와 엔티티 수에 따른 메모리 사용량을 사전에 계산한다. 엔티티 100만 개, 피처 50개 기준 약 2~5GB가 필요하다
- TTL 설정: 피처의 비즈니스 유효 기간에 맞는 TTL을 설정한다. 너무 짧으면 cache miss, 너무 길면 stale 데이터 서빙
- Persistence 설정: RDB + AOF 조합으로 데이터 손실을 최소화한다
- 복제(Replication): 읽기 부하 분산을 위해 Redis Replica를 구성한다
3. 피처 버전 관리
피처 정의가 변경되면 모델 호환성에 영향을 준다. 다음 전략을 권장한다.
- 피처 뷰 버전닝: 피처 정의 변경 시 새로운 피처 뷰를 생성한다 (예: user_features_v2)
- Feature Service 버전닝: 모델 버전에 맞는 Feature Service를 별도로 관리한다
- 하위 호환성 유지: 기존 피처 삭제 대신 deprecated 태그를 추가하고, 의존 모델이 모두 마이그레이션된 후 삭제한다
4. 보안 고려사항
- 네트워크 격리: Redis 온라인 스토어를 VPC 내부에 배치한다
- 인증/암호화: Redis AUTH, TLS 암호화를 적용한다
- 접근 제어: 피처 서비스별 API 키를 발급하고, 팀별 접근 권한을 분리한다
- PII 마스킹: 개인정보가 포함된 피처는 해싱 또는 마스킹 처리 후 저장한다
실패 사례와 복구 절차
사례 1: Redis OOM(Out of Memory)으로 인한 서빙 중단
증상: 피처 서빙 요청이 전부 에러를 반환하고, Redis 로그에 OOM 에러가 기록된다.
원인: Materialization 시 예상보다 많은 엔티티가 동기화되면서 Redis 메모리 한도를 초과했다.
복구 절차:
- Redis maxmemory 설정을 임시로 증가 (또는 노드 스케일업)
- 불필요한 피처 뷰의 온라인 데이터를 삭제
- TTL이 만료된 키를 강제 정리
- 장기적으로는 피처 뷰별 엔티티 수 제한 정책 수립
# Redis 메모리 상태 확인
redis-cli INFO memory
# 특정 피처 뷰의 키 수 확인
redis-cli DBSIZE
# maxmemory 임시 증가
redis-cli CONFIG SET maxmemory "8gb"
사례 2: Materialization 파이프라인 실패로 인한 Stale 피처
증상: 모델 예측 정확도가 급격히 하락한다. 온라인 피처 값이 수일 전 데이터에 머물러 있다.
원인: Airflow DAG에서 Materialization 태스크가 오프라인 스토어 연결 에러로 3일간 실패했으나 알림이 누락되었다.
복구 절차:
- 오프라인 스토어 연결 문제를 해결한다
- 누락된 기간에 대해 수동 Materialization을 실행한다
- 피처 Freshness 모니터링 알림을 추가한다
# 누락 기간 수동 Materialization
feast materialize 2026-03-11T00:00:00 2026-03-14T00:00:00
# Materialization 상태 확인
feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S") --verbose
사례 3: 피처 스키마 변경으로 인한 모델 서빙 오류
증상: 모델 추론 API에서 피처 타입 불일치 에러가 발생한다.
원인: 피처 뷰의 스키마를 변경(Float32에서 Float64로)하고 feast apply를 실행했으나, 구버전 모델이 여전히 이전 스키마를 기대하고 있었다.
복구 절차:
- 스키마 변경을 롤백하거나, 새로운 피처 뷰(user_features_v3)를 생성한다
- 구버전 모델이 사용하는 Feature Service는 기존 피처 뷰를 유지한다
- 신규 모델 배포 시 새 Feature Service로 전환한다
- 모든 모델이 마이그레이션되면 구버전 피처 뷰를 deprecated 처리한다
사례 4: Point-in-Time Join에서 데이터 유출
증상: 오프라인 평가에서는 AUC 0.95인데, 온라인 A/B 테스트에서는 AUC 0.78로 급락한다.
원인: 피처 계산 시 event_timestamp를 잘못 설정하여 미래 데이터가 학습에 포함되었다. 예를 들어, 일별로 집계된 피처의 타임스탬프를 해당 일자의 시작(00:00:00)이 아닌 종료(23:59:59)로 설정하면, 그날 발생한 이벤트에 그날의 전체 집계가 조인될 수 있다.
복구 절차:
- 피처 소스의 event_timestamp를 검증한다 (해당 피처가 사용 가능해지는 시점으로 설정)
- created_timestamp 컬럼을 활용하여 이중 검증한다
- 학습 데이터를 재생성하고 모델을 재학습한다
마치며
Feature Store는 ML 시스템의 데이터 계층을 체계화하는 핵심 인프라다. 단순히 피처를 저장하고 서빙하는 것을 넘어, Training-Serving 일관성 보장, 피처 재사용, 데이터 유출 방지, 팀 간 협업 등 ML 시스템의 근본적인 문제들을 해결한다.
Feast는 오픈소스의 유연성과 다양한 백엔드 지원으로 중소규모 팀부터 대규모 조직까지 폭넓게 활용할 수 있다. 다만 실시간 변환이 필요한 경우 Tecton이나 Hopsworks를 검토하는 것이 좋다.
운영 관점에서 가장 중요한 것은 세 가지다.
- Materialization 파이프라인의 안정성: 자동화와 모니터링을 철저히 구성한다
- 피처 드리프트 감지: 정기적으로 피처 분포를 검증하고 임계값 기반 알림을 설정한다
- 피처 버전 관리: 피처 스키마 변경이 기존 모델에 영향을 주지 않도록 버전닝 전략을 수립한다
Feature Store를 도입할 때는 처음부터 완벽한 아키텍처를 목표로 하지 말고, 핵심 사용 사례(예: 추천 모델의 실시간 서빙)부터 시작하여 점진적으로 확장하는 것을 권장한다.