본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

듣는 네 가지 방법

~12 min · stt-engines, whisperkit, on-device, apple-silicon

Level 0불씨 없음
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Apple Silicon 엔 좋은 귀가 넷 있어. 네 목소리엔 기본을 고르고, 나머지는 어댑터로 둬."

온디바이스는 협상 대상이 아냐

엔진을 비교하기 전에, 시장 대부분을 걸러 내는 규칙이 하나 있어. 엔진은 이 기계 위에서 돌아야 해. 클라우드 API 가 더 정확할 수도 있어. 그런데 Firekeeper 가 존재하는 근거인 두 약속을 깨. 오디오가 로컬에 남는다는 것, 오프라인으로도 된다는 것. 그래서 아래 엔진은 전부 온디바이스야. 그 안에서 뭘 고르느냐는 속도, 언어 처리, 패키징 문제지 '오디오가 나가도 되나?' 문제가 아니고. 그 답은 항상 '안 돼' 니까.

네 엔진

엔진                   성격              Firekeeper 에서 역할
----                   ----              -----------------
WhisperKit             Swift 네이티브,   기본. 한/영 혼합 처리.
                       Core ML           large-v3-turbo 모델.
Apple SpeechAnalyzer   macOS 26 API,     빠른 2번 엔진. 모델 파일
                       무다운로드        없음. 스트리밍 친화.
whisper.cpp            C/C++, Core ML    폴백 / 벤치마크용.
MLX Whisper            Python, MLX       선택적 배치 '파워 모드'.

WhisperKit 이 기본인 건 Swift 네이티브고, Core ML 최적화됐고, 결정적으로 아빠의 한/영 혼합 말을 처리해서야. Apple SpeechAnalyzer(macOS 26)는 빠른 두 번째 엔진이야. 관리할 모델 파일이 없고 스트리밍도 네이티브로 돼. 대신 두 레슨 뒤에 만날 언어 함정을 하나 달고 있어. whisper.cpp 는 잘 익은 폴백이고, CLI 벤치마크를 올려놓기에 딱 좋아. MLX Whisper 는 배치 작업용 Python 기반 파워 모드야. 긴 녹음엔 좋은데 패키징한 앱에는 안 맞고.

기본 더하기 어댑터

모양을 봐. 기본 하나에 어댑터 여럿. 엔진 하나를 영원히 정하는 게 아냐. 흔한 경우에 쓸 기본을 고르고, 나머지는 공통 인터페이스 뒤에 두고 갈아끼울 수 있게 해 두는 거지. 엔진이 텍스트 감지한 언어를 같이 돌려주니까, 앱의 나머지는 넷 중 누가 그 단어를 만들었는지 알 필요가 전혀 없어. 그 인터페이스가 다음 트랙 주제 전부야. 여기선 '어느 STT 엔진을 쓸까' 가 평생 가는 결혼이 아니라 런타임에 고르는 선택이라는 것만 새겨 둬.

온디바이스가 먼저, 최적화는 그다음. 프라이버시랑 오프라인 약속이 후보 범위를 정해. 로컬 엔진만. 그 범위 안에서 기본을 정하는 건 속도, 언어 처리, 패키징이고. 더 빠른 클라우드 옵션이 이미 끝난 질문을 다시 열게 두지 마. 오디오가 기계에 남는 건 튜닝 값이 아니라 제품 자체니까.
첫날부터 벤치마크 도구를 하나 둬. 후보 엔진이 넷이고 각각 모델이 여러 개면 '뭐가 더 빠르고 나은가' 를 감으로 못 따져. 같은 녹음 클립을 엔진마다 돌려서 전사랑 지연을 찍어 주는 작은 CLI 하나면 엔진 선택이 감이 아니라 데이터가 돼. Firekeeper 엔 이미 있어. 네 것도 하나 있어야 하고.

Code

인터페이스 하나에 귀 여럿, 엔진은 텍스트와 언어를 같이 돌려줘·swift
struct STTResult {
    let text: String
    let detectedLanguage: String   // "ko", "en", ... — 앱이 이걸로 라우팅
}

protocol STTEngine {
    var id: String { get }
    func prewarm() async            // 첫 단축키 전에 로드/컴파일
    func finalize(_ clip: AudioClip, language: LanguageMode) async throws -> STTResult
}

// WhisperKitEngine, AppleSpeechEngine, WhisperCppEngine 모두 준수.
// DictationController 는 `any STTEngine` 을 쥐고 브랜드를 절대 안 부름.

External links

Exercise

네가 지을 만한 보이스 기능을 하나 잡고, 개별 엔진을 비교하기 전에 후보 범위부터 정하는 한 문장짜리 규칙을 써봐. 그다음 그 범위 안에서 기본을 고를 때 잴 속성 두 개를 적어. 범위 규칙은 왜 못 굽히고, 기본은 왜 갈아끼울 수 있어야 할까?
Hint
범위 규칙은 제품 약속('오디오가 로컬에 남는다')을 담고 있어서 제품을 깨지 않고는 못 굽혀. 기본은 최적화야. '오늘 나한테는 WhisperKit 이 혼합 언어를 제일 빨리 처리한다' 같은. 모델이랑 OS API 가 좋아지면 갈아끼울 수 있게 남겨 둬야 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.