음성으로 코딩하기 (2편) — 듣기는 쉬운데 왜 말하기가 어려울까

지난 편에서 "입력이 문제였다"고 했죠. 정확히 말하면, 듣기(출력)는 하루 만에 풀렸는데 말하기(입력)에서 몇 주를 썼습니다. 왜 그랬는지 오늘 풀어볼게요.

1편을 안 보셨다면 먼저 보고 오시면 좋아요 → 음성으로 코딩하기 (1편) — 허리 때문에 마우스를 놓았다

출력부터 풀렸어요 — MCP라는 열쇠

Claude Code에는 MCP(Model Context Protocol) 라는 게 있어요. 쉽게 말하면 Claude가 외부 도구를 직접 호출할 수 있게 해주는 규격이에요. "파일 읽어", "명령 실행해" 같은 걸 Claude가 스스로 부르는 거죠.

여기서 아이디어가 나왔어요. "말하기(speak)"라는 도구를 하나 만들어서, Claude가 답할 때마다 그걸 부르게 하면? 그러면 Claude의 답이 자동으로 음성이 됩니다.

실제로 이건 놀랄 만큼 깔끔했어요. speak 도구를 MCP로 등록하고 "매 응답마다 이 도구로 읽어줘"라고 약속하니, Claude가 알아서 자기 답을 음성으로 내보냈습니다. 출력은 이렇게 하루 만에 됐어요.

MCP를 알고 나니 입력도 되겠거니 했죠

"출력이 이렇게 쉬웠으니 입력도 MCP로 되겠지?" 싶었어요. 그런데 여기서 벽에 부딪혔습니다.

MCP는 "Claude가 도구를 부르는" 방향이에요. 즉 Claude → 바깥은 되는데, 제가 원한 건 반대였죠. 내 목소리 → Claude의 입력창. 이건 MCP가 다루는 영역이 아니었어요. Claude Code(VS Code 확장) 입력창은 제가 프로그램으로 글자를 꽂아넣을 공개된 통로가 없었거든요.

이게 제가 "비대칭"이라고 부르는 지점이에요.

방향방식난이도
출력 (Claude → 나)MCP speak 도구쉬움 — Claude가 스스로 호출
입력 (나 → Claude)???어려움 — 입력창에 넣을 통로가 없음

기술 스택은 이렇게 골랐어요

입력을 우회하기 전에, 음성을 텍스트로 바꾸는 부분부터 정리했어요.

역할선택이유
STT (음성→텍스트)faster-whisper로컬에서 돌고, 정확하고, GPU로 빠름
VAD (발화 감지)Silero VAD버튼 없이 말하기 시작/끝을 자동 감지 — hands-free의 핵심
TTS (텍스트→음성)edge-tts (한국어)Piper엔 쓸 만한 한국어 음성이 없어서

특히 VAD가 1편에서 말한 "hands-free"의 정체예요. push-to-talk처럼 버튼을 누르는 게 아니라, Silero VAD가 "지금 말하는 중"인지를 스스로 판단해줘서 손을 완전히 뗄 수 있게 해줍니다.

결국 입력은 우회로였어요

입력창에 직접 못 넣으니, 사람이 손으로 하는 걸 그대로 자동화했어요.

  1. 음성을 텍스트로 바꾸고 (STT)
  2. 그 텍스트를 클립보드에 복사하고 (pyperclip)
  3. 대상 창을 찾아서 포커스를 주고 (pygetwindow)
  4. Ctrl+V + Enter를 자동으로 누른다 (pyautogui)

말로 쓰면 네 줄인데, 여기 이후 편들에 걸쳐 풀 함정들이 다 숨어 있었어요. (오인식이 그대로 전송되는 걸 막는 "승인 단계"도 이 사이에 들어갑니다.)

이 "비대칭"이 VoxPlug 설계의 핵심이에요

돌아보면 VoxPlug의 절반은 MCP가 거저 풀어줬고, 나머지 절반은 이 우회로를 튼튼하게 만드는 싸움이었어요. AI(Claude)가 MCP로 출력은 우아하게 해결해줬지만, "내 목소리를 남의 입력창에 꽂는" 문제는 결국 제가 직접 부딪혀야 했죠.

그리고 그 우회로의 첫 관문은 엉뚱하게도… 마이크였습니다.

다음 편

블루투스 마이크의 음질이 인식률의 천장이었던 이야기. 데이터로 −0.64가 −0.12로 바뀌기까지. → (3편 — 마이크 지옥①, 게시 후 링크 추가 예정)


(이전 편: 1편 — 허리 때문에 마우스를 놓았다)

댓글