Skip to main content
LadeStack LogoLadeStack
AI Development 약 16분 소요 2024년 6월 25일

웹 개발자를 위한 머신러닝 파이프라인: 데이터 수집부터 브라우저 및 서버 추론까지

웹 엔지니어를 위한 종합 MLOps 파이프라인 가이드. 데이터 수집, 데이터 누수 방지, ONNX Runtime Web을 통한 브라우저 추론, FastAPI 서버 추론 및 운영 환경 드리프트 모니터링.

데이터 수집, 모델 학습, ONNX 브라우저 추론 및 FastAPI 마이크로서비스를 연결하는 머신러닝 파이프라인 아키텍처 다이어그램

현대 웹 개발 환경에서 머신러닝(ML)은 단순한 보조 기술을 넘어 핵심적인 제품 차별화 요소로 자리 잡았습니다. 개인화 추천, 검색 쿼리 의도 파악, 실시간 사기 탐지 및 클라이언트 측 이미지 처리에 이르기까지, 최신 웹 애플리케이션은 사용자 인터페이스 내에 지능형 예측 모델을 유기적으로 통합하고 있습니다.

하지만 많은 웹 엔지니어링 팀에게 데이터 사이언스 팀의 주피터 노트북(Jupyter Notebook) 모델을 프로덕션 웹 인프라로 배포하는 과정은 수많은 기술적 병목을 유발합니다. 학습과 서빙 간의 전처리 불일치(Training-Serving Skew), 데이터 누수(Data Leakage), 예측 레이턴시 급증, 그리고 시간 경과에 따른 모델 성능 저하(Data Drift)가 대표적인 예입니다.

본 가이드에서는 웹 엔지니어 관점에서 데이터 수집부터 검증, 브라우저 대 서버 추론 트레이드오프 분석, ONNX Runtime Web 및 FastAPI 구현, 실시간 MLOps 모니터링까지 견고한 머신러닝 파이프라인을 구축하는 실전 아키텍처를 살펴봅니다.


1. 엔드투엔드 머신러닝 파이프라인 아키텍처

안정적인 ML 기반 웹 시스템은 명확히 격리된 4단계 라이프사이클로 구성됩니다.

[ 1단계: 데이터 수집 및 유효성 검증 ]
사용자 이벤트 로그 / RDBMS / 스트리밍 큐
       │
       ▼
스키마 유효성 검증 및 정제 (Pydantic / Great Expectations)
       │
       ▼
시계열 기준 Train / Val / Test 분할 (데이터 누수 방지)

─────────────────────────────────────────────────────────────────

[ 2단계: 모델 학습 및 아티팩트 내보내기 ]
피처 저장소 및 파이프라인 학습 (Scikit-Learn / PyTorch)
       │
       ▼
포맷 변환 (ONNX / TorchScript) 및 경량 양자화 (INT8 / FP16)
       │
       ▼
모델 레지스트리 (S3 / MLflow) 및 버전 관리

─────────────────────────────────────────────────────────────────

[ 3단계: 배포 및 추론 서빙 ]
       ┌───────────────────────────────┐
       ▼                               ▼
[ 클라이언트 측 브라우저 추론 ]        [ 서버 측 추론 엔드포인트 ]
ONNX Runtime Web (WASM / WebGPU)      FastAPI / Triton 추론 서버
제로 서버 비용・강력한 데이터 프라이버시   대규모 딥러닝・핵심 IP 보호

─────────────────────────────────────────────────────────────────

[ 4단계: MLOps 및 드리프트 모니터링 ]
입력 피처 분포 추적 (KS 검정 / PSI) 및 모델 성능 저하 탐지

2. 데이터 수집과 데이터 누수(Leakage) 방지

웹 엔지니어가 ML 파이프라인을 구축할 때 가장 흔히 범하는 심각한 오류는 **데이터 누수(Data Leakage)**입니다.

