목차
- 상황별로 어떤 훅을 쓸까
- 준비물
- 1단계 — 채널 두 개 만들기
- 2단계 — 훅 추가하기
- 3단계 — 실제 요청을 기다리지 않고 테스트하기
- 4단계 — 차단 알림 채널을 울리게 만들기
- 알림 과부하 피하기
- 원격 환경이나 CI에서 Claude Code 실행하기
- 문제 해결
- 자주 묻는 질문
- Claude Code가 권한을 요청할 때 알림을 받으려면 어떻게 하나요?
- Claude Code가 작업을 끝낸 걸 어떻게 알 수 있나요?
- 방해 금지 모드를 뚫고 알림이 올 수 있나요?
- 웹에서 쓰는 Claude Code나 원격 세션에서도 되나요?
- 웹훅 URL을 settings.json에 넣어도 안전한가요?
- 마무리
- 관련 글
Claude Code가 멈추는 중요한 순간은 두 가지입니다. 권한 요청이나 질문처럼 여러분의 응답을 기다리며 막혀 있을 때, 그리고 작업을 끝냈을 때입니다. 터미널을 보고 있지 않으면 두 상황은 똑같아 보입니다. 아무 일도 일어나지 않고, 여러분이 돌아올 때까지 세션은 그대로 멈춰 있습니다. 해결책은 훅입니다. settings.json에 몇 줄만 추가하면, 둘 중 하나가 발생하는 즉시 휴대폰으로 웹훅이 날아갑니다.
이 글에서는 복사해서 쓸 수 있는 설정을 드리고, 상황별로 어떤 훅 이벤트를 써야 하는지 설명합니다. 또 안정적으로 동작시키는 데 필요한 세부 사항, 즉 훅 입력 읽기, 셸 따옴표 처리, 알림 폭주 막기까지 다룹니다.
상황별로 어떤 훅을 쓸까
Claude Code는 라이프사이클 이벤트마다 훅을 실행합니다. 이 문제는 그중 두 가지로 해결됩니다(훅 레퍼런스).
| 알고 싶은 상황 | 훅 이벤트 | Matcher / 필터 |
|---|---|---|
| Claude가 권한 결정을 기다릴 때 | Notification | permission_prompt |
| Claude가 한동안 대기하며 여러분의 입력을 기다릴 때 | Notification | idle_prompt |
| Claude가 응답을 마쳤을 때 | Stop | 필요 없음 |
Notification 훅의 matcher는 알림 유형으로 걸러 주므로, 어떤 알림을 휴대폰까지 보낼지 정확히 고를 수 있습니다. 입력은 stdin으로 JSON 형태로 들어오며 notification_type, message, cwd, session_id가 들어 있습니다. Notification 훅은 아무것도 막거나 바꿀 수 없는 관찰자일 뿐이고, 이번 목적에는 오히려 그게 딱 맞습니다.
Stop 입력에는 Claude가 마지막으로 작성한 텍스트인 last_assistant_message가 들어 있습니다. 알림에 쓸 요약 한 줄이 공짜로 생기는 셈입니다.
준비물
- 휴대폰에 설치된 Echobell (App Store / Google Play)
- Claude Code를 실행하는 머신의
curl과jq - 2분
1단계 — 채널 두 개 만들기
모든 알림을 한 채널로 보내고 싶은 유혹을 참으세요. Echobell에서 다음 두 채널을 만듭니다.
- Claude Blocked, 유형은 시간 민감 또는 전화 — 권한 요청용입니다. 에이전트가 멈춰 있으면 1분마다 컴퓨팅 자원과 시간이 낭비됩니다.
- Claude Done, 유형은 일반 — 평범한 푸시라서 언제 봐도 괜찮습니다.
각 채널의 웹훅 URL을 복사하세요. https://hook.echobell.one/t/<channel-token> 같은 형태입니다. 비밀번호처럼 다루세요. 이 URL을 아는 사람은 누구나 여러분의 휴대폰을 울릴 수 있습니다.
두 URL을 환경 변수로 저장해 두면 커밋되는 설정 파일에 들어가지 않습니다.
export ECHOBELL_CLAUDE_BLOCKED_URL="https://hook.echobell.one/t/<blocked-token>"
export ECHOBELL_CLAUDE_DONE_URL="https://hook.echobell.one/t/<done-token>"
Claude Code가 상속받을 수 있도록 셸 프로필에 넣어 두세요.
2단계 — 훅 추가하기
모든 프로젝트에 적용하려면 ~/.claude/settings.json에, 한 저장소에만 적용하려면 .claude/settings.local.json에 다음을 추가합니다.
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "jq -c '{title: \"Claude 권한 요청\", body: .message, project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_BLOCKED_URL\""
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "jq -c '{title: \"Claude 작업 완료\", body: (.last_assistant_message // \"완료\" | .[0:200]), project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_DONE_URL\""
}
]
}
]
}
}
각 부분이 하는 일은 다음과 같습니다.
jq -c '{...}'는 훅의 JSON을 stdin으로 읽어 작은 페이로드를 만듭니다. 그래서 Echobell은 훅 문서 전체가 아니라 원하는 필드만 받습니다..cwd | split("/") | last는/home/me/code/api를api로 바꿔 주므로, 어느 프로젝트에서 보낸 알림인지 바로 알 수 있습니다..[0:200]은 마지막 메시지를 잘라 줍니다. Echobell 워커는 알림 본문을 1500바이트로 제한하고, 잠금 화면에는 어차피 훨씬 적게 표시됩니다.-m 10은curl에 10초 타임아웃을 줍니다. 네트워크에 문제가 생겨도 Claude Code가 훅을 기다리며 멈추는 일이 없습니다.
Echobell은 보낸 키를 채널 템플릿으로 렌더링하는데, 새 채널에는 이 키를 읽는 템플릿이 없습니다. 채널 설정에서 제목 템플릿을 {{title}}, 본문 템플릿을 {{body}}로 지정하세요. 프로젝트 이름도 보이게 하려면 {{title}} ({{project}})처럼 둘 중 하나에 {{project}}를 추가하면 됩니다.
3단계 — 실제 요청을 기다리지 않고 테스트하기
가짜 훅 페이로드로 같은 파이프라인을 직접 실행해 보세요.
echo '{"message":"Claude에게 Bash 사용 권한이 필요합니다","cwd":"/home/me/code/api","notification_type":"permission_prompt"}' \
| jq -c '{title: "Claude 권한 요청", body: .message, project: (.cwd | split("/") | last)}' \
| curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- "$ECHOBELL_CLAUDE_BLOCKED_URL"
휴대폰이 울린다면 Echobell 쪽은 정상이며, 남은 문제는 Claude Code가 설정을 읽는 방식에 있습니다. Claude Code에서 /hooks를 실행해 훅이 로드되었는지 확인하세요. 세션이 실행 중일 때 바꾼 설정은 hooks 메뉴를 거치거나 재시작해야 적용될 수 있습니다.
4단계 — 차단 알림 채널을 울리게 만들기
푸시 알림은 방해 금지 모드를 이기지 못합니다. 전화 채널은 걸려 오는 전화처럼 동작해서 iOS 집중 모드를 뚫고 들어오며, 긴 작업을 맡기고 저녁에 자리를 비울 때 특히 중요합니다. 일부 요청에만 방해받고 싶다면 훅을 고치지 말고, 보내는 변수에 대해 Echobell 채널 조건(예: project == "api")으로 필터링하세요. 사용 가능한 변수 전체는 조건 레퍼런스에서 확인할 수 있습니다.
알림 과부하 피하기
바쁜 세션에서는 Stop 이벤트가 아주 많이 발생합니다. 응답이 끝날 때마다 하나씩 발생하니까요. 신호만 남기는 방법은 세 가지입니다.
Stop을 울리는 채널로 보내지 마세요. 완료는 급한 일이 아닙니다. 일반 채널에 두세요.Stop대신idle_prompt를 쓰세요. 응답이 끝날 때마다가 아니라, Claude가 멈추고 여러분을 기다릴 때만 알고 싶다면 이쪽이 맞습니다.- 조건으로 프로젝트를 좁히세요. 임시로 쓰는 저장소는 아예 알림이 오지 않게 할 수 있습니다.
원격 환경이나 CI에서 Claude Code 실행하기
클라우드나 원격 환경에서도 같은 방식이 통합니다. 훅은 그저 바깥으로 나가는 HTTPS 요청이기 때문입니다. 인터넷에서 접근 가능해야 하는 것은 아무것도 없습니다. 셸 프로필 대신 해당 환경의 시크릿에 두 환경 변수를 설정하면, 세션이 어디서 실행되든 알림이 휴대폰에 도착합니다.
문제 해결
아무것도 도착하지 않고 수동 테스트도 실패합니다. Claude Code를 실행하는 셸에 URL 변수가 설정되어 있는지 확인하세요: echo "$ECHOBELL_CLAUDE_BLOCKED_URL". 변수가 비어 있으면 curl이 조용히 실패합니다.
수동 테스트는 되는데 훅이 실행되지 않습니다. matcher를 확인하세요. permission_prompt와 idle_prompt는 서로 다르며, 일치하지 않는 matcher는 그냥 실행되지 않습니다. /hooks를 실행해 JSON이 올바른지 확인하세요. 끝에 쉼표가 하나만 남아 있어도 해당 설정 파일의 훅이 전부 비활성화됩니다.
알림 본문이 비어 있습니다. jq 필터가 해당 이벤트에 없는 필드를 참조한 경우입니다. message는 Notification 입력에, last_assistant_message는 Stop 입력에 있습니다. 위 설정처럼 //로 기본값을 지정하세요.
너무 자주 울립니다. 위 섹션을 참고하세요. Stop에서 idle_prompt로 바꾸거나 조건을 추가하세요.
자주 묻는 질문
Claude Code가 권한을 요청할 때 알림을 받으려면 어떻게 하나요?
matcher가 permission_prompt인 Notification 훅을 추가해서 채널로 웹훅을 보내세요. Echobell이 그 요청을 휴대폰의 푸시 또는 전화형 알림으로 바꿔 줍니다.
Claude Code가 작업을 끝낸 걸 어떻게 알 수 있나요?
Stop 훅을 추가하세요. Claude가 응답을 마치면 실행되고, 입력에 들어 있는 last_assistant_message를 알림 본문으로 쓸 수 있습니다.
방해 금지 모드를 뚫고 알림이 올 수 있나요?
네, 받는 채널이 전화 채널이라면 가능합니다. 걸려 오는 전화처럼 동작하기 때문에 iOS 집중 모드도 통과합니다. 다른 앱의 일반 푸시는 그렇지 못합니다.
웹에서 쓰는 Claude Code나 원격 세션에서도 되나요?
네. 훅은 바깥으로 나가는 HTTPS 요청만 보내므로, 세션이 인터넷에 연결되는 곳이라면 어디서든 동작합니다. 채널 URL은 환경의 시크릿으로 전달하세요.
웹훅 URL을 settings.json에 넣어도 안전한가요?
위에서 본 것처럼 환경 변수를 쓰는 편이 좋습니다. 토큰이 저장소에 커밋되지 않기 때문입니다. 유출되었다면 새 채널을 만들고 기존 채널은 폐기하세요.
마무리
채널 두 개, 훅 두 개, 그리고 jq | curl 파이프라인이면 충분합니다. permission_prompt에는 전화 또는 시간 민감 채널을, Stop에는 일반 채널을 연결하세요. 이제 긴 에이전트 작업을 시작해 놓고 자리를 떠나도, 에이전트가 정말 여러분을 필요로 할 때만 불려 오게 됩니다.
iPhone용 Echobell 다운로드 또는 Google Play에서 받기 후, 긴 작업을 맡기기 전에 위의 수동 테스트부터 실행해 보세요.