
Claude Code의 후크(Hook)로 경고를 표시하고 싶다면 additionalContext가 아니라 systemMessage를 사용하라
요약
Claude Code의 SessionStart 후크를 사용하여 특정 실행 환경을 강제하는 방법을 다룹니다. additionalContext 대신 systemMessage를 사용해야 경고 메시지가 확률적이지 않고 터미널에 즉시 표시됨을 설명합니다.
핵심 포인트
- additionalContext는 대화 컨텍스트에 주입되어 Claude의 응답에 따라 출력 여부가 결정됨
- systemMessage는 Claude의 응답을 거치지 않고 터미널에 직접 표시되어 재현성이 높음
- 특정 에일리어스(ccx) 사용 여부를 감지하여 사용자에게 경고를 띄우는 실용적인 팁 제공
무엇이 일어났는가
Claude Code에는 claude --add-dir를 통해 특정 디렉토리를 추가로 읽어오며 실행하는 ccx라는 실행 에일리어스(Alias)가 준비되어 있다 (cortex라는 대화 전용 리포지토리에 항상 접근 가능하게 만들기 위한 에일리어스다). 이 에일리어스를 거치지 않고 실수로 일반 claude로 실행하는 케이스를 감지하기 위해, SessionStart 후크(세션 시작 시 발화하며, 표준 출력으로 반환한 JSON 내용으로 Claude Code 측의 동작에 간섭할 수 있는 후크)를 통해 경고를 띄우는 메커니즘을 만들었다. 그런데 이것이 처음에는 "경고가 뜨기도 하고 안 뜨기도 하는" 재현성 없는 상태가 되었다.
원인은 후크의 출력 필드 선택에 있었다. 같은 "메시지를 표시한다"는 목적이라도, hookSpecificOutput.additionalContext와 systemMessage는 동작이 완전히 다르다.
additionalContext만으로는 부족했다
최초 구현: ccx를 거쳐 실행하면 환경 변수 CCX_SESSION이 설정되므로, 이것이 설정되어 있지 않으면 경고를 띄우면 된다. 처음에는 이렇게 구현했다.
#!/bin/bash
if [[ -z "$CCX_SESSION" ]]; then
cat <<'EOF'
...
이것으로 완성되어야 했지만, 실제로 사용하면 경고가 나오지 않는다. --debug-file로 확인해보니 후크 자체는 올바르게 발화하고 있었으므로, 문제는 "표시" 쪽에 있었다.
additionalContext는 대화 컨텍스트(Context)에 주입될 뿐이다
원인: hookSpecificOutput.additionalContext는 Claude의 대화 컨텍스트에 주입될 뿐인 메커니즘이라, Claude가 실제 응답에서 이를 언급할지 여부는 보장되지 않는다. 즉, 확률적이다. 실제로 같은 메커니즘과 같은 메시지임에도 불구하고, cortex 디렉토리에서는 언급되지만 ~/workspace/thinking라는 다른 디렉토리에서는 언급되지 않는 재현성 없는 현상까지 확인했다.
게다가 실행 직후 사용자가 아무런 발언을 하지 않으면 Claude는 아무 말도 하지 않는다. "실행하자마자 경고가 나온다"고 생각했지만, 실제로는 처음에 무언가 질문을 해야 그 답변 과정에서 처음으로 언급되는 구조였다.
순간 "재현성이 없다면 경고가 없어도 괜찮지 않을까"라고도 생각했지만, 이는 후술하듯이 잘못된 판단이었다.
systemMessage를 확실하게 표시하려면
Claude에게 claude-code-guide 서브 에이전트(공식 문서 조사용 서브 에이전트)를 통해 공식 hooks 문서를 조사하게 하고, WebFetch로도 교차 검증한 결과, 모든 이벤트 공통으로 사용할 수 있는 systemMessage 필드가 있다는 것을 알게 되었다. 이것은 Claude의 응답을 거치지 않고, 후크 실행 시 사용자 터미널에 직접 표시된다.
#!/bin/bash
if [[ -z "$CCX_SESSION" ]]; then
cat <<'EOF'
...
(additionalContext 측의 문구는 원래대로 남겨두었으므로 "..."는 생략 기호이다). settings.json 측의 등록은 바꾸지 않았다.
"SessionStart": [
{ "matcher": "", "hooks": [{ "type": "command", "command": "~/.claude/hooks/warn-if-not-ccx.sh" }] }
]
대화형으로 실행하여 확인한 결과, 확실히 경고가 표시되었다. ccx를 사용해야 하는 상황인지 여부는 스스로 의외로 잊기 쉬운데, 재현성 있는 방식으로 대응할 수 있게 되면서 이 대책은 처음으로 실용적인 수준이 되었다.
systemMessage는 둘 다 표시되는가
여러 후크를 사용할 때 또 다른 의문이 생겼다. 여러 후크가 각각 systemMessage를 반환할 경우, 둘 다 표시되는가, 아니면 나중에 실행된 쪽으로 덮어씌워지는가. 공식 문서에는 additionalContext는 "여러 개가 있으면 전부 Claude에게 전달된다"고 되어 있지만, systemMessage의 집약(Aggregation) 사양은 명시되어 있지 않았다.
명시되어 있지 않은 이상, 직접 확인하는 수밖에 없다. 테스트용 후크(Hook)를 하나 더 추가하여 실지 검증을 진행한 결과, 두 개 모두 화면에 표시되었으며 서로 덮어쓰여지는 일은 없었다. 검증 후에는 테스트 후크를 삭제하고, chezmoi diff (dotfiles 관리 도구 chezmoi의 차분 확인 명령어)로 차분이 0임을 확인한 뒤 뒷정리를 마쳤다.
요약
SessionStart 후크에서 메시지를 표시하는 수단은 두 가지가 있다. additionalContext는 어디까지나 Claude에게 문맥(Context)을 제공하기 위한 용도이며, 사용자에게 보일지 여부는 Claude의 응답에 달려 있다. 반면 systemMessage는 모든 이벤트에 공통적으로 적용되며, 확실하게 사용자 터미널에 표시된다. "경고를 확실하게 보여주고 싶다"면 후자(systemMessage)를 사용해야 한다는 것이 이번의 결론이다.
여러 개의 후크가 systemMessage를 반환할 경우에도 모두 표시된다는 것을 실지 검증을 통해 확인했다. 문서에 명시되지 않은 부분은 테스트용 후크를 일시적으로 추가하여 실행해 보는 것이 가장 빠르다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기