본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 21. 00:29

피처 스토어(Feature Store): 일관성과 지연 시간(Latency)은 모두 타협할 수 없는 요소입니다

요약

피처 스토어는 ML 파이프라인에서 피처 생성과 모델 소비 사이를 연결하는 운영 중심지로, 저장, 서빙, 레지스트리, 일관성 강제의 네 가지 핵심 책임을 가집니다. 특히 대규모 환경에서 일관성과 낮은 지연 시간을 동시에 확보하기 위해 온라인 스토어와 오프라인 스토어를 분리하는 이중 스토어 아키텍처가 필수적입니다.

핵심 포인트

  • 피처 스토어의 4대 핵심 책임: 저장(Storage), 서빙(Serving), 레지스트리(Registry), 일관성 강제(Consistency enforcement)
  • 설계의 핵심 과제: 대규모 환경에서의 데이터 일관성 유지와 낮은 지연 시간(Low latency) 확보 사이의 균형
  • 이중 스토어 아키텍처: 추론을 위한 저지연 온라인 스토어와 학습을 위한 배치 읽기용 오프라인 스토어를 분리하여 최적화
  • 온라인 스토어의 요구사항: 추론 시 수십 개의 피처를 5~20ms 내에 가져올 수 있는 O(1) 키-값 액세스 패턴 필요

시리즈의 5부작 중 3부: AI 파이프라인이 성장할 때. 이전 포스트에서는 원시 이벤트(raw events)에서 계산된 상태(computed state)로 피처(features)를 가져오는 파이프라인 아키텍처(pipeline architecture)를 살펴보았습니다. 이제 피처가 계산된 후 어디에 머무는지, 그리고 추론(inference) 시점에 저장소에서 모델로 어떻게 전달되는지에 대해 이야기해야 합니다. 그것이 바로 피처 스토어(feature store)의 역할입니다. 피처 스토어는 실시간 ML(머신러닝) 시스템의 운영 중심지입니다. 피처를 생성하는 파이프라인과 이를 소비하는 모델 사이에 위치합니다. 이를 제대로 구축하면 여러분이 만들 모든 모델의 토대가 됩니다. 잘못 구축하면 초기 설계 결정에서 비롯된 문제들을 해결하기 위해 수년간 소방수 역할을 하며 시간을 허비하게 될 것입니다. 피처 스토어 설계의 핵심적인 긴장 관계는 다음과 같습니다: 대규모 환경에서 일관성(consistency)과 낮은 지연 시간(low latency)을 동시에 확보해야 한다는 점입니다. 이 두 목표는 서로 다른 방향으로 작용합니다. 왜 그런지, 그리고 어떤 아키텍처 패턴(architectural patterns)이 이 긴장 관계를 해결하는지에 대해 이 포스트에서 다루고자 합니다.

피처 스토어가 실제로 하는 일
설계에 들어가기에 앞서, 피처 스토어라는 용어가 느슨하게 사용되는 경우가 많으므로 피처 스토어가 무엇을 책임지는지 정확히 정의할 가치가 있습니다. 피처 스토어는 네 가지 핵심 책임을 가집니다:

저장(Storage): 모델 학습(대규모 과거 윈도우에 대한 배치 읽기(batch reads))과 모델 추론(밀리초 미만의 지연 시간 요구 사항을 가진 현재 값의 포인트 읽기(point reads)) 모두를 위해 효율적으로 검색할 수 있는 형태로 피처 값을 영구 저장합니다.

서빙(Serving): 추론 시점에 모델에 피처 값을 전달합니다. 여기에는 특정 엔티티(entity)에 대한 피처를 가져오고, 결측치(missing values)를 처리하며, 잠재적으로 많은 피처 그룹(feature groups)으로부터 완전한 피처 벡터(feature vector)를 조립하는 작업이 포함됩니다.

레지스트리(Registry): 어떤 피처가 존재하는지, 어떻게 정의되었는지, 소유자가 누구인지, 그리고 현재 어떤 버전이 프로덕션(production) 환경에 있는지에 대한 카탈로그를 유지합니다. 이것이 거버넌스(governance) 계층입니다.

