▣ Streaming 및 Batch 처리를 고려한 Inference API 설계 및 구현 Overview
음성 인식(ASR)이나 잡음 제거 모델을 실제 시스템에 올릴 때 가장 까다로운 부분 중 하나가 바로 데이터의 유입 방식에 따른 아키텍처 분리입니다.
마이크에서 실시간으로 들어오는 오디오를 처리하는 스트리밍(Streaming) 방식과, 이미 녹음된 파일이나 대량의 텍스트를 한 번에 처리하는 배치(Batch) 방식은 요구되는 프로토콜과 내부 상태 관리 방법이 완전히 다릅니다. 이 두 가지를 모두 수용하는 Inference API의 설계 및 구현 가이드를 정리해 드립니다.
1. 실시간(Stream) & 배치(Batch) Processing

(1) Stream Processing
Stream Processing은 이벤트가 발생하는 즉시 처리하는 방식이다. 데이터가 시스템에 유입되는 순간 단위로 반응하며, 실시간 대시보드 업데이트나 이상 거래 탐지와 같은 작업에 적합하다. 처리 결과가 즉시 반영되므로 실시간성이 핵심 요구사항인 환경에서 활용된다.
(2) Batch Processing
Batch Processing은 데이터가 모두 준비된 시점에 한 번에 처리하는 방식이다. 일정 기간 동안 축적된 데이터를 기반으로 작업이 수행되며, 대표적인 사례로 하루 동안 저장된 판매 데이터를 자정에 요약하는 작업이 있다. 이 방식은 데이터가 완전하게 확보된 이후 처리되기 때문에 분석 결과가 생성되기까지의 지연 시간이 상대적으로 길다.
(3) Batch Processing과 Stream Processing의 특성 비교
| 항목 | Stream Processing | Batch Processing |
| 데이터 특성 | 무한하고 연속적인 데이터셋 | 유한하고 완전한 데이터 |
| 처리 시점 | 데이터가 도착하는 즉시 처리 | 모든 데이터가 수집된 후 처리 |
| 지연 시간 | 낮음(밀리초~초) | 높음(분~수시간) |
| 예시 | 이상 거래 탐지, 실시간 추천 | 일별 판매 리포트, ETL 작업 |
시간 처리 방식(Timing Semantics)
• Batch Processing은 프로세스 시간(processing time)을 중심으로 처리한다. 반면 Stream Processing은 이벤트 시간(event time)과 도착 시간(ingestion time)을 기반으로 처리하며, 데이터의 실제 발생 시점을 반영하기 위한 시간 의미론이 사용된다.
내결함성(Fault Tolerance)
• Batch Processing에서는 실패한 작업을 재시도하는 방식으로 오류를 복구한다.
• Stream Processing에서는 체크포인트와 상태 복구 메커니즘을 통해 연속적이고 무한한 데이터 흐름 속에서도 일관된 상태를 유지해야 한다.
2. 스트리밍(Streaming) vs 배치(Batch)의 근본적 차이
• 스트리밍 (Real-time)
- 특징: 데이터가 작게 쪼개져(Chunk) 지속적으로 들어옵니다. (예: 100ms 단위의 오디오 버퍼).
- 핵심 제약: 상태 유지(Stateful). 이전 100ms에서 처리했던 모델의 은닉 상태(Hidden State)를 다음 100ms 처리에 넘겨주어야 문맥이 이어집니다.
- 목표: 초저지연(Low Latency)과 실시간 피드백.
• 배치 (Offline/Batch)
- 특징: 완성된 데이터 전체가 한 번에 들어옵니다. (예: 1분짜리 WAV 파일).
- 핵심 제약: 무상태(Stateless). 각 요청이 독립적이므로 병렬 처리가 쉽습니다.
- 목표: 시스템 자원의 효율적 사용과 높은 처리량(Throughput).
3. API Interface Design (Protocol Selection)
두 가지 방식을 하나의 엔드포인트에서 우아하게 처리하려면 REST API(HTTP/1.1)보다는 양방향 스트리밍을 기본 지원하는 gRPC (HTTP/2 기반) 또는 차량 내 로컬 통신을 위한 AIDL (Android Automotive OS 환경) 설계가 필수적입니다.
gRPC / Protobuf 설계 예시 (Universal ASR API)
하나의 서비스 안에 단일 요청(Unary)과 스트리밍(Bidirectional) 메서드를 모두 정의합니다.
Protocol Buffers
3. 내부 파이프라인 및 상태 관리 (Implementation)
API로 들어온 데이터를 TFLite 엔진까지 전달하는 내부 구현은 다음과 같이 분기해야 합니다.
A. 스트리밍 처리 파이프라인
• 세션 매니저 (Session Manager): session_id 단위로 메모리를 할당합니다.
• 링 버퍼 (Ring Buffer): 마이크에서 들어오는 패킷이 유실되지 않도록 락-프리(Lock-free) 링 버퍼에 데이터를 쌓습니다.
• 모델 상태 관리 (State Caching): Conformer나 RNN-T 같은 모델은 추론 후 출력되는 state 텐서를 메모리에 캐싱했다가, 다음 청크가 들어올 때 입력 텐서로 다시 밀어 넣어야 합니다.
• VAD(Voice Activity Detection) 결합: 묵음 구간이 지속되면 is_last_chunk를 자체적으로 트리거하여 메모리를 초기화하고 최종 텍스트를 반환합니다.
B. 배치 처리 파이프라인
• 작업 큐 (Task Queue): 대용량 파일이 들어오면 메인 스레드가 블록되지 않도록 Thread Pool이나 큐(예: Celery, 멀티스레드 워커)에 작업을 던집니다.
• 청킹 및 병렬화: 긴 오디오는 내부적으로 10초~30초 단위로 자른 뒤, 배치 사이즈(Batch Size > 1)를 늘려 NPU/CPU에 한 번에 태워 연산 효율을 극대화합니다.
4. 하드웨어 라우팅 전략 (TFLite & NPU 최적화)
추론 API의 백엔드에서는 요청의 성격에 따라 하드웨어 가속기(NPU, DSP, CPU)를 다르게 할당하는 라우터 로직이 필요합니다.
(1) 스트리밍 추론 (Latency 최우선):
• 할당: 가벼운 스트리밍용 Conformer 모델 -> NPU (INT8 Delegate) 또는 DSP.
• 이유: 레이턴시가 생명이므로 뱃치 사이즈를 무조건 1로 고정하고, 캐시 미스를 줄이기 위해 특정 코어에 바인딩(Thread Affinity)합니다.
(2) 배치 추론 (Throughput 최우선):
• 할당: 무거운 Whisper 모델 -> NPU (병렬) 또는 CPU 멀티코어.
• 이유: 실시간일 필요가 없으므로 뱃치 사이즈를 키워 NPU의 연산기(ALU)를 100% 활용하도록 유도합니다.
5. 안드로이드 오토모티브(AAOS) 환경에서의 특별 고려사항
만약 이 API가 서버가 아니라 차량 내부의 로컬 서비스로 동작한다면, 네트워크 스택(gRPC) 대신 AIDL (Android Interface Definition Language)을 사용하여 Native System Service를 구축해야 합니다.
• 클라이언트(인포테인먼트 앱)는 AIDL 인터페이스를 통해 PCM 버퍼를 Native C++ 데몬으로 넘깁니다.
• 이때 메모리 복사로 인한 오버헤드를 막기 위해 Android의 SharedMemory (또는 FMQ: Fast Message Queue)를 사용하여 오디오 데이터를 C++ 추론 엔진으로 직접 전달하는 방식이 매우 중요합니다.
참고: https://velog.io/@yin/Intro-Batch와-Streaming-의-차이점
구축하시려는 Inference API의 주요 클라이언트(이 API를 호출해서 사용하는 쪽)는 주로 어떤 애플리케이션(예: 안드로이드 앱, 백그라운드 네이티브 서비스, 외부 클라우드)이 될 예정인가요?