푸시투톡 파이프라인, STT WebSocket 프로토콜, 홀드투톡 역학, 포커스 모드.
Claude Code의 음성 시스템은 푸시투톡(Push-to-Talk) 파이프라인을 통해 음성 입력을 텍스트로 변환합니다. 전체 흐름은 다음과 같습니다:
음성 기능은 세 단계의 게이트를 통과해야 활성화됩니다:
/voice 사전 검사 (5단계) — 런타임에 마이크 권한, 오디오 백엔드 가용성, 네트워크 연결, 서버 상태, 사용자 자격을 순차적으로 확인마이크 권한 요청은 사전 검사의 일부로 수행됩니다. 사용자가 처음 음성 기능을 활성화할 때 OS 수준의 권한 대화 상자가 표시되며, 이후 세션에서는 캐시된 권한이 사용됩니다.
오디오 녹음 백엔드는 플랫폼과 가용성에 따라 우선순위 체인으로 선택됩니다:
| 우선순위 | 백엔드 | 플랫폼 | 특징 |
|---|---|---|---|
| 1 | NAPI |
네이티브 애드온 지원 시 | 가장 낮은 지연 시간 |
| 2 | arecord |
Linux | ALSA 직접 접근 |
| 3 | SoX |
크로스 플랫폼 | 침묵 감지 내장 |
| 4 | Windows 네이티브 |
Windows | Win32 API 사용 |
// 녹음 백엔드 우선순위 체인
const RECORDING_BACKENDS = [
{ name: 'napi', probe: () => hasNativeAddon('audio-recorder') },
{ name: 'arecord', probe: () => raceWithTimeout(spawn('arecord', ['--version']), 150) },
{ name: 'sox', probe: () => commandExists('sox') },
{ name: 'windows', probe: () => process.platform === 'win32' },
]
arecord 프로브는 150ms 타임아웃이 있는 경쟁(race)으로 실행됩니다. arecord --version을 spawn하되, 150ms 내에 응답하지 않으면 다음 백엔드로 넘어갑니다. arecord가 행(hang)에 걸릴 수 있는 환경(예: ALSA 디바이스 없음)에서 무한 대기를 방지합니다.
SoX 백엔드는 내장 침묵 감지 기능을 활용하여 녹음 종료 시점을 자동 판별할 수 있습니다. 설정 가능한 임계값과 지속 시간으로 환경 소음과 실제 침묵을 구분합니다.
STT 서비스는 wss://api.anthropic.com/api/ws/speech_to_text/voice_stream에 WebSocket으로 연결합니다.
WebSocket 프레임은 다음 메시지 타입을 사용합니다:
KeepAlive — 연결 유지를 위한 주기적 핑Binary PCM — 원시 오디오 데이터 (16비트 PCM)CloseStream — 녹음 종료 신호TranscriptText — 중간 전사 텍스트 (서버 → 클라이언트)Endpoint — 발화 종료 감지 (서버 → 클라이언트)Error — 오류 보고 (서버 → 클라이언트)별도 STT 서비스 대신 api.anthropic.com을 사용하는 이유는 JA3 핑거프린트 호환성입니다. 엔터프라이즈 환경에서 방화벽이나 프록시가 알려지지 않은 도메인을 차단할 수 있으므로, 이미 허용된 API 도메인을 통해 STT 트래픽을 전달합니다.
finalize()는 4가지 소스에서 전사 결과를 받을 수 있으며, 가장 빠른 소스를 사용합니다:
| 소스 | 지연 시간 | 정확도 |
|---|---|---|
| 스트리밍 중간 결과 | 가장 낮음 | 중간 |
| Endpoint 이벤트 | 낮음 | 높음 |
| CloseStream 응답 | 중간 | 높음 |
| 타임아웃 폴백 | 가장 높음 | 가용한 최선 |
가장 빠른 소스를 사용하는 이유는 체감 지연 시간 최소화입니다 — 사용자가 말을 멈춘 후 텍스트가 나타나기까지의 시간이 핵심 경험 지표이며, 가장 빠른 소스가 충분히 정확합니다.
터미널은 "키 다운"과 "키 업" 이벤트를 구분하지 않습니다. 홀드투톡은 키 반복(repeat) 이벤트의 타이밍 패턴을 분석하여 키가 눌려 있는 상태를 재구성합니다.
stripTrailing()은 키 반복으로 입력된 후행 문자를 제거합니다. 전각 공백(일본어/한국어 IME)도 지원하여, 다양한 키보드 레이아웃에서 올바르게 동작합니다.
키 해제를 감지하기 위해 3단계 타이머를 사용합니다:
음성 세션은 idle, recording, processing 세 가지 상태로 동작합니다:
idle — 대기 중. 키 입력을 기다림recording — 녹음 중. 오디오를 캡처하여 WebSocket으로 스트리밍processing — 처리 중. 녹음 종료 후 최종 전사 결과를 기다림상태 전환은 비동기 작업을 await하기 전에 동기적으로 수행됩니다. 이는 경합 조건을 방지합니다 — await 사이에 다른 이벤트가 상태를 변경할 수 있으므로, 먼저 상태를 전환한 후 비동기 작업을 시작합니다.
무음으로 판단하여 오디오를 드롭하는 조건은 6가지입니다:
무음으로 드롭된 오디오가 실제로는 유효한 발화였을 수 있습니다. 리플레이는 32KB 슬라이스로 오디오를 재전송하며, 각 슬라이스 사이에 250ms 백오프를 적용하여 서버 부하를 제한합니다.
리플레이용 오디오 버퍼는 크기가 제한됩니다. 제한 없는 버퍼는 장시간 녹음 시 메모리를 소진할 수 있으므로, 최근 N초분의 오디오만 유지합니다.
| 행동 | 홀드투톡 | 포커스 모드 |
|---|---|---|
| 녹음 시작 | 키를 누르고 있는 동안 | 토글 키로 시작 |
| 녹음 종료 | 키 해제 시 | 5초 침묵 타이머로 자동 종료 |
| 손 자유도 | 한 손이 키에 고정 | 양손 자유 |
| 정밀 제어 | 높음 — 즉시 종료 가능 | 낮음 — 침묵 감지에 의존 |
| 긴 발화 | 손이 피로해질 수 있음 | 적합 |
| 환경 소음 민감도 | 낮음 — 의도적 키 조작 | 높음 — 소음이 침묵 감지를 방해 |
포커스 모드에서는 5초간 침묵이 지속되면 녹음이 자동 종료됩니다. 이는 사용자가 말을 멈추고 생각하는 짧은 멈춤과 실제 발화 종료를 구분하기 위한 임계값입니다.
normalizeLanguageForSTT()는 다양한 로케일 형식을 BCP-47 표준으로 정규화합니다. 사용자 시스템의 로케일 설정이 ko_KR.UTF-8, ko-KR, Korean 등 다양한 형식일 수 있으므로, STT 서버가 요구하는 표준 형식으로 통일합니다.
getVoiceKeyterms()는 3가지 소스에서 최대 50개의 핵심 용어를 수집합니다:
이 핵심 용어는 STT 서버에 전달되어 도메인 특화 용어의 인식 정확도를 높입니다.
splitIdentifier()는 코드 식별자를 단어로 분리합니다:
camelCase → ["camel", "Case"]kebab-case → ["kebab", "case"]snake_case → ["snake", "case"]분리된 단어는 핵심 용어로 STT 서버에 전달되어, "카멜케이스"라고 발음해도 camelCase로 올바르게 인식되도록 돕습니다.
오디오 레벨은 16비트 PCM 샘플에서 RMS(Root Mean Square)로 계산됩니다. 각 오디오 프레임의 샘플 값을 제곱하여 평균을 구한 후 제곱근을 취합니다.
RMS 값은 제곱근 곡선으로 변환되어 시각화에 사용됩니다. 선형 매핑보다 제곱근 곡선이 인간의 음량 인식에 더 가깝습니다 — 조용한 소리의 변화가 더 뚜렷하게 표시되고, 큰 소리의 변화는 압축됩니다.
터미널에 16개의 바로 실시간 파형이 표시됩니다. 각 바의 높이는 해당 주파수 대역의 에너지를 반영하며, 녹음 중 시각적 피드백을 제공합니다.
/voice 사전 검사)를 모두 통과해야 음성 기능이 활성화finalize()는 4가지 소스 중 가장 빠른 전사 결과를 사용하여 체감 지연 시간을 최소화getVoiceKeyterms()는 3소스에서 최대 50개 용어를 수집하여 도메인 특화 STT 정확도를 향상Q1. 음성 시스템의 3계층 게이트 순서는?
/voice 사전 검사 5단계로 런타임 조건을 점검합니다.Q2. arecord 사용 가능 여부 판단 시 150ms 경쟁을 하는 이유는?
arecord --version은 ALSA 디바이스가 없거나 잘못 구성된 환경에서 행에 걸릴 수 있습니다. 150ms 타임아웃으로 무한 대기를 방지하고 다음 백엔드로 폴백합니다.Q3. finalize()가 4가지 소스 중 가장 빠른 것을 사용하는 이유는?
Q4. 홀드투톡 워밍업이 2번째 누르기부터 시작하는 이유는?
Q5. 포커스 모드와 홀드투톡의 가장 큰 차이는?