"이음새 위에 'WhisperKit' 이란 단어가 나타나면, 추상화는 이미 실패한 거야."
어댑터의 유일한 일
STT 어댑터가 할 일은 자기 엔진을 인터페이스 처럼 보이게 하는 거야. 그 이상은 없어. WhisperKit 엔 자기만의 디코딩 옵션이랑 모델 enum, 오디오 포맷 요구가 있어. whisper.cpp 는 C API 를 내밀고. SpeechAnalyzer 는 async 결과 스트림을 내밀고. 그중 어느 것도 이음새 위로 새면 안 돼. 어댑터가 자기 엔진의 별난 점을 다 삼키고 공유 계약만 내놔. 클립이랑 언어를 주면 텍스트랑 실제로 감지한 언어를 돌려준다, 그게 전부야. 컨트롤러는 any STTEngine 을 쥐고 있을 뿐 자기가 누구랑 얘기하는지 끝까지 안 배워.
누수 테스트
추상화가 진짜인지 확인하는 구체적인 방법이 있어. 이음새 위 코드를 엔진 이름으로 grep 해 봐. DecodingOptions 나 WhisperKitConfig, 모델 파일명이 DictationController 에서 나오면 추상화가 새는 거야. 컨트롤러가 모른다고 해 놓고 실은 그 디테일에 기대고 있는 거니까. 깨끗한 어댑터라면 그 단어들이 어댑터 파일 안에서만 나와. 그 위는 전부 중립 계약만 말하고.
누수 (나쁨):
컨트롤러 -> WhisperKit 의 DecodingOptions 직접 읽음 # 엔진이 위로 샘
깨끗 (좋음):
컨트롤러 -> STTEngine.finalize(clip, language) # 중립 계약
WhisperKitEngine -> DecodingOptions 내부에서 만듦 # 별난 점 흡수
계약은 하류가 필요한 걸 담아
반환 타입은 일부러 작게, 대신 빠짐없이 만들었어. 텍스트, 그리고 엔진이 감지한 언어. 왜 언어까지 실을까. 바로 다음 단계인 정리가 그게 필요하고, 그걸 아는 컴포넌트가 엔진뿐이거든. 어댑터가 언어를 떨구면 정리가 다시 감지하거나 추측해야 해. 엔진이 이미 푼 문제를 다시 푸는 거지. 좋은 계약은 하류가 필요한 걸 정확히 담고 그 이상은 안 담아. 엔진 내부 확신 점수도, 토큰 타이밍도 아니고, 앱 나머지가 굴러가는 데 필요한 두 가지 사실만.