Apple 개발을 위한 Hooks: 프로젝트를 살리는 패턴
iOS 프로젝트를 향한 Claude Code 세션은 일반적인 Python이나 웹 프로젝트에는 없는 접근 범위를 가집니다. 에이전트는 Bash 도구를 통해 xcodebuild와 xcrun을 실행할 수 있습니다. .pbxproj 파일(기본적으로는 구식 ASCII 속성 목록이며, 도구 변환 후에는 XML 또는 JSON 형식이 되기도 하는데, 어떤 형식이든 손상되면 치명적인 것은 마찬가지입니다)을 읽고 편집할 수 있습니다. 개발자의 머신에서 실행되기 때문에 개발자의 서명 ID도 보유합니다. 시뮬레이터를 지울 수도 있습니다. 잘못된 스킴으로 프로젝트를 다시 빌드할 수도 있습니다. 커밋하고 푸시할 수도 있습니다. 프로토콜은 이 중 어떤 것도 차단하지 않습니다. 개발자의 파일시스템이 곧 에이전트의 파일시스템이며, Claude Code의 --dangerously-skip-permissions 플래그 하나면 키 입력 한 번으로 완전 자동화에 도달할 수 있습니다.
해결책은 “에이전트를 신뢰하는 것”이 아닙니다. 해결책은 hooks입니다. 호스트가 라이프사이클 경계(PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop)에서 실행하는 결정론적 셸 스크립트입니다.1 Hooks는 위험한 입력에 대해 에이전트의 속도를 늦추고, 파괴적인 출력을 검증하며, 그린 빌드 위에서 완료를 게이팅합니다. 에이전트를 실행하는 모든 iOS 개발자가 에이전트가 무언가를 실행하기 전에 설정해야 할 핵심 안전 장치입니다.
iOS 프로젝트에서 가치를 입증하는 hook 패턴은 네 가지입니다. 이는 프로젝트별이 아니라 프레임워크 수준이며, 클러스터의 앱들(Return, Get Bananas, Reps, Water, Ace Citizenship)이 모두 이 변형을 실행합니다. 각 패턴은 실제 실패 모드, 구체적인 스크립트, 그리고 영향 범위를 제한하는 라이프사이클 이벤트를 명시합니다.
TL;DR
- iOS에서 중요한 네 가지 hook 패턴:
.pbxproj검증(PostToolUse, 에이전트에 오류 피드백), 위험한 bash 게이팅(PreToolUse, 실행 전 차단), 그린 빌드 Stop 게이트, 시뮬레이터 상태 위생(Stop). - Hook 종료 코드는 중요하며, 이벤트마다 동작이 다릅니다.
exit 2는PreToolUse에서 제안된 작업을 차단하고(도구는 절대 실행되지 않음),PostToolUse에서는 차단할 수 없지만(도구가 이미 실행됨) stderr를 에이전트에 다시 전달하여 복구하거나 되돌릴 수 있게 합니다.Stop에서는 에이전트가 세션을 종료하지 못하게 합니다.exit 0은 허용합니다.exit 1은 일반적으로 로그만 남기고 차단하지는 않습니다.1 - Hook 스크립트는 저장소의
.claude/hooks/*.sh에 위치하며,.claude/settings.json에서 상대 경로로 참조됩니다. 코드 리뷰 대상입니다. - 에이전트의 권한은 곧 개발자의 권한입니다. Hooks는 개발자가 그 권한을 의도적으로 승인된 작업 집합으로 다시 깎아내는 방법입니다.
패턴 1: 모든 편집 시 .pbxproj 검증
Xcode 프로젝트 파일은 에이전트가 정기적으로 수정하는 파일 중 라인당 영향 범위 비율이 가장 높은 파일입니다. project.pbxproj의 잘못된 괄호 하나가 팀의 모든 개발자에 대해 빌드를 조용히 망가뜨립니다. 빌드 오류는 편집 순간이 아니라 다음 xcodebuild 호출 시점에 나타나므로, 에이전트는 일반적으로 변경이 작동했다고 주장한 후에 손상이 드러납니다.
이 hook은 모든 .pbxproj 쓰기에 대해 plutil -lint를 실행합니다. PostToolUse는 쓰기 자체를 차단할 수는 없지만(hook이 발동하는 시점에는 파일이 이미 디스크에 있음), exit 2는 검증 오류를 도구 호출 실패로 에이전트에 즉시 전달합니다. 에이전트는 실패를 읽고 파일이 손상되었음을 알게 되며, 세션이 계속되기 전에 되돌리거나 복구할 수 있습니다.
#!/bin/bash
# .claude/hooks/post-write-pbxproj.sh
# Runs after every Edit or Write tool call. Exits 2 to surface the
# validation failure to the agent so it can revert/repair the broken
# .pbxproj before the session moves on. (PostToolUse cannot prevent
# the write itself; it can only feed the error back.)
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" != *.pbxproj ]]; then
exit 0
fi
if ! plutil -lint "$FILE" >/dev/null 2>&1; then
echo "ERROR: $FILE failed plutil -lint after write" >&2
echo "The Xcode project file is structurally broken. Revert and try again." >&2
exit 2
fi
exit 0
plutil -lint는 구조적 손상을 잡아냅니다. 균형이 맞지 않는 중괄호나 괄호, 누락된 세미콜론, 잘못된 plist 토큰, 손상된 XML 중첩 등입니다.2 이는 Xcode 의미론적 오류—구문적으로는 유효한 plist 텍스트지만 잘못된 형식의 UUID, 누락된 파일을 참조하는 빌드 단계—를 잡지는 못합니다. 그러한 오류는 에이전트가 평소처럼 디버깅할 수 있는 일반적인 빌드 오류를 발생시킵니다. plutil 게이트는 치명적인 파싱 실패 클래스를 잡아내며, 의미론적 오류는 빌드 자체로 흘러갑니다.
.claude/settings.json의 hook 설정(공백이 있는 경로를 위해 인용된 $CLAUDE_PROJECT_DIR에 주의):
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-write-pbxproj.sh"
}]
}]
}
}
매처는 Write와 Edit 도구 호출에서만 hook을 발동시킵니다. 스크립트의 첫 번째 작업은 .pbxproj가 아닌 경로에서 단락 처리하는 것입니다. 경로 필터가 첫 검사이기 때문에 모든 Edit에서 실행되는 비용은 무시할 만합니다.
패턴 2: 파괴적인 Bash 명령을 실행 전에 게이팅
xcrun simctl erase는 시뮬레이터의 데이터를 지웁니다. xcodebuild archive는 서명을 호출하며 개발자가 의도하지 않은 서명된 아티팩트를 생성할 수 있습니다. git push --force는 히스토리를 다시 씁니다. 에이전트는 Bash 도구를 통해 이 모두에 접근할 수 있습니다. Bash에 대한 PreToolUse hook은 제안된 명령 패턴을 매칭하고 진행 여부를 결정합니다.
형태는 다음과 같습니다.
#!/bin/bash
# .claude/hooks/pre-bash-xcode.sh
# Runs before every Bash tool call. Exits 2 (blocking) on irreversible
# Xcode/signing/git operations the developer hasn't explicitly approved.
# PreToolUse hooks CAN block: exit 2 prevents the tool from running.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
case "$COMMAND" in
*"simctl erase"*)
echo "ERROR: simulator erase requires explicit human approval" >&2
echo "Tell the developer what you wanted to erase and why; let them run it." >&2
exit 2
;;
*"xcodebuild archive"*|*"xcodebuild -exportArchive"*)
echo "ERROR: xcodebuild archive/export invokes signing; requires human approval" >&2
exit 2
;;
*"git push --force"*|*"git push -f"*)
echo "ERROR: force-push rewrites history; requires human approval" >&2
exit 2
;;
*"rm -rf"*)
echo "ERROR: rm -rf requires explicit approval" >&2
exit 2
;;
esac
exit 0
이 hook은 명령 패턴 부분 문자열에 대한 switch입니다. 차단 클래스는 되돌릴 수 없는 작업 클래스입니다. 지우기, 서명, 강제 푸시, 재귀적 삭제. 되돌릴 수 있는 작업(일반 빌드, 테스트, force 없는 git 커밋)은 통과합니다.
일반적인 개선 사항은 개발자가 환경 변수나 대화 중 플래그로 명시적으로 옵트인한 경우 일부 명령을 허용하는 것입니다. 예를 들어, xcodebuild archive는 환경에 CLAUDE_ALLOW_ARCHIVE=1이 있을 때 허용될 수 있으며, 이는 특정 아카이브 작업을 위해 세션 전에 개발자가 설정합니다. Hook은 환경을 읽고 차단을 우회합니다.
*"xcodebuild archive"*)
if [[ "${CLAUDE_ALLOW_ARCHIVE:-0}" == "1" ]]; then
exit 0
fi
echo "ERROR: xcodebuild archive requires CLAUDE_ALLOW_ARCHIVE=1" >&2
exit 2
;;
이 패턴은 되돌릴 수 없는 클래스에 대해서는 기본적으로 거부하고, 개발자가 에이전트가 처리하기를 원하는 경우에 대한 옵트인 비상 밸브를 제공합니다.
패턴 3: 그린 빌드 위에서 완료를 게이팅하는 Stop Hook
에이전트는 대화가 해결된 것처럼 보일 때 작업을 완료했다고 선언하고 싶어 합니다. 게이트가 없으면 “완료”는 “빌드가 여전히 컴파일된다”가 아니라 “파일을 편집했고 채팅이 일관된 상태에 있다”를 의미할 수 있습니다. Stop hook은 올바른 의미를 강제하는 곳입니다.
#!/bin/bash
# .claude/hooks/stop-build-check.sh
# Runs when the agent tries to stop. Exits 2 if the build is broken,
# which prevents the session from concluding until the agent fixes it.
cd "$CLAUDE_PROJECT_DIR" || exit 0
# Hard-code project, scheme, and destination per repo. Do not rely on
# auto-discovery: workspaces, multiple projects, or shared-vs-user
# schemes all break naive heuristics.
PROJECT="MyApp.xcodeproj"
SCHEME="MyApp"
DESTINATION="platform=iOS Simulator,name=iPhone 17 Pro"
LOG=/tmp/claude-stop-build.log
if ! xcodebuild -project "$PROJECT" -scheme "$SCHEME" \
-configuration Debug \
-destination "$DESTINATION" \
build > "$LOG" 2>&1; then
echo "ERROR: build failed; cannot stop with a broken build" >&2
echo "See $LOG for the full xcodebuild output." >&2
tail -50 "$LOG" >&2
exit 2
fi
exit 0
PROJECT, SCHEME, DESTINATION을 하드코딩하는 것은 저장소에 커밋되는 hook의 올바른 형태입니다. 값이 절대 표류하지 않으며, 워크스페이스와 다중 프로젝트 저장소가 머신별 조정 없이 작동하고, 같은 hook을 사용하는 CI 빌드 시스템은 환경 변수로 destination을 교체할 수 있습니다. 자동 검색(ls *.xcodeproj, xcodebuild -list | awk)은 가장 단순한 단독 사례에서는 작동하지만 .xcworkspace 기반 프로젝트, 여러 .xcodeproj 파일이 있는 저장소, 공유-vs-사용자 스킴 구분에서 실패합니다. destination 문자열은 xcodebuild의 문서화된 platform=...,name=... 구문을 따릅니다.3 개발자의 머신에 실제로 있는 시뮬레이터여야 하며, 그렇지 않으면 hook은 코드 이유가 아닌 환경 이유로 실패합니다.
이 hook이 내리는 두 가지 제품 결정:
Stop은 에이전트의 “끝났습니다” 신호를 차단하지, 사람의 신호를 차단하지 않습니다. 개발자는 언제든지 Ctrl+C를 누르거나, 터미널을 닫거나, 재정의할 수 있습니다. Hook은 에이전트의 낙관에 대한 가드레일이지, 사람을 가두는 잠금장치가 아닙니다.
Hook은 구문 검사가 아니라 실제 빌드를 실행합니다. iOS 전용 프로젝트에 대한 swift build는 iOS 전용 컴파일 단계를 건너뜁니다. 오직 xcodebuild만이 iOS 타깃이 컴파일된다는 것을 증명합니다. 비용은 빌드 시간 자체이지만(대부분의 프로젝트에서 10-60초), 가치는 매번 빌드가 깨진 채로 완료된 사례를 잡아내는 것입니다.
패턴 4: 시뮬레이터 상태 위생
긴 에이전트 세션 후에는 시뮬레이터가 쌓일 수 있습니다. 에이전트가 종료를 잊은 부팅된 시뮬레이터, 오래된 상태를 캐시하는 오래된 앱 설치, 세션 간에 살아남아 재현 불가능한 버그를 만드는 런타임 데이터 등입니다. Stop hook은 이를 정리할 수 있습니다.
#!/bin/bash
# .claude/hooks/stop-simulator-cleanup.sh
# Soft cleanup: shuts down booted simulators we don't need anymore.
# Does NOT erase data; does NOT block. Logs only.
BOOTED=$(xcrun simctl list devices booted 2>/dev/null | grep -E "Booted" | wc -l | xargs)
if (( BOOTED > 0 )); then
echo "[hook] $BOOTED booted simulators at session end; consider shutdown" >&2
# Uncomment to auto-shutdown:
# xcrun simctl shutdown all 2>/dev/null
fi
exit 0
이 형태는 의도적으로 차단하지 않습니다. Hook은 상태를 보고하지만 개발자가 종료 라인의 주석을 해제하지 않는 한 행동하지는 않습니다. 그 이유는 에이전트의 다음 세션이 부팅된 시뮬레이터를 보존하기를 원할 수 있기 때문입니다(시뮬레이터가 이미 실행 중일 때 더 빠른 콜드 스타트). 결정은 개발자별로 이루어집니다. 여러 세션에 걸쳐 시뮬레이터가 쌓이고 비용이 실재하면 종료 라인의 주석을 해제하고, 그렇지 않으면 로그 신호로 남겨두면 됩니다.
더 적극적인 변형은 세션 사이에 시뮬레이터를 지우는 것이지만, 이는 패턴 2의 파괴적 작업 영역으로 넘어갑니다. 지우기는 Stop 자동화가 아니라 PreToolUse 차단에 속합니다.
Hooks가 해결하지 못하는 것
위의 네 가지 패턴은 작동하는 집합이지, 전체 그림은 아닙니다. Hooks가 잡을 수 없는 세 가지 실패 클래스가 있습니다.
에이전트 코드의 로직 버그. Hook은 구조를 검증하지, 의미를 검증하지 않습니다. 에이전트는 컴파일되고, 프로젝트 파일 lint를 통과하고, 그린 빌드되지만 의미적으로 잘못된 @Model 클래스를 작성할 수 있습니다(누락된 마이그레이션, 깨진 고유 제약, 역방향이 없는 SwiftData 관계 등). 로직 정확성은 테스트, 코드 리뷰, 개발자의 눈에 있습니다. Hooks는 구조적이고 라이프사이클적인 문제를 위한 것입니다.
에이전트 품질의 느린 표류. 각 개별 hook은 첫 만남에서 실패 클래스를 막지만, 많은 세션에 걸친 누적 표류(점차 더 지저분해지는 코드, 점차 약해지는 테스트, 점차 오래된 CLAUDE.md 지침)는 hook이 측정하는 것이 아닙니다. 그것은 hook 문제가 아니라 세션 리뷰 문제입니다.
에이전트 도구 표면 외부의 신뢰 경계 위반. Bash와 Edit에 대한 hook은 일반적인 경로를 다룹니다. 에이전트가 호출할 수 있는 모든 MCP 도구에 대한 hook은 도구별 매처가 필요합니다. 일부 MCP 서버는 수십 또는 수백 개의 도구를 노출하며(XcodeBuildMCP는 약 80개를 광고함) 도구별로 hook을 작성하는 것은 비현실적입니다. 거기서의 올바른 패턴은 모든 개별 도구에 hook을 거는 것이 아니라 MCP 서버 접근을 범위 지정하는 것이며(프로젝트 수준의 .mcp.json, 첫 사용 시 승인 흐름), 에이전트가 자신의 MCP 서버를 운영하는 것이 승인된 권한의 일부임을 받아들이는 것입니다.
Hooks와 더 넓은 신뢰 자세 사이의 관계는 저장소가 자신의 신뢰에 대해 투표해서는 안 되는 이유에서 다룹니다. 신뢰는 다운스트림 검사가 아니라 로드 순서 불변량입니다. Hooks는 이미 신뢰받는 에이전트가 취하는 행동에 대한 다운스트림 가드입니다. 에이전트를 처음부터 신뢰해야 하는지에 대한 업스트림 결정을 대체하지는 않습니다.
다르게 만들었으면 하는 것
클러스터의 앱들이 출시했거나 출시했더라면 좋았을 세 가지 패턴.
프로젝트의 나머지 부분과 함께 버전 관리되는 hook 스크립트. Hook 스크립트는 저장소의 .claude/hooks/*.sh에 위치합니다. .claude/settings.json은 상대 경로로 이를 참조합니다. 팀은 머신 전반에서 동일한 안전망을 얻고, 코드 리뷰가 hook 변경에 적용되며, 새 개발자 온보딩은 복사-붙여넣기 작업이 아니라 git clone이 됩니다. ~/.claude/settings.json의 사용자 범위 hook은 프로젝트별 게이팅에 잘못된 세분성입니다.
활성 hook 설정을 출력하는 SessionStart hook. Hook은 발동하기 전까지는 조용합니다. 모든 Claude Code 세션 시작 시 실행되어 “Active hooks: pbxproj-validation, dangerous-bash-gate, build-check-on-stop”을 출력하는 SessionStart hook은 어떤 가드가 실행 중인지 개발자(와 에이전트)에게 상기시킵니다. 비용은 세션당 stderr 한 줄이며, 가치는 안전망이 있다는 것을 모르는 채로 개발하는 사람이 없도록 하는 것입니다.
에이전트 도구 호출의 저장소 수준 감사 로그. 모든 도구 호출(타임스탬프, 도구 이름, 인수 포함)을 .claude/logs/(gitignore됨)의 JSONL 파일에 추가하는 PostToolUse hook. 로그는 채팅 기록 스크롤이 아니라 jq 쿼리로 “이 세션에서 에이전트가 무엇을 했는가?”에 답합니다. Hook은 도구 호출당 몇 밀리초를 추가하며 무언가가 잘못되었을 때 개발자가 grep할 수 있는 영구 감사 데이터를 생성합니다.
Hooks가 잘못된 답인 경우
Hook 계층이 문제를 해결할 잘못된 곳인 두 가지 경우.
에이전트의 MCP 서버 자체. 잘못된 일을 하는 나쁜 MCP 서버는 hook 문제가 아니라 MCP 서버 문제입니다. 해결책은 프로젝트가 신뢰하는 MCP 서버의 범위를 지정하는 것이며(.mcp.json 리뷰, 프로젝트 범위의 첫 사용 승인), 오픈 소스라면 서버의 소스 코드를 읽는 것입니다. 모든 MCP 도구 호출에 대한 hook은 신뢰 문제를 다루지 않으면서 오버헤드만 추가합니다.
무인으로 실행되는 에이전트. 전체 hook 자세는 개발자가 세션 근처에 있고 실패한 hook을 해석할 수 있다고 가정합니다. 사람 없이 CI에서 실행되는 에이전트에는 다른 자세가 필요합니다. 더 엄격한 MCP 범위 지정, 더 좁은 도구 집합, 다른 신뢰 모델. Hooks만으로는 유인 개발에서 무인 자동화로 다리를 놓을 수 없으며, 그 격차는 의도적입니다.
iOS 26+ 출시 iOS 앱에서 이 패턴이 의미하는 것
세 가지 시사점.
-
되돌릴 수 없는 작업에 대해 기본적으로 거부하고, 구조적 영향 범위 파일을 검증하며, 그린 빌드 위에서 완료를 게이팅하세요. 세 가지 hook 라이프사이클 이벤트(
PreToolUse,PostToolUse,Stop), 네 가지 패턴으로 일반적인 iOS 실패 모드를 다룹니다. 결합된 집합은 오후 한 나절에 작성할 만큼 작고, 특정 에이전트나 모델보다 오래 살아남을 만큼 견고합니다. -
종료 코드는 중요하며, 이벤트별로 다릅니다.
exit 2는PreToolUse에서 작업을 차단하고(도구는 절대 실행되지 않음),PostToolUse에서는 차단할 수 없지만(도구가 이미 실행됨) stderr를 에이전트에 다시 전달하여 복구하거나 되돌릴 수 있게 합니다.Stop에서는 에이전트가 세션을 종료하지 못하게 합니다.exit 1은 대부분의 이벤트에서 차단하지 않습니다. 모든 hook을 의존하기 전에 의도적으로 실패하는 케이스로 테스트하세요. -
Hooks는 권한을 제한합니다. 권한을 부여하지는 않습니다. 에이전트의 개발자 머신에 대한 접근은 OS가 개발자의 터미널 세션에 허용하는 모든 것입니다. Hooks는 개발자가 그 권한에서 특정 작업을 깎아내고 명시적인 승인을 요구하게 합니다. 기본값은 OS가 부여하는 모든 것이며, hooks의 목표는 기본값을 더 크게가 아니라 더 작게 만드는 것입니다.
전체 Apple Ecosystem 클러스터: Apple Intelligence 표면을 위한 타입화된 App Intents; 에이전트 표면을 위한 MCP 서버; 둘 사이의 라우팅 질문; 인앱 온디바이스 LLM 기능을 위한 Foundation Models; 런타임 vs 도구 LLM 구분; 세 표면 종합; 단일 진실 소스 패턴; 이러한 hooks와 짝을 이루는 Xcode 통합을 위한 두 개의 MCP 서버; iOS 잠금 화면 상태 머신을 위한 Live Activities; Apple Watch의 watchOS 런타임 계약; 프레임워크 기질을 위한 SwiftUI 내부; visionOS 장면을 위한 RealityKit의 공간 정신 모델; 영속성을 위한 SwiftData 스키마 규율; 시각 계층을 위한 Liquid Glass 패턴; 디바이스 간 도달 범위를 위한 멀티 플랫폼 출시. 허브는 Apple Ecosystem Series에 있습니다. 더 넓은 iOS-with-AI-agents 컨텍스트는 iOS Agent Development guide를 참조하세요.
FAQ
Claude Code hooks란 무엇이며 왜 iOS 개발에 중요한가요?
Claude Code hooks는 라이프사이클 이벤트(PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop)에서 실행되는 결정론적 셸 스크립트입니다. iOS 개발에서는 파괴적 작업—시뮬레이터 지우기, 코드 서명, 프로젝트 파일 변경, 강제 푸시—에 대한 에이전트의 권한을 제한합니다. Hooks 없이 에이전트는 개발자의 머신 권한 전체를 가집니다. Hooks가 있으면 특정 위험한 작업에 명시적 승인이 필요해집니다.
iOS 개발자가 우선순위를 두어야 할 hook 이벤트는 무엇인가요?
파괴적 명령(simctl erase, xcodebuild archive, git push --force)을 차단하기 위한 Bash의 PreToolUse. .pbxproj 무결성 검증을 위한 Edit/Write의 PostToolUse. 그린 빌드 게이팅을 위한 Stop. 활성 hook 설정 로깅을 위한 SessionStart. 이 네 가지가 함께 가장 일반적인 iOS 특화 에이전트 실패를 잡아냅니다.
종료 코드 0, 1, 2의 차이는 무엇인가요?
Exit 0은 작업을 허용하고 진행합니다. Exit 2는 이벤트별로 다르게 동작합니다. PreToolUse에서는 제안된 작업을 차단합니다(도구는 절대 실행되지 않음). PostToolUse에서는 도구가 이미 실행되었기 때문에 차단할 수 없지만, stderr를 에이전트에 다시 전달하여 에이전트가 복구하거나 되돌릴 수 있게 합니다. Stop에서는 에이전트가 세션을 종료하지 못하게 합니다. Exit 1은 오류를 로그에 기록하지만 대부분의 hook 이벤트에서 차단하지는 않습니다. 실제로 작업이 실행되기 전에 막아야 하는 안전 패턴의 경우 PreToolUse에서 exit 2를 사용하세요. 파괴적 쓰기 후 검증의 경우 실패를 에이전트에 다시 전달하기 위해 PostToolUse에서 exit 2를 사용하세요. 모든 hook을 의도적으로 실패하는 입력으로 테스트하여 특정 이벤트에 대해 예상대로 동작하는지 확인하세요.
Hook 스크립트는 어디에 위치해야 하나요?
프로젝트 루트의 .claude/hooks/*.sh에 위치하며, .claude/settings.json이 상대 경로로 이를 참조합니다. 프로젝트의 나머지 부분과 함께 버전 관리되고 코드 리뷰됩니다. ~/.claude/settings.json의 사용자 범위 hook도 작동하지만 프로젝트별 iOS 게이팅에는 잘못된 세분성입니다.
Hooks가 코드 리뷰의 필요성을 대체하나요?
아닙니다. Hooks는 구조적 오류(깨진 프로젝트 파일, 위험한 bash, 깨진 빌드)를 출시 전에 잡습니다. 코드 리뷰는 의미적 오류(로직 버그, 누락된 마이그레이션, 약한 테스트)를 잡습니다. 두 계층은 서로를 보완합니다. Hooks는 에이전트를 내부 루프에 더 안전하게 배포할 수 있게 하고, 코드 리뷰는 경계에서 에이전트의 출력을 정직하게 유지합니다.
References
-
Anthropic, “Claude Code reference: Hooks”. Lifecycle events (
PreToolUse,PostToolUse,UserPromptSubmit,SessionStart,Stop), matcher syntax, command shape, and the role of exit codes. Exit 2 behaves differently per event: onPreToolUseandStopit blocks the action; onPostToolUseit cannot block (the tool already ran) but it surfaces stderr back to the agent. Exit 0 allows; exit 1 generally logs but does not block. Author’s analysis in When the LLM Lives in Your App vs in Your Tooling covers the runtime-vs-tooling LLM trust posture that hooks operationalize. ↩↩ -
Apple,
plutil(1)man page. The-lintflag validates property list syntax across old-style ASCII, XML, and binary formats. It detects parse-level breakage but does not check Xcode-specific semantics like build phase references or UUID validity within the project graph. ↩ -
Apple Developer, “xcodebuild Destination Specifier” and the
xcodebuildman page. The-destination 'platform=...,name=...'syntax is the canonical way to pin a build target; CI environments override the simulator name via env vars or scripted device-availability detection. ↩