1. 토스뱅크 ML Service Team 분석
1-1. 팀의 미션과 역할
토스뱅크 ML Service Team은 은행 서비스 전반에 걸쳐 AI/ML 기술을 프로덕션 레벨로 서빙하는 핵심 팀입니다. 단순히 모델을 만드는 것이 아니라, 수천만 사용자가 실시간으로 이용하는 금융 서비스에 ML 모델을 안정적으로 배포하고 운영하는 것이 이 팀의 존재 이유입니다.
금융 도메인의 특수성을 고려하면, ML Service Team이 다루는 기술적 난이도는 일반 IT 기업보다 한 단계 높습니다.
- **가용성 요구사항**: 은행 서비스는 24/7 무중단 운영이 필수이며, 서비스 중단은 곧 금전적 손실과 규제 이슈로 직결됩니다
- **응답 속도 제약**: 신용평가, 이상거래 탐지 등은 밀리초 단위의 응답 속도가 요구됩니다
- **규제 준수**: 금융 데이터 처리에 관한 엄격한 규제를 기술적으로 충족해야 합니다
- **대규모 트래픽**: 토스 생태계의 수천만 사용자 트래픽을 안정적으로 처리해야 합니다
1-2. Data Scientist + ML Engineer 협업 구조
ML Service Team의 가장 큰 특징은 Data Scientist와 ML Engineer가 명확하게 역할을 분리하면서도 긴밀하게 협업하는 구조라는 점입니다.
**Data Scientist의 역할:**
- 비즈니스 문제를 ML 문제로 정의
- 모델 실험 및 학습 (실험 환경)
- 모델 성능 지표 정의 및 평가
- A/B 테스트 설계 및 결과 분석
**ML Backend Engineer의 역할:**
- 학습된 모델을 프로덕션에 서빙하는 인프라 구축
- 모델 서빙 API 설계 및 개발
- 서빙 성능 최적화 (지연시간, 처리량)
- 모델 버전 관리 및 배포 파이프라인 구축
- 모니터링 및 장애 대응
이 구조의 핵심은 **"Data Scientist가 모델에 집중하고, ML Backend Engineer가 서빙에 집중"**하는 분업 체계입니다. 서로의 전문 영역을 존중하면서 협업하기 때문에, ML Backend Engineer는 반드시 ML 전문가가 아니어도 됩니다. 대신 서버 아키텍처와 인프라에 대한 깊은 이해가 필수입니다.
1-3. 은행 업무 AI 제품
토스뱅크에서 ML이 적용되는 대표적인 영역을 살펴보겠습니다.
**신용평가 모델 서빙:**
- 대출 신청 시 실시간으로 신용점수를 산출해야 합니다
- P99 지연시간 100ms 이하가 일반적인 요구사항입니다
- 모델 업데이트 시 무중단 배포가 필수입니다
**이상거래 탐지 (FDS):**
- 모든 금융 거래를 실시간으로 분석합니다
- 초당 수만 건의 거래를 처리하는 처리량이 필요합니다
- False Positive를 최소화하면서 실제 사기를 놓치지 않아야 합니다
**개인화 추천:**
- 금융 상품 추천, 소비 패턴 분석 등
- 사용자별 실시간 피처를 기반으로 추론합니다
- Feature Store를 통한 피처 관리가 핵심입니다
**AI 챗봇:**
- 고객 문의에 자동으로 응답하는 시스템
- LLM 기반 서빙은 GPU 리소스 관리가 중요합니다
- RAG(Retrieval-Augmented Generation) 아키텍처 적용
1-4. 크로스펑셔널 협업
ML Backend Engineer는 기술만 잘한다고 되는 포지션이 아닙니다. 토스뱅크는 PO(Product Owner), 디자이너, 프론트엔드/백엔드 개발자, Data Scientist가 하나의 스쿼드를 구성하여 제품을 만드는 구조입니다.
- **PO와의 소통**: 비즈니스 요구사항을 기술적 제약 조건과 함께 논의
- **Data Scientist와의 소통**: 모델 서빙 요구사항 (지연시간, 배치 크기, 모델 포맷) 조율
- **프론트엔드 개발자와의 소통**: API 스펙 정의, 응답 포맷 협의
- **DevOps/SRE와의 소통**: 인프라 요구사항, 배포 전략, 모니터링 기준 설정
따라서 면접에서도 기술적 깊이뿐만 아니라 커뮤니케이션 역량을 평가할 가능성이 높습니다.
2. JD 완전 해부: 라인 바이 라인
이 섹션에서는 토스뱅크 ML Backend Engineer JD의 핵심 키워드를 하나씩 뜯어보며, 각 항목이 실제로 어떤 기술과 역량을 요구하는지 분석합니다.
2-1. "서버 아키텍처를 설계하고 개발"
이 문구는 단순한 CRUD API 개발이 아닙니다. **시스템 수준의 아키텍처 설계 능력**을 요구합니다.
**구체적으로 의미하는 것:**
- 서비스 간 통신 방식 선택 (동기 vs 비동기, REST vs gRPC)
- 데이터 흐름 설계 (어떤 데이터가 어디서 생성되어 어디로 흐르는가)
- 장애 격리 설계 (하나의 서비스 장애가 전체 시스템에 영향을 주지 않도록)
- 확장성을 고려한 설계 (수평 확장이 가능한 구조)
**핵심 패턴:**
- **CQRS (Command Query Responsibility Segregation)**: 읽기와 쓰기를 분리하여 각각 최적화
- **Event Sourcing**: 상태 변경을 이벤트로 기록하여 추적 가능성 확보
- **Saga Pattern**: 분산 트랜잭션을 관리하는 패턴
- **Strangler Fig Pattern**: 레거시 시스템을 점진적으로 마이그레이션하는 패턴
2-2. "대규모 트래픽을 안정적으로"
토스 생태계의 MAU(월간 활성 사용자)를 고려하면, "대규모"는 수천만 단위의 사용자 트래픽을 의미합니다.
**기술적 요구사항:**
- 초당 수만~수십만 건의 요청 처리 능력
- 피크 시간대(급여일, 이벤트 등) 급격한 트래픽 증가 대응
- 99.99% 이상의 서비스 가용성 유지
- 장애 발생 시 빠른 복구 (MTTR 최소화)
**필요한 기술:**
- 로드 밸런싱 전략 (L4/L7)
- 오토스케일링 (HPA, VPA, KEDA)
- 서킷 브레이커를 통한 장애 전파 방지
- 속도 제한(Rate Limiting)으로 시스템 보호
- 커넥션 풀링으로 리소스 효율화
- 비동기 처리로 처리량 극대화
2-3. "무거운 AI 모델을 안정적인 응답 속도로 서빙"
이 부분이 ML Backend Engineer의 핵심 차별점입니다. 일반 백엔드 엔지니어와 다른 점은 바로 **ML 모델 서빙에 대한 전문성**입니다.
**"무거운 AI 모델"이 의미하는 것:**
- 대형 딥러닝 모델 (수백 MB ~ 수 GB 크기)
- GPU 추론이 필요한 모델
- LLM 기반 모델 (수십억 파라미터)
- 앙상블 모델 (여러 모델의 조합)
**"안정적인 응답 속도"를 위한 기술:**
- 모델 최적화: ONNX 변환, TensorRT 가속, 양자화(Quantization)
- 배치 추론: 여러 요청을 묶어서 한 번에 처리
- 모델 캐싱: 자주 사용되는 추론 결과를 캐싱
- GPU 리소스 관리: GPU 메모리 최적화, Multi-Instance GPU(MIG) 활용
- 모델 서빙 프레임워크: Triton Inference Server, BentoML 등
2-4. "MSA 환경에서 분산 추적"
MSA 환경에서는 하나의 사용자 요청이 10개 이상의 마이크로서비스를 거칠 수 있습니다. 이때 문제가 발생하면 어디서 병목이 생겼는지, 어떤 서비스에서 에러가 발생했는지 추적하는 것이 **Observability**입니다.
**3가지 핵심 요소 (Three Pillars of Observability):**
1. **Traces (추적)**: 요청의 전체 경로를 시각화
- OpenTelemetry를 통한 계측(Instrumentation)
- Jaeger, Tempo 등으로 시각화
- Span, Trace ID, Parent-Child 관계 이해
2. **Metrics (메트릭)**: 시스템 상태를 숫자로 표현
- Prometheus를 통한 메트릭 수집
- RED 메트릭: Rate, Errors, Duration
- USE 메트릭: Utilization, Saturation, Errors
- Grafana로 대시보드 구성
3. **Logs (로그)**: 이벤트 기록
- 구조화된 로깅 (JSON 포맷)
- 로그 집계: ELK Stack, Loki
- 로그와 트레이스의 상관관계(Correlation)
2-5. "ML 경험 없어도 OK"
이 문구는 매우 중요한 시그널입니다. 토스뱅크가 이 포지션에서 진정으로 원하는 것은 **견고한 서버 개발 능력**이라는 뜻입니다.
**이것이 의미하는 바:**
- ML/DL 이론(CNN, RNN, Transformer 등)을 깊이 알 필요는 없습니다
- 대신 모델을 서빙하는 인프라, 서버, DevOps에 대한 전문성이 핵심입니다
- ML 도메인 지식은 입사 후 팀에서 학습할 수 있습니다
- 하지만 ML 서빙 프레임워크(Triton 등) 사용 경험이 있으면 큰 플러스입니다
**성장 기회:**
- ML 도메인 지식을 팀에서 자연스럽게 습득
- 최신 ML 인프라 기술을 실무에서 경험
- Data Scientist와 협업하며 ML 파이프라인 전체를 이해
- 금융 도메인 전문성도 함께 성장
3. 필수 기술스택 딥다이브
3-1. 서버 개발 (Python/Kotlin/Go)
토스뱅크 JD에서 언급된 세 가지 언어를 각각 깊이 분석합니다. 모든 언어를 다 알 필요는 없지만, 최소 하나는 깊이 있게, 나머지 하나는 기본적으로 다룰 수 있어야 합니다.
Python: ML 서빙의 핵심 언어
Python은 ML 생태계의 중심 언어이므로, ML Backend Engineer에게 가장 중요한 언어입니다.
**FastAPI (비동기 웹 프레임워크):**
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class PredictionRequest(BaseModel):
user_id: str
features: list[float]
class PredictionResponse(BaseModel):
score: float
model_version: str
latency_ms: float
@app.post("/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
start = asyncio.get_event_loop().time()
모델 추론 로직
score = await run_inference(request.features)
elapsed = (asyncio.get_event_loop().time() - start) * 1000
return PredictionResponse(
score=score,
model_version="v2.1.0",
latency_ms=round(elapsed, 2)
)
**asyncio 기반 비동기 프로그래밍:**
async def fetch_features(user_id: str) -> dict:
"""Feature Store에서 사용자 피처를 비동기로 조회"""
async with aiohttp.ClientSession() as session:
async with session.get(
f"http://feature-store/v1/features/user_id/{user_id}"
) as response:
return await response.json()
async def parallel_feature_fetch(user_ids: list[str]) -> list[dict]:
"""여러 사용자의 피처를 병렬로 조회"""
tasks = [fetch_features(uid) for uid in user_ids]
return await asyncio.gather(*tasks)
**Pydantic을 활용한 데이터 검증:**
from pydantic import BaseModel, Field, validator
class ModelConfig(BaseModel):
model_name: str = Field(..., description="모델 이름")
model_version: str = Field(..., regex=r"^v\d+\.\d+\.\d+$")
batch_size: int = Field(default=32, ge=1, le=512)
timeout_ms: int = Field(default=100, ge=10, le=5000)
@validator("model_name")
def validate_model_name(cls, v):
allowed = ["credit_score", "fds", "recommender", "chatbot"]
if v not in allowed:
raise ValueError(f"허용되지 않는 모델: {v}")
return v
**추천 학습 자료 (Python):**
- FastAPI 공식 문서 (fastapi.tiangolo.com)
- "Architecture Patterns with Python" - Harry Percival, Bob Gregory
- Real Python: asyncio 튜토리얼
- uvicorn + gunicorn 배포 가이드
Kotlin: 엔터프라이즈 서버 개발
Kotlin은 토스 생태계에서 핵심 언어 중 하나이며, Spring Boot 기반의 서비스 개발에 사용됩니다.
**Spring Boot + Coroutines:**
@RestController
@RequestMapping("/api/v1/models")
class ModelServingController(
private val inferenceService: InferenceService,
private val featureStore: FeatureStore
) {
@PostMapping("/predict")
suspend fun predict(
@RequestBody request: PredictionRequest
): PredictionResponse {
val startTime = System.nanoTime()
// 비동기로 피처 조회
val features = featureStore.getFeatures(request.userId)
// 모델 추론
val result = inferenceService.infer(
modelName = request.modelName,
features = features
)
val latencyMs = (System.nanoTime() - startTime) / 1_000_000.0
return PredictionResponse(
score = result.score,
modelVersion = result.version,
latencyMs = latencyMs
)
}
}
**Coroutines를 활용한 병렬 처리:**
suspend fun fetchMultipleFeatures(
userIds: List<String>
): List<FeatureSet> = coroutineScope {
userIds.map { userId ->
async(Dispatchers.IO) {
featureStore.getFeatures(userId)
}
}.awaitAll()
}
**추천 학습 자료 (Kotlin):**
- Kotlin 공식 문서 (kotlinlang.org)
- "Kotlin in Action" - Dmitry Jemerov
- Spring Boot + Kotlin 공식 가이드
- Kotlin Coroutines 공식 가이드
Go: 고성능 시스템 프로그래밍
Go는 인프라 도구, API 게이트웨이, 사이드카 프록시 등 성능이 중요한 컴포넌트에 사용됩니다.
**Gin 기반 API 서버:**
package main
"context"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
type PredictionRequest struct {
UserID string `json:"user_id" binding:"required"`
Features []float64 `json:"features" binding:"required"`
}
type PredictionResponse struct {
Score float64 `json:"score"`
ModelVersion string `json:"model_version"`
LatencyMs float64 `json:"latency_ms"`
}
func predictHandler(c *gin.Context) {
var req PredictionRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
start := time.Now()
ctx, cancel := context.WithTimeout(
c.Request.Context(),
100*time.Millisecond,
)
defer cancel()
score, err := runInference(ctx, req.Features)
if err != nil {
c.JSON(http.StatusInternalServerError,
gin.H{"error": "inference failed"})
return
}
latency := float64(time.Since(start).Microseconds()) / 1000.0
c.JSON(http.StatusOK, PredictionResponse{
Score: score,
ModelVersion: "v2.1.0",
LatencyMs: latency,
})
}
**goroutine과 channel을 활용한 동시성:**
func batchInference(
ctx context.Context,
requests []InferenceRequest,
) ([]InferenceResult, error) {
results := make([]InferenceResult, len(requests))
errChan := make(chan error, len(requests))
for i, req := range requests {
go func(idx int, r InferenceRequest) {
result, err := runSingleInference(ctx, r)
if err != nil {
errChan <- err
return
}
results[idx] = result
errChan <- nil
}(i, req)
}
for range requests {
if err := <-errChan; err != nil {
return nil, err
}
}
return results, nil
}
**추천 학습 자료 (Go):**
- Go 공식 투어 (tour.golang.org)
- "Go Programming Language" - Donovan, Kernighan
- "Concurrency in Go" - Katherine Cox-Buday
- Go by Example (gobyexample.com)
언어 선택 가이드
| 기준 | Python | Kotlin | Go |
| ----------- | ------------ | ---------------- | ---------------- |
| ML 생태계 | 최고 | 보통 | 제한적 |
| 서버 성능 | 보통 | 좋음 | 최고 |
| 동시성 모델 | asyncio | Coroutines | goroutine |
| 학습 곡선 | 낮음 | 중간 | 낮음 |
| 토스 생태계 | ML 서빙 | 비즈니스 로직 | 인프라 도구 |
| 추천 대상 | ML 서빙 중심 | 서비스 개발 중심 | 인프라/도구 중심 |
**권장 조합:**
- **메인 언어 1개** + **서브 언어 1개** 전략
- ML 서빙 중심이라면: Python(메인) + Kotlin 또는 Go(서브)
- 서비스 개발 중심이라면: Kotlin(메인) + Python(서브)
3-2. 견고한 서버 아키텍처
Design Patterns
**CQRS (Command Query Responsibility Segregation):**
읽기와 쓰기를 분리하는 패턴입니다. ML 서빙에서는 모델 업데이트(Command)와 추론 요청(Query)을 분리할 수 있습니다.
[모델 업데이트 요청] --> Command Service --> Model Registry --> Event Bus
[추론 요청] --> Query Service --> Model Cache --> 추론 결과 반환
**Event Sourcing:**
상태 변경을 이벤트로 기록합니다. 모델 서빙에서는 모델 배포 이력, A/B 테스트 결과 등을 이벤트로 관리할 수 있습니다.
Event 1: ModelDeployed(v1.0, timestamp, config)
Event 2: TrafficSplit(v1.0=90%, v1.1=10%)
Event 3: ModelPromoted(v1.1, reason="A/B test passed")
Event 4: ModelDeprecated(v1.0)
**Saga Pattern:**
분산 트랜잭션을 관리하는 패턴입니다. 예를 들어 모델 배포 프로세스에서 활용합니다.
Step 1: 모델 파일 업로드 → 성공
Step 2: 설정 파일 업데이트 → 성공
Step 3: 카나리 배포 시작 → 실패!
→ 보상 트랜잭션: Step 2 롤백 → Step 1 롤백
API Design: REST vs gRPC vs GraphQL
**REST API:**
- 범용적이고 클라이언트(웹/앱)와의 통신에 적합
- OpenAPI(Swagger) 명세를 통한 문서화가 용이
- JSON 기반으로 디버깅이 쉬움
**gRPC:**
- 서비스 간 내부 통신에 최적
- Protocol Buffers 기반으로 직렬화/역직렬화 성능이 우수
- 양방향 스트리밍 지원
- ML 모델 서빙에서 자주 사용 (Triton의 기본 프로토콜)
syntax = "proto3";
service ModelServing {
rpc Predict(PredictRequest) returns (PredictResponse);
rpc StreamPredict(stream PredictRequest) returns (stream PredictResponse);
rpc GetModelStatus(ModelStatusRequest) returns (ModelStatusResponse);
}
message PredictRequest {
string model_name = 1;
string model_version = 2;
repeated float features = 3;
}
**GraphQL:**
- 클라이언트가 필요한 데이터만 정확히 요청 가능
- 모델 메타데이터 조회 등에 활용 가능
**추천 전략:** 외부 API는 REST, 내부 서비스 간 통신은 gRPC, 메타데이터 조회는 GraphQL
인증/인가
- **OAuth2**: 토큰 기반 인증/인가 프레임워크
- **JWT (JSON Web Token)**: 상태를 서버에 저장하지 않는 토큰 방식
- **mTLS (mutual TLS)**: 서비스 간 상호 인증 (Service Mesh에서 자동 적용)
- **API Key**: 간단한 서비스 인증에 사용
캐싱 전략
ML 서빙에서 캐싱은 성능 최적화의 핵심입니다.
[요청] → Cache Hit? → YES → 캐시된 결과 반환 (1ms 이하)
→ NO → 모델 추론 수행 → 결과 캐싱 → 반환 (50~100ms)
**캐싱 레이어:**
- **L1 Cache (Application)**: 인메모리 캐시 (예: Python의 lru_cache)
- **L2 Cache (Redis/Memcached)**: 분산 캐시, 서비스 인스턴스 간 공유
- **L3 Cache (CDN)**: 정적 콘텐츠나 변하지 않는 추론 결과
**Redis 활용 패턴:**
class InferenceCache:
def __init__(self, redis_url: str, ttl: int = 300):
self.redis = redis.from_url(redis_url)
self.ttl = ttl
async def get_or_compute(
self, cache_key: str, compute_fn
):
캐시 조회
cached = await self.redis.get(cache_key)
if cached:
return json.loads(cached)
추론 수행
result = await compute_fn()
결과 캐싱
await self.redis.setex(
cache_key, self.ttl, json.dumps(result)
)
return result
메시지 큐
비동기 통신과 이벤트 기반 아키텍처의 핵심 인프라입니다.
**Kafka:**
- 대용량 이벤트 스트리밍에 최적
- 모델 추론 결과 로깅, 피처 파이프라인에 활용
- 높은 처리량과 내구성
- 파티셔닝을 통한 병렬 처리
**사용 시나리오:**
- 비동기 배치 추론 요청 처리
- 모델 학습 데이터 파이프라인
- 실시간 피처 업데이트
- 이벤트 기반 모델 재학습 트리거
Database
**PostgreSQL:**
- 모델 메타데이터, 실험 결과, 사용자 데이터 저장
- JSON 컬럼으로 유연한 스키마 지원
- 성능 최적화: 인덱싱, 파티셔닝, 커넥션 풀링(PgBouncer)
**MongoDB:**
- 모델 설정, 피처 정의 등 비정형 데이터에 적합
- 문서 기반이라 스키마 변경이 자유로움
**Redis:**
- 실시간 피처 서빙
- 세션 관리, 속도 제한 카운터
- 모델 추론 결과 캐싱
3-3. ML 모델 서빙
ML Backend Engineer의 핵심 전문 영역입니다. 이 섹션은 특히 깊이 있게 학습해야 합니다.
Triton Inference Server (NVIDIA)
가장 널리 사용되는 프로덕션 레벨 모델 서빙 프레임워크입니다.
**핵심 기능:**
- 다중 프레임워크 지원 (TensorFlow, PyTorch, ONNX, TensorRT)
- Dynamic Batching: 여러 요청을 자동으로 배치로 묶어 처리
- Model Pipeline: 여러 모델을 순차적으로 실행
- 모델 앙상블: 여러 모델의 결과를 조합
- GPU/CPU 동시 서빙
- 모델 핫 리로드 (무중단 모델 업데이트)
**모델 저장소 구조:**
model_repository/
credit_score/
config.pbtxt
1/
model.onnx
2/
model.onnx
fds_detector/
config.pbtxt
1/
model.plan
**config.pbtxt 예시:**
name: "credit_score"
platform: "onnxruntime_onnx"
max_batch_size: 64
input [
{
name: "features"
data_type: TYPE_FP32
dims: [ 128 ]
}
]
output [
{
name: "score"
data_type: TYPE_FP32
dims: [ 1 ]
}
]
dynamic_batching {
preferred_batch_size: [ 16, 32 ]
max_queue_delay_microseconds: 5000
}
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [ 0 ]
}
]
TorchServe / TensorFlow Serving
**TorchServe:**
- PyTorch 모델 전용 서빙 프레임워크
- 커스텀 핸들러를 통한 전/후처리 로직 구현
- 모델 버전 관리 기본 지원
**TensorFlow Serving:**
- TensorFlow/Keras 모델 전용
- SavedModel 포맷 지원
- gRPC, REST 인터페이스 제공
BentoML, Ray Serve, Seldon Core
**BentoML:**
- Python 네이티브 모델 패키징 프레임워크
- 간단한 데코레이터로 모델 서빙 API 생성
- 컨테이너화 자동화
from bentoml.io import JSON
runner = bentoml.onnx.get("credit_score:latest").to_runner()
svc = bentoml.Service("credit-scoring", runners=[runner])
@svc.api(input=JSON(), output=JSON())
async def predict(input_data: dict) -> dict:
features = input_data["features"]
result = await runner.predict.async_run(features)
return {"score": float(result[0])}
**Ray Serve:**
- Ray 기반 분산 모델 서빙
- 파이프라인 구성이 유연
- 오토스케일링 기본 지원
- 여러 모델을 하나의 서빙 그래프로 구성 가능
**Seldon Core:**
- Kubernetes 네이티브 ML 서빙 플랫폼
- A/B 테스트, 카나리 배포 기본 지원
- 모델 설명(Explainability) 기능 내장
- 다양한 추론 그래프 패턴 지원
모델 최적화
**ONNX (Open Neural Network Exchange):**
- 프레임워크 간 모델 호환성을 위한 표준 포맷
- PyTorch, TensorFlow 등에서 변환 가능
- ONNX Runtime으로 최적화된 추론
PyTorch 모델을 ONNX로 변환
dummy_input = torch.randn(1, 128)
torch.onnx.export(
model, dummy_input, "credit_score.onnx",
input_names=["features"],
output_names=["score"],
dynamic_axes={"features": {0: "batch_size"}},
)
**TensorRT:**
- NVIDIA GPU 전용 최적화 엔진
- 레이어 융합, 커널 자동 튜닝 등으로 추론 속도 2~5배 향상
- INT8/FP16 양자화 지원
**양자화 (Quantization):**
- FP32 → FP16: 메모리 절반, 속도 약 2배 향상
- FP32 → INT8: 메모리 1/4, 속도 약 3~4배 향상
- 정확도 손실을 최소화하면서 성능을 극대화
Feature Store
ML 모델이 추론을 수행하려면 입력 피처가 필요합니다. Feature Store는 이 피처를 중앙에서 관리합니다.
**Feast (Feature Store):**
- 오프라인 스토어: 학습용 피처 (BigQuery, S3 등)
- 온라인 스토어: 실시간 서빙용 피처 (Redis, DynamoDB)
- 피처 정의를 코드로 관리 (Feature Definition as Code)
from feast import Entity, FeatureView, Field
from feast.types import Float32, String
user = Entity(
name="user_id",
description="은행 고객 ID",
)
user_features = FeatureView(
name="user_credit_features",
entities=[user],
schema=[
Field(name="avg_balance_30d", dtype=Float32),
Field(name="transaction_count_7d", dtype=Float32),
Field(name="credit_utilization", dtype=Float32),
Field(name="payment_history_score", dtype=Float32),
],
online=True,
source=user_credit_source,
)
A/B Testing 프레임워크
새로운 모델을 배포할 때 기존 모델과 비교 실험을 수행합니다.
사용자 요청 → 트래픽 분배기 → 80% → 모델 v1 (기존)
→ 20% → 모델 v2 (신규)
→ 결과 비교 분석 → 승자 결정
**핵심 고려사항:**
- 통계적 유의성 확보 (p-value, 신뢰 구간)
- 사용자 경험 일관성 (같은 사용자는 같은 모델로)
- 실시간 모니터링 (조기 종료 기준 설정)
- 비즈니스 메트릭과 모델 메트릭 동시 추적
3-4. Kubernetes 운영
K8s 아키텍처 기초
**핵심 리소스:**
Deployment: ML 서빙 서버 배포
apiVersion: apps/v1
kind: Deployment
metadata:
name: credit-score-serving
namespace: ml-serving
spec:
replicas: 3
selector:
matchLabels:
app: credit-score
template:
metadata:
labels:
app: credit-score
spec:
containers:
- name: triton
image: nvcr.io/nvidia/tritonserver:23.10-py3
ports:
- containerPort: 8000
name: http
- containerPort: 8001
name: grpc
resources:
requests:
cpu: '2'
memory: '4Gi'
nvidia.com/gpu: '1'
limits:
cpu: '4'
memory: '8Gi'
nvidia.com/gpu: '1'
readinessProbe:
httpGet:
path: /v2/health/ready
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /v2/health/live
port: 8000
initialDelaySeconds: 30
periodSeconds: 15
Service: 내부 통신용 ClusterIP
apiVersion: v1
kind: Service
metadata:
name: credit-score-service
spec:
selector:
app: credit-score
ports:
- name: http
port: 8000
targetPort: 8000
- name: grpc
port: 8001
targetPort: 8001
type: ClusterIP
Ingress: 외부 트래픽 라우팅
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ml-serving-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: ml-serving.tossbank.internal
http:
paths:
- path: /credit-score
pathType: Prefix
backend:
service:
name: credit-score-service
port:
number: 8000
오토스케일링
**HPA (Horizontal Pod Autoscaler):**
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: credit-score-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: credit-score-serving
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: inference_requests_per_second
target:
type: AverageValue
averageValue: '100'
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
**KEDA (Kubernetes Event-Driven Autoscaling):**
- Kafka 큐 길이, Prometheus 메트릭 등을 기반으로 스케일링
- 0으로 스케일 다운 가능 (비용 절약)
- 이벤트 기반 배치 추론에 적합
Helm Charts & Kustomize
**Helm:**
- Kubernetes 리소스를 패키지로 관리
- 환경별(dev/staging/prod) 값 분리
- 차트 저장소를 통한 버전 관리
**Kustomize:**
- 베이스 설정 + 오버레이로 환경별 관리
- YAML 패치 방식으로 직관적
- kubectl에 기본 내장
Service Mesh
**Istio:**
- 서비스 간 mTLS 자동 적용
- 트래픽 라우팅 (카나리 배포, A/B 테스트)
- 서킷 브레이커, 재시도, 타임아웃 정책
- 분산 추적 자동 주입
VirtualService: 카나리 배포 트래픽 분배
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: credit-score-routing
spec:
hosts:
- credit-score-service
http:
- route:
- destination:
host: credit-score-service
subset: stable
weight: 90
- destination:
host: credit-score-service
subset: canary
weight: 10
GitOps: ArgoCD
ArgoCD Application 정의
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: credit-score-serving
namespace: argocd
spec:
project: ml-serving
source:
repoURL: https://github.com/tossbank/ml-serving-manifests
targetRevision: main
path: credit-score/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: ml-serving
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
**GitOps 워크플로:**
코드 변경 → Git Push → ArgoCD 감지 → 자동 배포 → 상태 검증
→ 실패 시 자동 롤백
GPU Scheduling on K8s
ML 모델 서빙에서 GPU 관리는 핵심 과제입니다.
**GPU 리소스 요청:**
resources:
limits:
nvidia.com/gpu: 1 # GPU 1개 할당
**Multi-Instance GPU (MIG):**
- 하나의 물리 GPU를 여러 인스턴스로 분할
- 작은 모델 여러 개를 하나의 GPU에서 동시 서빙
- 비용 효율성 극대화
**GPU Node Pool:**
- GPU 노드와 CPU 노드를 분리하여 관리
- 모델 추론은 GPU 노드, 전/후처리는 CPU 노드
- 노드 어피니티(Affinity)를 통한 스케줄링 제어
3-5. MSA & 분산 추적
OpenTelemetry
분산 추적, 메트릭, 로그를 통합하는 표준 프레임워크입니다.
**Python에서의 계측:**
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
트레이서 설정
provider = TracerProvider()
processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint="http://otel-collector:4317")
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("ml-serving")
async def predict(request):
with tracer.start_as_current_span("predict") as span:
span.set_attribute("model.name", request.model_name)
span.set_attribute("model.version", request.model_version)
with tracer.start_as_current_span("feature_fetch"):
features = await fetch_features(request.user_id)
with tracer.start_as_current_span("model_inference"):
result = await run_inference(features)
span.set_attribute("inference.latency_ms", result.latency)
return result
Prometheus + Grafana
**주요 메트릭:**
from prometheus_client import (
Counter, Histogram, Gauge, start_http_server
)
추론 요청 수
INFERENCE_REQUESTS = Counter(
"inference_requests_total",
"Total inference requests",
["model_name", "model_version", "status"]
)
추론 지연시간
INFERENCE_LATENCY = Histogram(
"inference_latency_seconds",
"Inference latency in seconds",
["model_name"],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0]
)
활성 모델 수
ACTIVE_MODELS = Gauge(
"active_models",
"Number of actively serving models"
)
GPU 사용률
GPU_UTILIZATION = Gauge(
"gpu_utilization_percent",
"GPU utilization percentage",
["gpu_id"]
)
**Grafana 대시보드 구성:**
- **서비스 헬스 패널**: 요청률(RPS), 에러율, P50/P95/P99 지연시간
- **모델 성능 패널**: 모델별 추론 시간, 배치 크기 분포
- **인프라 패널**: CPU/GPU/메모리 사용률, 파드 상태
- **비즈니스 패널**: 모델 정확도 모니터링, A/B 테스트 결과
로그 수집 및 분석
**구조화된 로깅:**
logger = structlog.get_logger()
async def predict(request):
logger.info(
"inference_started",
model_name=request.model_name,
user_id=request.user_id,
request_id=request.request_id,
)
try:
result = await run_inference(request)
logger.info(
"inference_completed",
model_name=request.model_name,
latency_ms=result.latency_ms,
score=result.score,
)
return result
except Exception as e:
logger.error(
"inference_failed",
model_name=request.model_name,
error=str(e),
error_type=type(e).__name__,
)
raise
**ELK Stack vs Loki:**
- **ELK Stack** (Elasticsearch + Logstash + Kibana): 전문(Full-text) 검색에 강점, 대규모 로그 분석에 적합
- **Loki** (Grafana): 라벨 기반 로그 집계, Grafana와 네이티브 통합, 비용 효율적
Alerting
**알림 전략:**
- **P1 (즉시 대응)**: 서비스 다운, 에러율 급증, 데이터 유실
- **P2 (30분 내 대응)**: 지연시간 SLA 위반, 디스크 사용률 90% 초과
- **P3 (업무 시간 내 대응)**: 메모리 사용률 증가 추세, 모델 성능 저하
Prometheus AlertManager 규칙 예시
groups:
- name: ml-serving-alerts
rules:
- alert: HighInferenceLatency
expr: |
histogram_quantile(0.99,
rate(inference_latency_seconds_bucket[5m])
) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: 'P99 추론 지연시간이 100ms를 초과했습니다'
- alert: HighErrorRate
expr: |
rate(inference_requests_total{status="error"}[5m])
/ rate(inference_requests_total[5m]) > 0.01
for: 2m
labels:
severity: critical
annotations:
summary: '추론 에러율이 1%를 초과했습니다'
3-6. 대규모 트래픽 & SLA
Load Balancing
**L4 (Transport Layer):**
- TCP/UDP 레벨에서 트래픽 분배
- 매우 빠른 처리 속도
- IP + Port 기반 라우팅
**L7 (Application Layer):**
- HTTP 헤더, URL 경로 기반 라우팅
- gRPC, WebSocket 지원
- 모델 이름에 따른 라우팅 가능
**알고리즘:**
- **Round Robin**: 순차적 분배 (기본)
- **Least Connections**: 연결 수가 가장 적은 서버로
- **Weighted Round Robin**: 서버 성능에 따라 가중치 부여
- **Consistent Hashing**: 같은 사용자를 같은 서버로 (캐시 효율)
Rate Limiting
**Token Bucket 알고리즘:**
class TokenBucket:
def __init__(self, rate: float, capacity: int):
self.rate = rate # 초당 토큰 생성 속도
self.capacity = capacity
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = asyncio.Lock()
async def acquire(self) -> bool:
async with self.lock:
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(
self.capacity,
self.tokens + elapsed * self.rate
)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True
return False
**Sliding Window 알고리즘:**
- 고정 윈도우의 경계 효과를 해결
- Redis ZSET을 활용한 분산 환경 구현
- 더 정밀한 속도 제한 가능
Circuit Breaker
from enum import Enum
from dataclasses import dataclass
class CircuitState(Enum):
CLOSED = "closed" # 정상 상태
OPEN = "open" # 차단 상태
HALF_OPEN = "half_open" # 복구 확인 중
@dataclass
class CircuitBreaker:
failure_threshold: int = 5
recovery_timeout: float = 30.0
state: CircuitState = CircuitState.CLOSED
failure_count: int = 0
last_failure_time: float = 0
async def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if time.monotonic() - self.last_failure_time > self.recovery_timeout:
self.state = CircuitState.HALF_OPEN
else:
raise CircuitOpenError("Circuit is open")
try:
result = await func(*args, **kwargs)
if self.state == CircuitState.HALF_OPEN:
self.state = CircuitState.CLOSED
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.monotonic()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
raise
성능 테스트
**k6 로드 테스트 스크립트:**
export const options = {
stages: [
{ duration: '2m', target: 100 }, // 점진적 증가
{ duration: '5m', target: 1000 }, // 피크 부하
{ duration: '2m', target: 0 }, // 점진적 감소
],
thresholds: {
http_req_duration: ['p(99)<100'], // P99 100ms 이하
http_req_failed: ['rate<0.01'], // 에러율 1% 미만
},
}
export default function () {
const payload = JSON.stringify({
user_id: `user_${__VU}`,
features: Array.from({ length: 128 }, () => Math.random()),
})
const params = {
headers: { 'Content-Type': 'application/json' },
}
const res = http.post('http://ml-serving.internal/v1/predict', payload, params)
check(res, {
'status is 200': (r) => r.status === 200,
'latency < 100ms': (r) => r.timings.duration < 100,
})
}
**Locust 파이썬 기반 테스트:**
from locust import HttpUser, task, between
class MLServingUser(HttpUser):
wait_time = between(0.1, 0.5)
@task(weight=10)
def predict_credit_score(self):
self.client.post("/v1/predict", json={
"model_name": "credit_score",
"user_id": "test_user",
"features": [0.5] * 128,
})
@task(weight=5)
def predict_fds(self):
self.client.post("/v1/predict", json={
"model_name": "fds_detector",
"transaction_id": "txn_123",
"features": [0.3] * 64,
})
4. 면접 예상 질문 30선
서버 아키텍처 (10문항)
**Q1. REST API와 gRPC의 차이점을 설명하고, ML 모델 서빙에서 어떤 것을 선택하겠습니까?**
핵심 포인트: gRPC는 Protocol Buffers 기반으로 직렬화 성능이 우수하고, 양방향 스트리밍을 지원하므로 내부 서비스 간 통신에 적합합니다. 외부 클라이언트 API는 REST로, 내부 모델 서빙은 gRPC로 구성하는 것이 일반적입니다.
**Q2. CQRS 패턴을 ML 서빙 시스템에 어떻게 적용할 수 있습니까?**
핵심 포인트: 모델 배포(Command)와 추론(Query)를 분리합니다. Command 측은 모델 레지스트리 업데이트, 설정 변경을 담당하고, Query 측은 읽기 전용으로 최적화된 추론 서비스를 운영합니다.
**Q3. 서킷 브레이커의 세 가지 상태(CLOSED, OPEN, HALF_OPEN)를 설명하고, ML 서빙에서의 활용 사례를 들어주세요.**
핵심 포인트: Feature Store 장애 시 캐시된 피처를 사용하거나, 외부 모델 서비스 장애 시 폴백 모델로 전환하는 등의 사례를 설명합니다.
**Q4. 동기 통신과 비동기 통신의 트레이드오프를 설명하고, 각각 어떤 상황에서 사용하겠습니까?**
핵심 포인트: 실시간 추론은 동기(gRPC/REST), 배치 추론이나 로깅은 비동기(Kafka)로 처리합니다. 비동기 통신은 서비스 간 결합도를 낮추고 장애 전파를 방지합니다.
**Q5. 캐싱 전략에서 Cache Invalidation 문제를 어떻게 해결하겠습니까?**
핵심 포인트: TTL 기반 만료, 이벤트 기반 무효화, Write-through/Write-behind 패턴 등을 설명합니다. ML 서빙에서는 모델 업데이트 시 관련 캐시를 이벤트로 무효화합니다.
**Q6. Connection Pooling이 왜 중요하고, 어떻게 구현하겠습니까?**
핵심 포인트: DB 커넥션, gRPC 채널, HTTP 커넥션 등의 생성 비용을 줄이고, 리소스를 효율적으로 관리합니다. 풀 크기 설정이 중요합니다 (너무 작으면 병목, 너무 크면 리소스 낭비).
**Q7. API 버전 관리 전략을 설명해주세요.**
핵심 포인트: URL 경로 기반(/v1/predict, /v2/predict), 헤더 기반 등의 전략이 있습니다. 하위 호환성을 유지하면서 점진적으로 새 버전으로 마이그레이션합니다.
**Q8. Saga 패턴의 두 가지 구현 방식(Choreography vs Orchestration)의 차이는 무엇입니까?**
핵심 포인트: Choreography는 이벤트 기반으로 각 서비스가 자율적으로 동작하고, Orchestration은 중앙 조율자가 흐름을 제어합니다. 복잡한 워크플로에는 Orchestration이 적합합니다.
**Q9. 데이터베이스 인덱싱 전략을 설명하고, 추론 로그 테이블에 어떤 인덱스를 설계하겠습니까?**
핵심 포인트: B-Tree, Hash, GIN 인덱스의 차이를 설명합니다. 추론 로그는 timestamp, model_name, user_id 등에 복합 인덱스를 설정하고 파티셔닝을 적용합니다.
**Q10. 마이크로서비스 간 데이터 일관성을 어떻게 보장하겠습니까?**
핵심 포인트: 강한 일관성(분산 트랜잭션)보다 최종적 일관성(Eventual Consistency)을 선택하는 것이 일반적입니다. Outbox 패턴, CDC(Change Data Capture) 등을 활용합니다.
K8s & 인프라 (10문항)
**Q11. Pod의 라이프사이클과 Probe(readinessProbe, livenessProbe, startupProbe)의 차이를 설명해주세요.**
핵심 포인트: livenessProbe는 컨테이너 재시작 여부 결정, readinessProbe는 트래픽 수신 가능 여부 결정, startupProbe는 초기화 완료 여부 결정입니다. ML 서빙에서 모델 로딩 시간이 긴 경우 startupProbe가 중요합니다.
**Q12. HPA와 VPA의 차이점과 각각의 적합한 사용 사례는 무엇입니까?**
핵심 포인트: HPA는 파드 수를 조절(수평 확장), VPA는 파드의 리소스 요청을 조절(수직 확장)합니다. ML 서빙은 HPA를 기본으로 사용하고, GPU 리소스 최적화에 VPA를 보조적으로 사용합니다.
**Q13. Kubernetes에서 무중단 배포(Zero-Downtime Deployment)를 어떻게 구현하겠습니까?**
핵심 포인트: Rolling Update 전략, readinessProbe 설정, PodDisruptionBudget 설정, Graceful Shutdown(preStop hook) 구현을 설명합니다.
**Q14. Helm Chart와 Kustomize의 차이점은 무엇이고, 어떤 상황에서 각각을 사용하겠습니까?**
핵심 포인트: Helm은 템플릿 기반 패키징으로 복잡한 애플리케이션 배포에 적합하고, Kustomize는 YAML 패치 기반으로 단순한 환경별 설정 관리에 적합합니다.
**Q15. Service Mesh(Istio)가 ML 서빙에 제공하는 이점은 무엇입니까?**
핵심 포인트: mTLS 자동 적용, 트래픽 관리(카나리 배포, A/B 테스트), 서킷 브레이커, 분산 추적 자동 주입 등을 설명합니다.
**Q16. GitOps(ArgoCD)의 원칙과 장점을 설명해주세요.**
핵심 포인트: Git을 single source of truth로 사용하여 선언적 배포를 관리합니다. 감사 추적, 롤백 용이성, 재현 가능성이 핵심 장점입니다.
**Q17. Kubernetes에서 GPU를 효율적으로 관리하는 방법은 무엇입니까?**
핵심 포인트: NVIDIA Device Plugin, MIG(Multi-Instance GPU), GPU 타임셰어링, 노드 어피니티를 통한 GPU 노드 전용 스케줄링을 설명합니다.
**Q18. Pod 스케줄링에서 Affinity, Taint, Toleration의 차이를 설명해주세요.**
핵심 포인트: Node Affinity는 특정 노드에 스케줄링을 선호하고, Taint/Toleration은 특정 노드를 특정 워크로드에 예약합니다. GPU 노드에 ML 서빙 파드만 스케줄링하는 등에 활용합니다.
**Q19. ConfigMap과 Secret의 차이점과 보안 관련 고려사항은 무엇입니까?**
핵심 포인트: Secret은 base64 인코딩(암호화 아님), RBAC으로 접근 제어, etcd 암호화 설정, External Secrets Operator를 통한 외부 비밀 관리 도구 연동을 설명합니다.
**Q20. Kubernetes의 리소스 요청(Requests)과 제한(Limits)을 어떻게 설정하겠습니까?**
핵심 포인트: Requests는 스케줄링 기준, Limits는 최대 사용량 제한입니다. ML 서빙에서는 GPU 리소스의 정확한 설정이 중요하며, CPU/메모리는 프로파일링을 통해 적정값을 찾습니다.
ML 서빙 & 시스템 설계 (10문항)
**Q21. 실시간 신용평가 모델 서빙 시스템을 설계해주세요.**
핵심 포인트: API Gateway → Feature Store 조회 → 모델 추론 → 결과 반환의 플로우를 설계합니다. P99 100ms SLA, 99.99% 가용성, 무중단 모델 업데이트를 충족하는 아키텍처를 제시합니다.
**Q22. Dynamic Batching이 무엇이고, 어떤 트레이드오프가 있습니까?**
핵심 포인트: 여러 요청을 모아서 배치로 처리하면 GPU 활용률이 높아지지만, 개별 요청의 지연시간이 증가할 수 있습니다. max_queue_delay로 최대 대기 시간을 설정합니다.
**Q23. 모델 A/B 테스트를 어떻게 설계하고 운영하겠습니까?**
핵심 포인트: 트래픽 분배(해시 기반), 통계적 유의성 검정, 조기 종료 기준, 안전 가드레일(에러율 급증 시 자동 롤백) 등을 설명합니다.
**Q24. ONNX와 TensorRT를 활용한 모델 최적화 과정을 설명해주세요.**
핵심 포인트: PyTorch 모델 → ONNX 변환 → ONNX 최적화 → TensorRT 엔진 생성 → 벤치마크 → 정확도 검증의 과정을 설명합니다.
**Q25. Feature Store의 온라인/오프라인 구분과 각각의 역할은 무엇입니까?**
핵심 포인트: 오프라인은 학습용(대용량, 배치 처리), 온라인은 서빙용(저지연, 실시간 조회)입니다. Feature Consistency(학습과 서빙 시 동일한 피처 보장)가 핵심 과제입니다.
**Q26. 이상거래탐지(FDS) 시스템의 아키텍처를 설계해주세요.**
핵심 포인트: 실시간 스트리밍(Kafka) → 피처 엔지니어링 → ML 추론 → 알림/차단의 파이프라인입니다. 초당 수만 건 처리, 밀리초 단위 응답, 높은 재현율(Recall)이 요구됩니다.
**Q27. 카나리 배포와 블루-그린 배포의 차이점과 각각의 장단점은 무엇입니까?**
핵심 포인트: 카나리는 점진적 트래픽 전환(위험 최소화), 블루-그린은 즉시 전환(빠른 배포/롤백). ML 모델 서빙에서는 카나리 배포가 일반적입니다(모델 성능을 점진적으로 검증).
**Q28. P99 지연시간이 급증했을 때 어떻게 디버깅하겠습니까?**
핵심 포인트: 분산 추적으로 병목 구간 확인 → 메트릭으로 리소스 사용률 확인 → 로그로 에러/경고 확인 → 프로파일링으로 코드 레벨 원인 분석의 순서로 접근합니다.
**Q29. LLM 기반 AI 챗봇의 서빙 아키텍처를 설계해주세요.**
핵심 포인트: RAG(Retrieval-Augmented Generation) 아키텍처, 벡터 DB 활용, GPU 메모리 관리, 스트리밍 응답, 토큰 레벨 속도 제한 등을 포함합니다.
**Q30. ML 모델의 모니터링 전략을 설계해주세요. 모델 드리프트를 어떻게 감지하겠습니까?**
핵심 포인트: 입력 데이터 분포 변화(Data Drift), 모델 예측 분포 변화(Concept Drift)를 모니터링합니다. KS Test, PSI(Population Stability Index) 등의 통계 기법을 활용합니다.
5. 6개월 학습 로드맵
월별 상세 계획
| 월 | 주제 | 구체적 목표 |
| ----- | ----------------------------- | ------------------------------------------------- |
| 1개월 | 언어 선택 + 서버 기초 | FastAPI 또는 Spring Boot로 REST API 프로젝트 완성 |
| 2개월 | DB + 캐시 + 메시지 큐 | Redis, PostgreSQL, Kafka 실습 프로젝트 |
| 3개월 | K8s + 배포 | Minikube에서 EKS/GKE로, Helm 차트 작성 |
| 4개월 | ML 서빙 기초 | Triton, BentoML로 실제 모델 서빙 구현 |
| 5개월 | MSA + 분산 추적 | OpenTelemetry, Grafana 대시보드 구성 |
| 6개월 | 시스템 설계 면접 + 포트폴리오 | 모의면접 실전, 이력서 최종 완성 |
1개월차: 언어 선택 + 서버 기초
**주차별 계획:**
Week 1-2: 메인 언어 기초 다지기
- Python 선택 시: FastAPI 공식 튜토리얼 완주
- Kotlin 선택 시: Spring Boot + Kotlin 기본 프로젝트
- 기본 CRUD API 개발 (모델 메타데이터 관리 API)
Week 3-4: 비동기 프로그래밍 + 테스트
- asyncio/Coroutines/goroutine 심화 학습
- 단위 테스트, 통합 테스트 작성
- Docker로 개발 환경 구성
**이달의 산출물:** 모델 메타데이터 관리 REST API (GitHub에 공개)
2개월차: DB + 캐시 + 메시지 큐
**주차별 계획:**
Week 1-2: 데이터베이스 심화
- PostgreSQL: 스키마 설계, 인덱싱, 쿼리 최적화
- Redis: 캐싱 패턴, 데이터 구조 활용
- SQLAlchemy/Exposed ORM 사용법
Week 3-4: 메시지 큐 + 이벤트 기반 아키텍처
- Kafka 기초: Producer, Consumer, Topic, Partition
- 이벤트 기반 비동기 처리 구현
- docker-compose로 통합 환경 구성
**이달의 산출물:** 1개월차 프로젝트에 DB, 캐시, 비동기 이벤트 처리 추가
3개월차: K8s + 배포
**주차별 계획:**
Week 1-2: Kubernetes 기초
- Minikube로 로컬 클러스터 구성
- Pod, Deployment, Service, Ingress 실습
- ConfigMap, Secret 관리
Week 3-4: 배포 자동화
- Helm Chart 작성
- ArgoCD로 GitOps 배포 구현
- HPA 오토스케일링 설정
- CI/CD 파이프라인 (GitHub Actions)
**이달의 산출물:** K8s에 배포된 애플리케이션 + Helm Chart + ArgoCD 설정
4개월차: ML 서빙 기초
**주차별 계획:**
Week 1-2: 모델 서빙 기초
- Triton Inference Server 설치 및 기본 사용
- ONNX 모델 변환 실습
- BentoML로 간단한 모델 서빙
Week 3-4: 서빙 최적화
- Dynamic Batching 설정 및 벤치마크
- 모델 버전 관리 구현
- Feature Store(Feast) 기초 설정
- K8s에 Triton 배포
**이달의 산출물:** K8s 위에서 동작하는 ML 모델 서빙 파이프라인
5개월차: MSA + 분산 추적
**주차별 계획:**
Week 1-2: Observability 구축
- OpenTelemetry 계측 구현
- Prometheus + Grafana 모니터링 대시보드
- Jaeger로 분산 추적 시각화
Week 3-4: MSA 패턴 적용
- 서킷 브레이커 구현
- 서비스 간 gRPC 통신
- 알림(Alerting) 규칙 설정
- 성능 테스트(k6/Locust) 실행
**이달의 산출물:** 완전한 Observability 스택이 갖추어진 ML 서빙 시스템
6개월차: 시스템 설계 면접 + 포트폴리오
**주차별 계획:**
Week 1-2: 시스템 설계 연습
- 면접 예상 질문 30선 모두 연습
- 화이트보드 시스템 설계 연습
- 동료/스터디 그룹과 모의면접
Week 3-4: 포트폴리오 + 이력서
- GitHub 프로젝트 README 정비
- 기술 블로그 작성 (학습 과정 기록)
- 이력서 작성 및 리뷰
- 토스뱅크 외 관심 기업 지원 준비
**이달의 산출물:** 완성된 이력서 + 포트폴리오 + 모의면접 피드백
6. 이력서 작성 전략
STAR 기법으로 경험 정리
이력서에서 가장 중요한 것은 **"무엇을 했는가"가 아니라 "어떤 문제를 어떻게 해결했는가"**입니다.
**STAR 프레임워크:**
- **S (Situation)**: 어떤 상황이었나
- **T (Task)**: 어떤 과제가 주어졌나
- **A (Action)**: 구체적으로 무엇을 했나
- **R (Result)**: 어떤 결과를 만들었나
**좋은 예시:**
[프로젝트] ML 모델 서빙 플랫폼 구축
상황: 데이터팀이 학습한 모델을 프로덕션에 배포하는 데 평균 2주가 소요
과제: 모델 배포 시간을 단축하고 서빙 안정성을 확보
행동:
- FastAPI + Triton 기반 모델 서빙 API 개발
- Helm Chart로 K8s 배포 자동화
- ArgoCD GitOps 파이프라인 구축
- OpenTelemetry 기반 모니터링 대시보드 구성
결과:
- 모델 배포 시간 2주 → 30분으로 단축 (97% 감소)
- P99 추론 지연시간 50ms 달성
- 서비스 가용성 99.95% 유지
트레이드오프 분석 강조
면접관이 가장 보고 싶어하는 것은 **기술적 의사결정 과정**입니다.
**작성 팁:**
- "A 대신 B를 선택한 이유"를 명확히 기술
- 고려했던 대안과 각각의 장단점을 언급
- 성능, 비용, 운영 복잡성 등 다차원적 비교
**예시:**
gRPC를 서비스 간 통신 프로토콜로 선택
- REST 대비 직렬화 성능 3배 향상 확인
- 단, 디버깅 복잡성 증가를 고려하여 gRPC 리플렉션과
grpcurl을 활용한 디버깅 환경 구축
- HTTP/2 기반 다중화로 커넥션 관리 효율화
비즈니스 임팩트를 수치로 증명
**수치화 가이드:**
- 성능 개선: "P99 지연시간 200ms에서 50ms로 75% 감소"
- 비용 절감: "GPU 활용률 최적화로 월 클라우드 비용 30% 절감"
- 효율성: "모델 배포 자동화로 엔지니어 시간 주당 10시간 절약"
- 안정성: "서비스 가용성 99.9%에서 99.99%로 향상"
모니터링에서 개선까지의 사이클 보여주기
단순히 "만들었다"가 아니라 "운영하면서 개선했다"는 스토리가 강력합니다.
배포 → 모니터링 → 이슈 발견 → 원인 분석 → 개선 → 재배포 → 검증
**예시:**
1. Grafana 대시보드에서 특정 시간대 P99 지연시간 급증 발견
2. Jaeger 분산 추적으로 Feature Store 조회 병목 확인
3. Redis 캐시 히트율 분석 → 캐시 키 전략 개선
4. 결과: Feature Store 조회 지연시간 80% 감소,
캐시 히트율 60%에서 92%로 향상
7. 포트폴리오 프로젝트 아이디어
프로젝트 1: ML 모델 서빙 플랫폼
**기술스택:** FastAPI + Triton Inference Server + Kubernetes + ArgoCD
**구현 범위:**
- FastAPI 기반 모델 서빙 Gateway API
- Triton Inference Server로 실제 모델 서빙
- ONNX 모델 변환 및 최적화 파이프라인
- Dynamic Batching 설정 및 벤치마크
- K8s Deployment + HPA 오토스케일링
- Helm Chart로 패키징
- ArgoCD GitOps 배포
- Prometheus + Grafana 모니터링
- OpenTelemetry 분산 추적
**차별화 포인트:**
- 모델 버전 관리 및 카나리 배포 구현
- A/B 테스트 프레임워크 통합
- 성능 벤치마크 결과 포함 (P50, P95, P99 지연시간, 처리량)
프로젝트 2: 실시간 이상 탐지 시스템
**기술스택:** Kafka + Python + ML 추론 + Redis + PostgreSQL
**구현 범위:**
- Kafka Consumer로 실시간 거래 이벤트 수집
- Feature 실시간 계산 (시간 윈도우 기반 통계)
- Redis를 활용한 온라인 Feature Store
- ML 모델로 이상 거래 판별
- 판별 결과 DB 저장 및 알림 발송
- Grafana 대시보드로 실시간 모니터링
**차별화 포인트:**
- 실시간 스트리밍 처리 경험 증명
- 초당 수천 건 처리 성능 벤치마크
- 모델 재학습 트리거 구현 (Data Drift 감지)
프로젝트 3: AI 챗봇 백엔드 (RAG + LLM 서빙)
**기술스택:** FastAPI + LangChain + 벡터 DB + GPU 서빙
**구현 범위:**
- RAG(Retrieval-Augmented Generation) 아키텍처 설계
- 문서 임베딩 파이프라인 (청킹, 벡터화, 저장)
- 벡터 DB (Milvus, Qdrant, Pinecone 중 선택)
- LLM 서빙 (vLLM 또는 TGI 활용)
- 스트리밍 응답 구현 (SSE)
- 대화 히스토리 관리
- 속도 제한(Rate Limiting) 구현
- K8s GPU 노드 스케줄링
**차별화 포인트:**
- LLM 서빙은 최신 트렌드이며 토스뱅크 AI 챗봇과 직결
- GPU 리소스 관리 경험 증명
- 토큰 사용량 모니터링 및 비용 최적화
실전 퀴즈
배운 내용을 점검해 봅시다.
**정답:** 요청이 도착한 후 최대 5ms(5000마이크로초) 동안 추가 요청을 기다리면서 배치를 구성합니다. 이 시간 내에 더 많은 요청이 도착하면 하나의 배치로 묶어 GPU에서 한 번에 처리합니다.
**핵심 트레이드오프:**
- 값을 높이면: 배치 크기가 커져 GPU 활용률은 높아지지만, 개별 요청의 지연시간이 증가합니다
- 값을 낮추면: 개별 요청의 지연시간은 줄어들지만, 배치 크기가 작아져 GPU 활용률이 낮아집니다
- P99 SLA 요구사항에 맞게 조절해야 합니다
**정답:** HALF_OPEN 상태는 서킷 브레이커가 OPEN(차단) 상태에서 설정된 복구 대기 시간이 지난 후 진입하는 상태입니다.
이 상태에서는 **제한된 수의 요청만 통과**시켜 하류 서비스가 정상으로 복구되었는지 확인합니다.
- 테스트 요청이 성공하면: CLOSED(정상) 상태로 전환하여 모든 트래픽을 다시 통과시킵니다
- 테스트 요청이 실패하면: 다시 OPEN 상태로 돌아가 복구 대기 시간을 리셋합니다
이를 통해 장애가 복구된 서비스에 갑자기 대량 트래픽이 유입되는 "thundering herd" 문제를 방지합니다.
**정답:** 온라인 스토어와 오프라인 스토어는 요구사항이 완전히 다르기 때문입니다.
**오프라인 스토어 (학습용):**
- 대용량 데이터 저장 (수 TB ~ PB)
- 배치 처리로 대량 읽기 가능
- 지연시간은 크게 중요하지 않음 (초~분 단위 허용)
- 보통 BigQuery, S3, Hive 등 사용
**온라인 스토어 (서빙용):**
- 실시간 개별 조회 (key-value 패턴)
- 밀리초 단위의 낮은 지연시간 필수
- 비교적 작은 데이터 (최신 피처 값만)
- 보통 Redis, DynamoDB 등 사용
**가장 중요한 원칙:** Training-Serving Skew(학습-서빙 불일치)를 방지하기 위해, 같은 피처 정의를 양쪽 스토어에서 공유해야 합니다.
**정답:** P99 지연시간이 더 중요합니다.
평균 지연시간은 극단적으로 빠르거나 느린 요청이 서로 상쇄되어 실제 사용자 경험을 왜곡합니다. 예를 들어 평균 50ms라도, 1%의 사용자가 2초 이상 기다릴 수 있습니다.
P99 지연시간은 "99%의 요청이 이 시간 안에 응답된다"는 의미이므로, 대부분의 사용자 경험을 보장합니다.
금융 서비스에서 SLA를 정의할 때도 P99 (또는 P99.9)를 기준으로 하는 것이 일반적입니다. 100명 중 1명이라도 느린 응답을 받으면 그것이 바로 서비스 품질 문제이기 때문입니다.
**추가로 알면 좋은 점:** P99가 높다면 GC(Garbage Collection) 정지, 콜드 스타트, 캐시 미스 등 간헐적 원인을 조사해야 합니다.
**정답:** 새 모델에 예상치 못한 문제가 있을 때 영향 범위를 최소화하기 위해서입니다.
**단계적 증가 전략:**
1. 10%로 시작하여 에러율, 지연시간, 비즈니스 메트릭 관찰
2. 문제가 없으면 25% → 50% → 75% → 100%으로 점진적 증가
3. 각 단계에서 충분한 관찰 시간을 확보 (통계적 유의성 확보)
4. 문제 발생 시 즉시 0%로 롤백
**ML 모델 특유의 고려사항:**
- 모델 정확도는 A/B 테스트 기간 동안 충분한 데이터가 모여야 평가 가능합니다
- 일부 edge case에서만 성능이 떨어지는 모델 문제를 사전에 발견할 수 있습니다
- 금융 도메인에서는 모델 오류가 직접적인 금전적 손실로 이어지므로 더욱 보수적인 접근이 필요합니다
10%는 업계에서 일반적으로 사용하는 초기 비율이며, 서비스 특성에 따라 5% 또는 1%로 더 보수적으로 시작하기도 합니다.
참고 자료
서적
1. **"Designing Data-Intensive Applications"** - Martin Kleppmann: 분산 시스템의 바이블로, 데이터 시스템 설계의 핵심 원칙을 학습할 수 있습니다
2. **"System Design Interview Vol.1 & Vol.2"** - Alex Xu: 시스템 설계 면접 준비의 필수서로, 실전 설계 문제와 풀이를 다룹니다
3. **"Building Machine Learning Pipelines"** - Hannes Hapke: ML 파이프라인의 전체 라이프사이클을 다루는 실무서입니다
4. **"Kubernetes in Action"** - Marko Luksa: K8s 아키텍처부터 실전 운영까지 체계적으로 학습할 수 있습니다
5. **"Designing Machine Learning Systems"** - Chip Huyen: ML 시스템 설계의 핵심 원칙과 패턴을 배울 수 있습니다
온라인 강좌
6. **Stanford CS329S: Machine Learning Systems Design**: ML 시스템 설계에 관한 스탠포드 강의로, ML 서빙 아키텍처를 깊이 학습할 수 있습니다
7. **Google SRE Book (sre.google/books)**: Site Reliability Engineering의 원칙과 사례를 다루며, SLA 관리와 장애 대응에 대한 인사이트를 제공합니다
8. **Kubernetes 공식 문서 (kubernetes.io/docs)**: K8s의 모든 리소스와 개념을 참고할 수 있는 공식 레퍼런스입니다
기술 블로그 및 자료
9. **NVIDIA Triton Inference Server Documentation**: Triton의 모든 기능과 설정을 상세히 다루는 공식 문서입니다
10. **OpenTelemetry 공식 문서 (opentelemetry.io)**: 분산 추적 표준의 개념, SDK, Collector 설정을 학습할 수 있습니다
11. **Feast Documentation (feast.dev)**: Feature Store의 아키텍처와 사용법을 학습하는 공식 가이드입니다
12. **BentoML 공식 문서 (docs.bentoml.com)**: Python 기반 모델 서빙 프레임워크의 튜토리얼과 레퍼런스입니다
13. **토스 기술 블로그 (toss.tech)**: 토스 그룹의 기술 문화와 엔지니어링 사례를 파악하는 데 필수적인 자료입니다
14. **MLOps Community (mlops.community)**: ML 운영에 관한 실무 사례와 최신 트렌드를 공유하는 커뮤니티입니다
15. **The ML Engineer Newsletter**: ML 엔지니어링 분야의 최신 논문, 도구, 사례를 정리한 뉴스레터입니다
실습 플랫폼
16. **Katacoda/Killercoda**: 브라우저에서 바로 K8s를 실습할 수 있는 인터랙티브 환경입니다
17. **k6 Documentation (k6.io)**: 성능 테스트 도구의 사용법과 스크립트 작성 가이드입니다
18. **Grafana Play (play.grafana.org)**: Grafana 대시보드를 직접 체험해볼 수 있는 데모 환경입니다
현재 단락 (1/1187)
토스뱅크 ML Service Team은 은행 서비스 전반에 걸쳐 AI/ML 기술을 프로덕션 레벨로 서빙하는 핵심 팀입니다. 단순히 모델을 만드는 것이 아니라, 수천만 사용자가 실시...