UserPromptSubmit 훅 — 프롬프트 가공과 기밀 정보 차단
UserPromptSubmit 훅은 사용자가 프롬프트를 전송했을 때 발화해 입력을 가공·검증할 수 있는 Hooks 이벤트입니다. stdin JSON에서 prompt를 읽는 법, stdout으로 컨텍스트를 추가하는 법, API 키를 감지해 차단하는 법을 다룹니다.
UserPromptSubmit 훅은 사용자가 Claude Code에 프롬프트를 전송했을 때 자동 실행되어, 입력을 가공·검증하거나 추가 정보를 부여할 수 있는 Hooks 이벤트입니다. 프롬프트를 보내기 전에 추가 지시를 컨텍스트에 주입하거나, 무심코 넣은 API 키를 전송 전에 차단할 수 있습니다.
SubagentStop 훅까지의 훅이 도구와 에이전트의 동작을 다뤘다면, 이 훅은 사용자 입력 자체를 다룬다는 점에서 독특한 자리에 있습니다. Hooks 코스의 마지막 글입니다.
핵심 요약
- UserPromptSubmit 훅은 사용자가 프롬프트를 전송했을 때 발화합니다.
- stdin에서 JSON 형식으로 프롬프트를 받아,
prompt필드로 사용자 입력을 취득합니다. - stdout에 출력한 내용이 컨텍스트에 추가됩니다.
- API 키 등의 기밀 정보를 감지해 차단할 수 있습니다.
- 매처(패턴 지정)를 쓰지 않고 프롬프트 전송 시마다 매번 실행됩니다.
UserPromptSubmit 훅의 역할은 무엇인가
| 역할 | 설명 |
|---|---|
| 추가 | 프롬프트에 컨텍스트나 추가 지시를 주입합니다 |
| 차단 | 기밀 정보를 감지해 전송을 차단합니다 |
Hooks의 기본 구조는 훅(Hooks)으로 동작 강제하기를 참고하세요.
stdin에서 JSON을 어떻게 받나
UserPromptSubmit 훅은 stdin(표준 입력)으로 JSON 형식의 데이터를 받습니다. stdin은 프로그램에 데이터를 넘기기 위한 입구로, 터미널에서 echo "Hello" | cat처럼 파이프로 데이터를 넘기는 것과 같은 원리입니다. Claude Code가 스크립트의 표준 입력으로 아래와 같은 JSON을 넘겨 줍니다.
{
"session_id": "abc123",
"prompt": "안녕하세요, 오늘 날씨를 알려줘",
"cwd": "/Users/username/project",
"hook_event_name": "UserPromptSubmit"
}가장 중요한 것은 prompt 필드로, 사용자가 입력한 텍스트가 여기에 들어 있습니다. 환경 변수가 아니라 JSON으로 받는 이유는 프롬프트에 줄바꿈이나 특수 문자가 들어갈 수 있어 안전하게 주고받도록 설계했기 때문입니다. Hooks 환경 변수와는 전달 경로가 다른 셈입니다.
jq로 prompt 필드 꺼내기
JSON에서 데이터를 꺼내려면 jq 명령이 편리합니다. macOS에서는 brew install jq, Ubuntu/Debian에서는 sudo apt-get install jq로 설치합니다.
# stdin에서 JSON을 읽고 prompt 필드를 추출
prompt=$(cat | jq -r '.prompt')| 부분 | 설명 |
|---|---|
cat | 표준 입력에서 데이터를 읽습니다 |
| 파이프 | 다음 명령에 데이터를 넘깁니다 |
jq -r '.prompt' | JSON에서 prompt 필드 값을 꺼냅니다 (-r은 날것 문자열로 출력) |
prompt=$(...) | 결과를 변수 prompt에 저장합니다 |
기본 설정 구조
설정은 settings.json의 hooks 섹션에 기술합니다. 설정 파일 위치와 sh 파일화 요령은 Hooks 설정 방법을 참고하세요.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "실행할 명령"
}
]
}
]
}
}| 항목 | 설명 |
|---|---|
| matcher | 매처에 대응하지 않으므로 생략합니다 |
| type | "command"를 지정해 셸 명령을 실행합니다 |
| command | 실행할 명령을 기술합니다 |
| stdout | 출력한 내용이 컨텍스트에 추가됩니다 |
먼저 간단한 예로 확인하기
Hooks 설정 방법에서 만든 ~/Desktop/hooks-test 디렉터리를 그대로 사용합니다. 아직 없다면 아래 명령으로 만들어 주세요.
mkdir -p ~/Desktop/hooks-test/.claude/hooks
cd ~/Desktop/hooks-test- 프롬프트를 로그에 기록하는 스크립트를
.claude/hooks/log-prompt.sh로 만듭니다.
#!/bin/bash
# stdin에서 JSON을 읽고 prompt 필드를 추출
prompt=$(cat | jq -r '.prompt')
# 로그 파일에 기록
echo "$(date): $prompt" >> ~/Desktop/hooks-test/prompt.log
exit 0- 스크립트에 실행 권한을 부여합니다.
chmod +x ~/Desktop/hooks-test/.claude/hooks/log-prompt.sh~/Desktop/hooks-test/.claude/settings.json에 아래 설정을 추가합니다.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "~/Desktop/hooks-test/.claude/hooks/log-prompt.sh"
}
]
}
]
}
}~/Desktop/hooks-test에서 Claude Code를 실행하고 "안녕하세요" 같은 질문을 입력한 뒤 로그 파일을 확인합니다.
cat ~/Desktop/hooks-test/prompt.log
# Tue Dec 30 08:55:07 CST 2025: 안녕하세요방금 입력한 프롬프트가 기록되어 있으면, UserPromptSubmit 훅이 프롬프트를 받아 스크립트를 실행하고 있다는 뜻입니다.
사용처 1 — 컨텍스트에 추가 지시를 주입하기
stdout에 출력한 내용은 컨텍스트에 추가되므로, 매번의 프롬프트에 "주의사항"을 부여할 수 있습니다. Claude Code에는 사용자가 선택식으로 답할 수 있는 AskUserQuestion 도구가 있는데, 모호한 지시에는 이 도구로 되묻도록 촉구하는 스크립트를 .claude/hooks/add-guardrail.sh로 만들어 봅니다.
#!/bin/bash
# stdin을 소비
cat > /dev/null
# 추가 지시를 출력
echo "사용자로부터의 프롬프트가 모호한 경우엔, AskUserQuestion 도구를 사용해 사용자에게 확인하고 나서 작업을 진행해 주세요."
exit 0**
cat > /dev/null에 대해** — Claude Code의 훅에는 method나 id를 갖지 않는 심플한 JSON 데이터가 stdin으로 전달됩니다.cat > /dev/null은 "보내진 데이터를 읽고 버리는" 처리입니다. 이걸 넣지 않으면 데이터가 버퍼에 남은 채가 되어 드물게 통신이 잘 안 되는 경우가 있습니다. 우편함에 도착한 편지를 꺼내지 않고 방치하면 다음 편지가 안 들어가는 것과 같습니다. 생략해도 동작하는 경우가 많지만, 프로토콜상 올바른 작법으로 넣어 두는 것이 무난합니다.
settings.json은 아래처럼 됩니다.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "~/Desktop/hooks-test/.claude/hooks/add-guardrail.sh"
}
]
}
]
}
}이 설정으로 사용자의 프롬프트에 더해 "모호한 경우엔 확인한다"는 가이드라인이 컨텍스트에 추가됩니다. Claude Code는 확률론적으로 동작하므로 매번 확인해 준다고는 할 수 없지만, 확인 빈도는 올라갑니다. Claude Code에 질문을 던진 뒤 Ctrl + o로 트랜스크립트를 표시하면 가이드라인이 컨텍스트에 들어간 것을 확인할 수 있습니다.
> 로그 파일을 편집해줘
⎿ UserPromptSubmit hook succeeded: 사용자로부터의 프롬프트가 모호한 경우엔, AskUserQuestion 도구를 사용해 사용자
에게 확인하고 나서 작업을 진행해 주세요.
∴ Thinking…
사용자는 "로그 파일을 편집해줘"라고 말하고 있는데, 이건 모호한 요청입니다. 어느 로그 파일을 편집하는지,
어떤 편집을 하는지가 명확하지 않습니다.
⏺ 요청이 모호하므로 확인하겠습니다.
어느 로그 파일을 편집할까요? 파일 경로를 알려 주세요.
❯ 1. 파일을 찾는다
2. 경로를 지정
3. Type something.주입된 지시를 Claude Code가 이해하고, 실제로 AskUserQuestion 도구로 되묻는 것을 확인할 수 있습니다. 팀에서 Claude Code를 쓸 때 통일된 규칙을 적용하기에 가장 추천하는 사용처입니다.
셸 스크립트를 준비하는 것이 번거롭다면 한 줄로 설정할 수도 있습니다. 추가 문자열을 늘 삽입하는 용도라면 이 정도로 충분합니다.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo '사용자로부터의 프롬프트가 모호한 경우엔, AskUserQuestion 도구를 사용해 사용자에게 확인하고 나서 작업을 진행해 주세요.'"
}
]
}
]
}
}사용처 2 — 기밀 정보를 감지해 차단하기
개발 중에 API 키나 시크릿을 프롬프트에 포함해 버리는 일이 있을 수 있습니다. 그런 정보가 Claude Code에 전송되기 전에 감지해 차단하는 스크립트를 .claude/hooks/filter-secrets.sh로 만듭니다. 직접 커스터마이즈할 때는 Claude Code에 도움을 받으면 간단히 만들 수 있습니다.
#!/bin/bash
# stdin에서 JSON을 읽고 prompt 필드를 추출
prompt=$(cat | jq -r '.prompt')
# API 키 패턴을 감지 (OpenAI 형식: sk-xxx)
if echo "$prompt" | grep -qE "sk-[a-zA-Z0-9]{20,}"; then
echo '{"decision": "block", "reason": "API 키가 포함돼 있을 가능성이 있습니다. 기밀 정보를 삭제하고 나서 다시 시도해 주세요."}'
exit 0
fi
exit 0JSON 형식으로 {"decision": "block", "reason": "..."}를 반환하면 프롬프트 전송이 차단되고 사용자에게 이유가 표시됩니다. 시험 삼아 sk-로 시작하는 더미 API 키를 프롬프트로 입력해 봅니다.
> API 키를 당신에게 알려줄게요. sk-abcdefghijklmnopqrstuvwxyz123456 입니다.그러면 Claude Code에 전송되기 전에 차단되고, 스크립트에서 설정한 메시지가 표시됩니다.
⏺ UserPromptSubmit operation blocked by hook:
API 키가 포함돼 있을 가능성이 있습니다. 기밀 정보를 삭제하고 나서 다시 시도해 주세요.
Original prompt: API 키를 당신에게 알려줄게요. sk-abcdefghijklmnopqrstuvwxyz123456 입니다.이걸로 무심코 API 키를 전송해 버리는 일이 없어집니다. 종료 코드를 쓴 차단 제어의 상세는 종료 코드로 제어를 참고하세요.
정리
- UserPromptSubmit 훅은 프롬프트 전송 시마다 발화하며, stdin JSON의
prompt필드로 사용자 입력을 받습니다. - 컨텍스트 추가: stdout에 출력한 추가 지시가 프롬프트에 주입됩니다. 팀 규칙을 통일하는 데 편리합니다.
- 기밀 정보 차단:
{"decision": "block", "reason": "..."}를 출력하면 API 키 같은 기밀 정보의 오전송을 막을 수 있습니다. - 매처는 쓰지 않으며, stdin을 쓰지 않는 스크립트도
cat > /dev/null로 입력을 소비해 두는 것이 무난합니다. - 이것으로 Hooks 코스는 끝입니다. 이벤트 전체 목록과 설계 원칙은 훅(Hooks)으로 동작 강제하기에서 다시 확인할 수 있습니다.
자주 묻는 질문
팀에 Claude Code를 도입하려면 실제 코드베이스에 맞춘 설계가 필요합니다.
무료 상담 신청