일관성 강제(Consistency enforcement): 모델 학습에 사용된 피처가 추론 시점에 서빙되는 피처와 동일한 방식으로 계산되도록 보장합니다.

이 지점이 바로 대부분의 피처 스토어(Feature Store) 구현에서 격차가 발생하는 부분입니다. 스스로를 피처 스토어라고 부르면서 이러한 책임 중 한두 가지만 해결하는 시스템은 숨겨진 리스크를 생성합니다. 이러한 격차는 데모에서는 나타나지 않습니다. 프로덕션(Production) 환경에서 나타납니다.

이중 스토어 아키텍처 (The Dual-Store Architecture)
프로덕션용 피처 스토어를 위한 근본적인 디자인 패턴은 이중 스토어 아키텍처(Dual-store architecture)입니다. 이는 저장소를 두 개의 별도 레이어로 분리하며, 각 레이어는 서로 다른 액세스 패턴(Access pattern)에 최적화됩니다.

온라인 스토어(Online store)는 추론(Inference)을 위해 서비스됩니다. 모델이 추론해야 하는 모든 엔티티(Entity)—사용자, 제품, 계정, 트랜잭션 등—에 대한 현재 피처 값을 보유합니다. 읽기(Read) 작업은 매우 빨라야 합니다. 저지연(Low-latency) 서빙 경로에서 피처 검색(Feature retrieval) 단계는 수십 개의 피처 값을 동시에 가져오는 데 대개 5~20ms의 예산(Budget)만을 할당받습니다. 이를 위해서는 O(1) 키-값(Key-value) 액세스 패턴을 가진 인메모리(In-memory) 또는 SSD 기반 저장소가 필요합니다.

오프라인 스토어(Offline store)는 학습(Training)과 분석(Analysis)을 위해 서비스됩니다. 엔티티와 시간별로 쿼리할 수 있는 피처 값의 전체 이력을 보유합니다. 읽기 속도는 초에서 분 단위로 더 느리지만, 저장 비용은 온라인 스토어보다 훨씬 저렴합니다. 객체 스토리지(Object storage) 상의 Parquet와 같은 컬럼형(Columnar) 포맷이나 목적에 맞게 구축된 분석용 데이터베이스(Analytical databases)가 이곳의 전형적인 선택지입니다.

쓰기 경로(Write path)는 두 스토어를 동기화 상태로 유지합니다. 배치 작업(Batch job)이나 스트리밍 파이프라인(Streaming pipeline)에 의해 새로운 피처 값이 계산되면, 두 스토어 모두에 기록됩니다. 온라인 스토어에는 현재 값이 저장되고, 오프라인 스토어에는 이력 기록에 추가(Append)됩니다.

flowchart TD FP[
Feature Pipeline
(배치 또는 스트리밍)] WP[
쓰기 경로
(동기화 계층)] OS[
온라인 스토어
낮은 지연 시간 · 현재 값
Key-value / 인메모리] OF[
오프라인 스토어
전체 이력 · 배치 읽기
컬럼 기반 / 오브젝트 스토리지] MI[
모델 추론
(실시간 서비스)] MT[
모델 학습
(이력 데이터셋)] FP --> WP WP --> OS WP --> OF OS --> MI OF --> MT style OS fill:#d4edda,stroke:#28a745,color:#000 style OF fill:#d1ecf1,stroke:#17a2b8,color:#000 style WP fill:#fff3cd,stroke:#ffc107,color:#000 이 패턴은 잘 확립되어 있으며 광범위하게 구현되고 있습니다. 각 스토어에 어떤 기술을 사용할지, 쓰기 경로를 어떻게 처리할지, 어떻게 동기화할지 등 실행 세부 사항은 상당히 다양하며 매우 중요합니다. 하지만 개념적인 분리는 거의 모든 프로덕션 ML 시스템의 올바른 출발점입니다.

일관성 문제 (Consistency Problem)와 왜 그것이 보이는 것보다 어려운가