데이터 누수의 주요 원인

  1. 스케일러 파라미터 유출: 테스트 데이터를 포함한 전체 데이터셋에 대해 StandardScaler.fit()을 실행하면 미래의 통계적 평균과 분산이 학습 단계에 유출되어 오프라인 검증 점수가 비정상적으로 높게 산출됩니다.
  2. 시계열 이벤트의 무작위 분할: 사용자 활동 로그와 같은 시간 순서 의존적 데이터에 무작위 K-Fold 교차 검증을 적용하면 모델이 미래의 데이터를 기반으로 과거 이벤트를 학습하는 치명적인 오류가 발생합니다.

누수 방지 전처리 파이프라인 (Python)

# pipeline/preprocess.py
import pandas as pd
import numpy as np
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression

def build_leak_free_pipeline(df: pd.DataFrame, target_col: str, timestamp_col: str):
    # 시계열 순서로 엄격하게 정렬
    df = df.sort_values(timestamp_col).reset_index(drop=True)
    X = df.drop(columns=[target_col, timestamp_col])
    y = df[target_col]

    # 최근 20% 데이터를 운영 홀드아웃 검증 세트로 격리
    split_index = int(len(df) * 0.8)
    X_train, X_test = X.iloc[:split_index], X.iloc[split_index:]
    y_train, y_test = y.iloc[:split_index], y.iloc[split_index:]

    # Pipeline 객체 내부에 전처리를 캡슐화하여 서빙 스큐를 원천 차단
    pipeline = Pipeline([
        ('scaler', StandardScaler()),
        ('classifier', LogisticRegression(C=1.0, solver='lbfgs', max_iter=500))
    ])

    pipeline.fit(X_train, y_train)
    holdout_score = pipeline.score(X_test, y_test)
    print(f"홀드아웃 검증 정확도: {holdout_score:.4f}")

    return pipeline

3. 브라우저 추론 vs 서버 추론 트레이드오프

추론 연산을 실행할 위치를 결정하는 것은 인프라 비용과 사용자 경험에 결정적인 영향을 미칩니다.

평가 기준 클라이언트 브라우저 추론 (ONNX Runtime Web) 서버 측 추론 (FastAPI / Triton)
네트워크 레이턴시 0ms (네트워크 왕복 없음) 왕복 네트워크 지연 발생 (50~250ms)
컴퓨팅 비용 0원 (사용자의 클라이언트 하드웨어 활용) 서버 인스턴스 비용 발생 (오토스케일링 필요)
오프라인 지원 완전 지원 (서비스 워커로 캐싱 가능) 지원 불가 (인터넷 연결 필수)
데이터 프라이버시 최상 (민감한 데이터가 브라우저 밖으로 나가지 않음) 서버 전송 필요 (보안 규정 준수 필요)
모델 지적 재산(IP) 보호 불가 (클라이언트에 모델 바이너리 노출) 완벽 보호 (서버 내부 비공개 유지)
모델 크기 제한 소형/경량 모델 (< 50MB 권장) 초대형 딥러닝 모델 (수십 GB 지원)

4. 클라이언트 측 추론 구현: ONNX Runtime Web

가벼운 의도 분류나 입력 데이터 전처리는 WebAssembly(WASM) 및 WebGPU 하드웨어 가속을 지원하는 ONNX Runtime Web을 통해 브라우저에서 효율적으로 구동할 수 있습니다.

// src/lib/ml/browserInference.ts
import * as ort from 'onnxruntime-web';

ort.env.wasm.numThreads = 2;
ort.env.wasm.simd = true;

export interface PredictionResult {
  probabilities: number[];
  predictedClass: number;
  inferenceDurationMs: number;
}

export class BrowserInferenceService {
  private session: ort.InferenceSession | null = null;

  async loadModel(modelPath: string): Promise<void> {
    this.session = await ort.InferenceSession.create(modelPath, {
      executionProviders: ['wasm'],
      graphOptimizationLevel: 'all'
    });
  }

