Claude Code를 위한 Text-to-Speech — 에이전트가 무엇을 하고 있는지 들어보세요
요약
Claude Code의 답변을 음성으로 들을 수 있도록 TTS(Text-to-Speech) 기능을 추가하는 개인적인 해킹 방법을 소개합니다. Hooks 기능을 활용하여 에이전트의 작업 완료나 알림을 OS 기본 음성 명령으로 출력하는 레시피를 제공합니다.
핵심 포인트
- Claude Code의 Hooks 기능을 활용한 TTS 구현
- Notification 훅을 이용한 사용자 주의 알림 자동화
- macOS, Linux, Windows OS별 음성 명령 설정 방법
- Stop 훅을 통한 에이전트 답변 음성 출력 가능성
Claude Code는 이미 당신의 말을 들을 수 있습니다. /voice를 실행하면 푸시 투 토크 (push-to-talk) 받아쓰기 기능을 사용할 수 있습니다. 즉, 당신이 말하면 이를 프롬프트로 전사(transcribe)합니다 (docs). 하지만 Claude Code가 대답을 하는 기능은 없습니다. 긴 작업을 실행해 두면, 터미널을 계속 지켜보거나 작업이 완료되거나 질문을 던지는 순간을 놓치게 됩니다.
그래서 저는 나머지 절반인 텍스트 음성 변환 (Text-to-Speech, TTS) 기능을 추가했습니다. 후크 (hook)가 에이전트의 답변을 소리 내어 읽어줍니다. 저는 다른 방에 있어도 "완료되었습니다, 테스트를 통과했습니다" 또는 "여기서 결정이 필요합니다"라는 소리를 들을 수 있습니다. 이 포스트는 두 부분으로 구성됩니다. 누구나 자신의 설정에 붙여넣을 수 있는 간단한 레시피와, 책상 앞에 있지 않을 때를 위해 동일한 아이디어를 저의 자체 도구에 연결한 방법입니다.
이것은 개인적인 해킹(hack)이며, Claude Code의 공식 기능이 아닙니다. 에이전트가 멈춘 후 짧은 텍스트를 소리 내어 읽어줄 뿐입니다. 그게 전부입니다. 호출어 (wake words)도 없고, 대화도 없으며, 코드 블록을 읽지도 않습니다 (코드 블록을 읽는 것은 원치 않으실 겁니다).
레시피: 후크 + 운영체제(OS) 음성 명령
Claude Code hooks는 라이프사이클 이벤트(lifecycle events) 발생 시 셸 명령어를 실행합니다. 여기서 중요한 두 가지는 다음과 같습니다:
- Stop — 에이전트가 응답을 마쳤을 때 실행됩니다. 표준 입력 (stdin)을 통해 대화 전사(transcript) 파일의 경로를 전달받습니다.
- Notification — Claude Code가 사용자의 주의를 필요로 할 때 (권한 요청, 유휴 상태 알림 등) 실행됩니다. 표준 입력 (stdin)을 통해
message필드에 알림 텍스트를 전달받습니다.
Notification이 가장 간단하게 적용할 수 있는 방법이므로, 거기서부터 시작하세요. 모든 운영체제(OS)에는 음성 명령이 포함되어 있습니다: macOS의 say, Linux의 spd-say 또는 espeak-ng, 그리고 Windows의 한 줄 PowerShell 호출입니다.
다음은 메시지를 말해주는 Notification 후크입니다. 이를 ~/.claude/settings.json에 넣으세요:
{
"hooks": {
"Notification": [
...
jq가 표준 입력 (stdin)의 JSON에서 message 필드를 읽고, say (macOS)가 파이프(piped)로 전달된 텍스트를 소리 내어 읽습니다. Linux에서는 say를 spd-say -e 또는 espeak-ng로 바꾸면 되며, 두 명령어 모두 표준 입력 (stdin)을 읽을 수 있습니다. Windows에서는 명령어를 PowerShell로 지정하세요:
"command": "jq -r '.message // empty' | powershell -Command \"Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak([Console]::In.ReadToEnd())\"
이것으로 "주의가 필요함 (needs your attention)" 케이스에 대한 설명은 끝났습니다. 만약 에이전트가 실제 답변 내용까지 읽어주길 원한다면, Stop 훅 (Stop hook)을 추가하세요. 여기서 발생하는 까다로운 점은, Stop 훅은 텍스트가 아닌 트랜스크립트 (transcript) 경로를 제공한다는 것입니다. 트랜스크립트는 JSONL (한 줄당 하나의 JSON 객체) 형식이므로, 거기서 마지막 어시스턴트 (assistant) 텍스트 블록을 추출해야 합니다:
{
"hooks": {
"Stop": [
...
이 부분이 다소 거칠 수 있으므로, 몇 가지 솔직한 주의 사항을 말씀드립니다:
- 길이 제한.
head -c 600을 사용하면say명령어가 4 KB 크기의 상태 보고서를 끊임없이 읽어대는 것을 방지할 수 있습니다. 본인만의 제한 값을 선택하세요. - 가능하다면 마크다운 (markdown)을 제거하세요. 소리 내어 읽을 때, 코드 펜스 (code fences)와 URL은 소음일 뿐입니다. 위의 레시피는 이를 제거하지 않습니다. 한 줄짜리 명령어(one-liner)로서는 용인할 만하지만, 제대로 된 버전이라면 이를 제거해야 합니다.
- 트랜스크립트 구조는 안정적인 공개 규약 (public contract)이 아닙니다. 위의
jq필터는 현재의 JSONL 레이아웃에 맞춰져 있습니다. 만약 Claude Code가 이 구조를 변경하면 필터는 작동하지 않습니다. 이를 API가 아닌 해킹 (hack)으로 취급하세요.
대부분의 사용자에게는 Notification 훅 (Notification hook)만으로도 충분하며, 이 방식이 고장 날 가능성도 가장 낮습니다.
t3 추가 기능: 음성 설정 (speak settings)
저는 저의 Claude Code 자동화 기능을 teatree라는 프로젝트에서 관리하고 있습니다. 여기에는 하나의 [teatree.speak] 테이블로 구동되는 t3 speak 명령어가 있습니다:
[teatree.speak]
local = "dm" # 이 기기의 스피커에서 재생될 내용: "dm" | "all" | "off"
slack = true # 각 봇→사용자 Slack DM에 음성 오디오 파일 첨부
local은 눈앞의 스피커를 제어합니다: dm은 봇의 DM만 읽어주고, all은 모든 에이전트의 턴 (turn)을 소리 내어 읽으며, off는 무음 상태입니다. slack은 각 봇→사용자 DM에 음성 오디오 파일을 첨부합니다. 이 두 설정은 독립적이며 둘 다 기본값은 off이므로, 구성하기 전까지는 아무런 동작도 하지 않습니다.
두 가지 목적지가 있는 이유는 제가 있는 두 가지 장소 때문입니다. 책상 앞에 있을 때, local 설정은 DM(Direct Message)이 도착하는 즉시 스피커를 통해 재생되므로 클릭할 필요가 없습니다. 책상에서 떨어져 있을 때 제가 사용하는 것은 slack입니다. 음성 텍스트가 DM에 첨부된 오디오 파일로 전달되며, 휴대폰에서 재생 버튼을 누릅니다. 핸즈프리(Hands-free) 방식은 아니지만, 읽기 위해 멈추는 대신 이동하면서 들을 수 있습니다.
두 가지 운영상의 참고 사항이 있습니다. 음성은 macOS의 say 명령어를 통해 출력됩니다. 그리고 slack 설정은 봇의 파일 업로드 권한(file-upload permission)이 필요하므로, 기존 봇이 있다면 권한 부여를 위해 한 번 재설치해야 합니다.
현재 상태
훅 레시피(hook recipe)는 실제로 시도해 보라고 권장하고 싶은 부분입니다. 몇 줄 되지 않으며, 기능이 제한되더라도 자연스럽게 동작(degrades gracefully)합니다. Teatree 측면의 구현은 저의 개인적인 설정에 종속되어 있으므로, 그대로 복사하기보다는 동일한 아이디어를 구조화하는 한 가지 방법으로 참고하시기 바랍니다.
얼마나 많은 양을 소리 내어 읽게 할지는 여전히 고민 중입니다. local = "all"로 설정하면 금방 수다스러워집니다. dm은 더 차분하지만 내용을 놓칠 수 있습니다. 만약 이것을 시도해 보신다면, 어떤 임계값(threshold)이 귀하에게 적합할지 궁금합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기