dual-store 아키텍처는 과소평가하기 쉬운 일관성 문제를 도입합니다. 즉, 온라인 스토어와 오프라인 스토어가 피처(feature)가 어떻게 정의되어야 하는지에 대해 합의해야 한다는 것입니다. 만약 어떤 피처가 오프라인 스토어에 쓰는 배치 파이프라인과 온라인 스토어에 쓰는 스트리밍 파이프라인에서 다르게 계산된다면, 모델은 프로덕션에서 보는 데이터와 일치하지 않는 데이터로 학습됩니다. 우리는 이전 게시물에서 이를 트레이닝-서빙 스큐(training-serving skew)라고 언급했습니다. 여기서는 구조적인 원인을 살펴봅니다.

가장 흔한 불일치 원인은 **변환 로직의 중복(transformation logic duplication)**입니다. '사용자가 지난 7일 동안 구매한 횟수'로 정의된 피처를 생각해 봅시다. 배치 파이프라인은 이를 이력 테이블에 대한 SQL 집계(aggregation)로 계산합니다. 스트리밍 파이프라인은 인메모리에서 순환 카운트(rolling count)를 유지하여 계산합니다. 둘 다 같은 이름의 숫자를 생성하지만, 시간대 경계(timezone boundaries), null 트랜잭션, 취소된 주문 또는 이벤트 데이터의 엣지 케이스 처리 방식에 조금이라도 차이가 있다면—그리고 거의 항상 그렇습니다—값은 분기하게 됩니다.

배치(Batch)로 계산된 값으로 학습된 모델은 추론(Inference) 시점에 오프라인 지표(Offline metrics)가 예측했던 것과는 다르게 동작할 것입니다. '코드로서의 피처 정의(Feature definitions as code)'는 이에 대한 아키텍처적 대응입니다. 배치(Batch) 시스템과 스트리밍(Streaming) 시스템에 변환 로직(Transformation logic)을 각각 별도로 구현하는 대신, 피처를 버전이 지정된 이름 있는 연산(Versioned, named computation)으로 한 번만 정의하면 공유된 변환 계층(Shared transformation layer)이 두 컨텍스트 모두에서 해당 정의를 실행합니다.

한 번만 정의

@feature ( name = "user_purchases_7d", version = 1 )
def user_purchases_7d ( events : EventStream, window : Window ) -> int :
return events.filter(type = "purchase", status = "completed").window(days = 7).count()

피처 스토어는 이 정의를 다음 두 곳 모두에서 실행합니다:

- 배치 컨텍스트 (오프라인 스토어 / 학습 데이터용)

- 스트리밍 컨텍스트 (온라인 스토어 / 추론용)

구현 방식은 프레임워크마다 다르지만, 원칙은 일관됩니다. 즉, 정의(Definition)가 진실의 원천(Source of truth)이며, 이를 실행하는 파이프라인 코드가 아닙니다. 정의가 변경되면 두 경로 모두 함께 업데이트됩니다.

액세스 패턴 설계: 실제 추론은 어떻게 이루어지는가
피처 스토어 설계에서 가장 중대한 결정 중 하나는 명시적인 주의를 거의 받지 못하는 부분입니다. 바로 모델이 서빙(Serving) 시점에 실제로 피처를 어떻게 검색(Retrieve)하느냐 하는 점입니다. 순진한 가정은 추론 시의 검색이 데이터베이스 조회(Database lookup)와 같을 것이라고 생각하는 것입니다. 즉, "사용자 12345에 대한 피처를 가져와라"와 같은 방식 말입니다. 그것은 부분적으로는 맞지만, 실제 상황은 훨씬 더 까다롭습니다.

단일 추론 (Inference) 요청은 일반적으로 다음과 같은 요소를 필요로 합니다:

  • 여러 피처 그룹 (사용자 피처, 아이템 피처, 컨텍스트 피처, 교차 피처 (Cross features))으로부터의 피처
  • 동시에 해결되어야 하는 다수의 엔티티 (요청 중인 사용자, 점수가 매겨지는 아이템, 그리고 사용자-아이템 상호작용 이력)
  • 엄격한 지연 시간 (Latency) 예산 내에 도착해야 하는 값들. 왜냐하면 피처 검색 (Feature retrieval)은 모델 실행, 전/후처리 (Pre/post-processing), 네트워크 오버헤드를 포함하는 더 큰 서빙 파이프라인 (Serving pipeline)의 한 단계이기 때문입니다.