  async predict(features: number[]): Promise<PredictionResult> {
    if (!this.session) {
      throw new Error("추론 세션이 초기화되지 않았습니다. loadModel()을 먼저 실행하세요.");
    }

    const start = performance.now();
    const inputTensor = new ort.Tensor('float32', Float32Array.from(features), [1, features.length]);
    const inputName = this.session.inputNames[0];
    const feeds: Record<string, ort.Tensor> = {};
    feeds[inputName] = inputTensor;

    const results = await this.session.run(feeds);
    const outputName = this.session.outputNames[0];
    const outputTensor = results[outputName];
    const outputData = Array.from(outputTensor.data as Float32Array);

    let maxIdx = 0;
    let maxVal = outputData[0];
    for (let i = 1; i < outputData.length; i++) {
      if (outputData[i] > maxVal) {
        maxVal = outputData[i];
        maxIdx = i;
      }
    }

    return {
      probabilities: outputData,
      predictedClass: maxIdx,
      inferenceDurationMs: performance.now() - start
    };
  }
}

5. 서버 측 추론 마이크로서비스: FastAPI 구현

지적 재산 보호가 중요하거나 대규모 연산이 필요한 복잡한 딥러닝 모델의 경우, FastAPI 기반의 비동기 마이크로서비스를 구축하는 것이 적합합니다.

# server/app.py
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
import numpy as np
import onnxruntime as ort
import time

app = FastAPI(title="ML 추론 마이크로서비스", version="1.0.0")

class InferenceRequest(BaseModel):
    user_id: str
    features: list[float] = Field(..., min_items=5, max_items=5)

class InferenceResponse(BaseModel):
    user_id: str
    prediction: int
    confidence: float
    latency_ms: float

session = ort.InferenceSession("models/production_classifier.onnx")
input_name = session.get_inputs()[0].name

@app.post("/api/v1/predict", response_model=InferenceResponse)
async def predict_endpoint(payload: InferenceRequest):
    try:
        start_time = time.perf_counter()
        input_array = np.array([payload.features], dtype=np.float32)

        raw_outputs = session.run(None, {input_name: input_array})
        probabilities = raw_outputs[0][0]
        predicted_class = int(np.argmax(probabilities))
        confidence = float(probabilities[predicted_class])
        duration_ms = (time.perf_counter() - start_time) * 1000

        return InferenceResponse(
            user_id=payload.user_id,
            prediction=predicted_class,
            confidence=confidence,
            latency_ms=duration_ms
        )
    except Exception as exc:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"추론 엔진 연산 오류: {str(exc)}"
        )

6. MLOps 및 프로덕션 드리프트 모니터링

모델을 성공적으로 배포한 이후에도 현실 세계의 데이터 트렌드 변화로 인해 모델의 정확도가 저하되는 **데이터 드리프트(Data Drift)**가 발생할 수 있습니다.

핵심 모니터링 원칙

  1. 입력 피처 드리프트 감지: Kolmogorov-Smirnov(KS) 검정 또는 인구 안정성 지수(PSI)를 활용하여 프로덕션 쿼리 데이터의 통계적 분포가 학습 데이터와 차이를 보이는지 지속적으로 검증합니다.
  2. 예측 확신도 하락 추적: 모델이 반환하는 예측 신뢰도(Confidence) 점수가 급격히 하락하면 이상 데이터 입력 또는 새로운 트렌드 발생을 의미합니다.
  3. 섀도우 배포(Shadow Deployment): 새 모델(v2)을 백그라운드에 병렬 배포하여 실제 운영 트래픽을 처리하게 하되 사용자에게는 기존 v1 결과를 제공하면서 v2의 지연 시간과 예측 분포를 안전하게 검증합니다.

7. 결론

웹 엔지니어가 머신러닝을 실무에 효과적으로 통합하려면 모델 튜닝뿐만 아니라 데이터 정제, 브라우저 대 서버 아키텍처 선택, 운영 환경 모니터링을 포괄하는 전체 파이프라인을 체계적으로 설계해야 합니다.

더 자세한 인공지능 아키텍처 가이드는 LadeStack의 벡터 데이터베이스를 활용한 RAG 애플리케이션 구축 및 도메인 특화 태스크를 위한 LLM 파인튜닝 글을 참고하세요.

More Engineering Articles