이는 추론을 위한 피처 검색이 개별적인 읽기 (Read)의 연속이라기보다는, 거의 항상 배치 포인트 룩업 (Batch point lookup) — 즉, 단일 작업 내에서 여러 엔티티에 대한 많은 피처 값을 가져오는 방식 — 임을 의미합니다. 이 차이는 성능 면에서 엄청나게 중요합니다. 피처 벡터를 구성하기 위해 N번의 별도 읽기를 실행하는 피처 스토어는, 해당 읽기들을 단일 라운드 트립 (Round-trip)으로 배치하는 스토어보다 N배 더 느릴 것입니다. P99 지연 시간 예산이 20ms일 때, 한 번의 네트워크 라운드 트립과 다섯 번의 차이는 SLA (Service Level Agreement)를 준수하는 시스템과 그렇지 못한 시스템의 차이입니다. 피처 스토어의 서빙 API를 설계하고 온라인 스토어 기술을 선택할 때는 구현하기 가장 쉬운 패턴이 아니라, 이러한 액세스 패턴 (Access pattern)을 중심으로 해야 합니다.

스키마 버전 관리 (Schema Versioning) 및 거버넌스 (Governance)
피처는 변합니다. 지난 분기에 한 방식으로 정의되었던 피처가 이번 분기에 재정의되어야 할 수도 있습니다. 새로운 데이터 소스가 사용 가능해지거나, 변환 로직 (Transformation logic)에서 버그가 발견되거나, 비즈니스 정의가 변경될 수 있기 때문입니다. 운영 시스템을 중단시키지 않으면서 이러한 변경을 관리하는 것이 피처 스토어의 스키마 거버넌스 문제입니다. 명시적인 거버넌스가 없을 때 발생하는 실패 모드는 조용히 진행됩니다. 피처의 정의가 바뀌고, 새 버전이 파이프라인에 배포되며, 온라인 스토어가 새로운 값을 제공하기 시작하면 — 이전 정의로 학습된 모델은 이제 자신의 학습 분포 (Training distribution)와 일치하지 않는 입력을 받게 됩니다. 에러는 발생하지 않습니다. 예측 품질이 저하될 뿐입니다. 디버깅 비용은 매우 비싸집니다.

버전 관리 기능이 있는 피처 레지스트리(Feature Registry)는 피처 정의에 대한 각 변경 사항을 명시적이고 추적 가능하게 만듦으로써 이 문제를 해결합니다:

feature : user_purchases_7d
version : 1 → "완료된(completed)" 구매만 포함, UTC 시간대
version : 2 → "환불된(refunded)" 상태 제외 추가, 사용자의 로컬 시간대
version : 3 → 윈도우(window)를 달력 주(calendar week) 대신 이동 7일(rolling 7 days)로 변경

모델은 특정 피처 버전(feature version)에 고정(pinned)됩니다. 새로운 모델 버전은 새로운 피처 버전에 맞춰 학습될 수 있는 반면, 기존 모델은 프로덕션(production) 환경에서 기존 버전을 대상으로 계속 실행될 수 있습니다. 롤백(Rollback) 또한 깔끔하고 감사(auditable)가 가능합니다. 또한 레지스트리는 발견 가능성(discoverability)을 제공합니다. 사용자 참여(user engagement) 피처를 찾는 데이터 사이언티스트는 동일한 것을 다르게 계산하는 새로운 파이프라인을 구축하는 대신 레지스트리를 검색할 수 있습니다. 이것이 우리가 이전 포스트에서 논의했던 조직적 레버리지(organizational leverage) 지점입니다. 재사용(reuse)은 피처가 가시적이고 문서화가 잘 되어 있을 때만 발생합니다. 최소한의 실행 가능한 거버넌스(Minimum viable governance)에는 다음이 포함됩니다: 피처 이름, 버전, 소유자, 설명, 변환 정의(transformation definition), 출력 스키마(schema of outputs), 그리고 현재 각 버전을 소비하고 있는 모델들. 이러한 인프라에 조기에 투자하는 팀은 나중에 상당한 운영 비용을 절감할 수 있습니다.

콜드 스타트(Cold Start): 예외 상황이 아닌 필수 고려 사항
모든 피처 스토어(feature store)는 콜드 스타트(cold start) 문제에 직면합니다. 즉, 시스템에 이력이 없는 새로운 엔티티(entity)에 대해 모델이 어떤 피처 값(feature values)을 받게 될 것인가의 문제입니다. 이는 종종 예외 상황(edge case)으로 취급되어 서빙(serving) 시점에 null 값, 0, 또는 전역 평균(global average)을 임퓨테이션(imputation, 결측치 대체)하는 방식으로 성급하게 처리되곤 합니다. 실제로 콜드 스타트는 예외 상황이 아닙니다. 모든 사용자의 첫 세션은 콜드 스타트입니다. 모든 새로운 제품 목록은 콜드 스타트입니다. 모든 새로운 계정은 콜드 스타트입니다. 어떤 모델과 어떤 피처에게는 임퓨테이션 전략이 그리 중요하지 않을 수 있습니다. 하지만 다른 경우에는 그것이 엄청나게 중요합니다. 새로운 계정이 생성되었을 때 "최근 30일간의 구매 횟수"에 대해 null 값을 받는 사기 탐지(fraud) 모델은, 0이나 전역 평균, 또는 세그먼트별 사전 확률(segment-specific prior)을 받는 모델과는 매우 다르게 동작할 수 있습니다.

콜드 스타트 (Cold start) 전략은 서빙 레이어 (serving layer)가 아닌 피처 정의 (feature definition) 단계에 포함되어야 합니다. 정의 단계에서는 다음 사항들을 명시해야 합니다:

  • 이력이 존재하지 않을 때 어떤 값을 제공할 것인가
  • 전역 기본값 (global default), 세그먼트별 사전 확률 (segment-specific prior), 또는 모델별 오버라이드 (model-specific override) 중 무엇을 사용할 것인가
  • 엔티티 (entity)가 콜드 스타트 처리 단계에서 벗어나기 전까지 얼마나 오랫동안 존재해야 하는가

콜드 스타트를 서빙 레이어에서 사후에 고려하는 것은 해당 전략이 모델 학습 과정에서 보이지 않는다는 것을 의미합니다. 즉, 모델은 콜드 스타트 예시 없이 학습되었으므로, 이를 적절하게 처리하는 법을 결코 배우지 못하게 됩니다.

모니터링: 무엇이 "건강한" 상태인가?
피처 스토어 (feature store)에는 자연스러운 테스트 스위트 (test suite)가 존재하지 않습니다. 피처 파이프라인 (feature pipeline)이 오류 없이 실행되는지, 그리고 두 스토어 모두에 값이 기록되고 있는지는 확인할 수 있습니다. 하지만 값이 실제로 정확한지, 즉 정답 여부(correctness)를 확인하려면 모니터링 전략이 필요합니다.

추적할 가치가 있는 핵심 신호들은 다음과 같습니다:

  • 피처 신선도 (Feature freshness): 각 피처에 대해 온라인 스토어 (online store)에 있는 가장 최근 값은 얼마나 오래되었는가? 이는 사후에 살펴보는 지표가 아니라, 능동적인 알람 (active alert) 대상이어야 합니다. 예상되는 갱신 주기 (refresh interval)의 두 배 동안 업데이트되지 않은 피처는 아마도 고장 났을 가능성이 높습니다.
  • 값 분포 드리프트 (Value distribution drift): 온라인 스토어 내 피처 값의 통계적 분포는 시간이 지남에 따라 대략적으로 안정적이어야 합니다 (또는 사용자 기반이 성장함에 따라 예상된 방식으로 변화해야 합니다). 평균, 분산, 또는 카디널리티 (cardinality)의 갑작스러운 변화는 상류 파이프라인 (upstream pipeline) 문제의 조기 경고 신호입니다. 예를 들어 소스 데이터의 스키마 변경 (schema change), 새로운 파이프라인 버전에서 도입된 필터링 버그, 또는 데이터 소스의 노후화 등이 이에 해당합니다.
  • 학습-서빙 분포 비교 (Training-serving distribution comparison): 서빙 시점에 기록된 피처 값의 분포와 학습 데이터셋 (training dataset)의 분포를 주기적으로 비교하십시오. 체계적인 괴리는 학습-서빙 스큐 (training-serving skew)가 축적되고 있다는 증거입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0