파일 저장 시 자동 포맷팅, main 브랜치 직접 커밋 차단, 위험 명령어 차단을 Hooks로 구현했다. Windows 환경에서 cmd /c 래퍼 이슈가 다시 발생했고, 이를 통해 OS별 명령어 차이 문제와 크로스 플랫폼 배포 시 해결 전략을 정리했다.
~/.claude/hooks/ ← 전역 Hook 스크립트
├── security-gate.sh ← 보안 게이트 (PreToolUse)
└── notify-complete.sh ← 작업 완료 알림 (Stop)
onboarding-automation/
└── .claude/
└── settings.json ← Hook 이벤트 연결 설정
주의: ~/.claude/hooks/는 프로젝트 폴더가 아닌 홈 디렉토리에 있어서 파일 트리에 보이지 않는다. 전역 Hook은 모든 프로젝트에 공통 적용되므로 홈 디렉토리에 두는 게 표준 패턴이다.
팀 Plugin으로 공유하려면 프로젝트 전용 위치에 둬야 한다:
onboarding-automation/
└── .claude/
└── hooks/
└── security-gate.sh ← 프로젝트 전용, git으로 팀 공유 가능
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# rm -rf 위험 명령어 차단
if echo "$CMD" | grep -qE 'rm\s+-rf\s+(/|~)'; then
echo "위험한 명령어 감지됨: $CMD" >&2
exit 2
fi
# main 브랜치 직접 커밋 차단
if echo "$CMD" | grep -q "git commit" && \
[ "$(git branch --show-current 2>/dev/null)" = "main" ]; then
echo "main 브랜치 직접 커밋은 금지되어 있습니다." >&2
exit 2
fi
# .env 파일 git add 차단
if echo "$CMD" | grep -q "git add" && echo "$CMD" | grep -q "\.env"; then
echo ".env 파일은 git에 추가할 수 없습니다." >&2
exit 2
fi
exit 0
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cmd /c npx prettier --write \"$TOOL_INPUT_FILE_PATH\" 2>nul || exit 0"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/security-gate.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/notify-complete.sh"
}
]
}
]
}
}
원인: MCP 트러블슈팅에서 동일하게 나타난 문제. Windows Git Bash 환경에서 npx를 직접 실행하면 Claude Code가 실행하지 못한다.
해결:
"command": "cmd /c npx prettier --write \"$TOOL_INPUT_FILE_PATH\""
개발자 A (Windows) → cmd /c npx -y 패키지명
개발자 B (macOS) → npx -y 패키지명
같은 settings.json을 git으로 공유하면 한쪽에서 반드시 에러가 난다.
전략 1 — OS 감지 설치 스크립트 (추천)
팀원 각자가 한 번만 실행하는 셸 스크립트를 제공한다.
# .claude/scripts/setup-mcp.sh
#!/bin/bash
OS=$(uname -s)
if [[ "$OS" == *"MINGW"* ]] || [[ "$OS" == *"CYGWIN"* ]]; then
# Windows Git Bash
claude mcp add slack -- cmd /c npx -y @modelcontextprotocol/server-slack
echo "Windows 환경 설치 완료"
else
# macOS / Linux
claude mcp add slack -- npx -y @modelcontextprotocol/server-slack
echo "macOS/Linux 환경 설치 완료"
fi
bash .claude/scripts/setup-mcp.sh
Plugin README에 이 스크립트 실행을 설치 가이드 첫 번째 단계로 안내한다.
전략 2 — settings.json에 OS별 분기 없이, settings.local.json에서 덮어쓰기
settings.json (git 공유)
→ npx 명령어를 넣지 않음
→ MCP 서버 이름과 구조만 정의
settings.local.json (개인, git 제외)
→ 각자 본인 OS에 맞는 명령어 작성
Plugin README에 OS별 설치 방법을 섹션으로 나눠서 안내한다.
전략 3 — 내장 커넥터 우선 사용 (OS 무관)
npx가 필요한 서비스는 Claude 내장 커넥터로 대체 가능한지 먼저 확인한다.
Gmail, Google Calendar → 내장 커넥터 → OS 무관
GitHub → HTTP 엔드포인트 → OS 무관
Slack → npx 필요 → OS 분기 처리 필요
전략 4 — 추상화 레이어 (The Wrapper Strategy)
에이전트가 "Prettier를 실행해"라고 명령하면, 내부의 '실행 워커'가 알아서 OS를 감지해 처리하도록 만드는 것.
Harness 설계: find-lead.md나 설정 파일에 직접 명령어를 박지 않고, run-prettier.js 같은 중간 매개 스크립트를 만든다.
작동 흐름: 클로드 코드 → node run-prettier.js 호출 → 스크립트 내부에서 OS 감지 후 Prettier 실행.
이렇게 하면 클로드 코드(에이전트)는 OS가 무엇인지 신경 쓸 필요 없이 동일한 인터페이스(node run-prettier.js)만 사용하면 됨.
개발에서 배포까지 각 시점에 따라 관리 방식이 달라진다.
로컬 개발
├── .env 실제 토큰값 저장 (git 제외)
├── .env.example 키 이름만 있는 템플릿 (git 커밋)
├── settings.local.json Claude Code용 env 주입 (git 제외)
└── settings.json ${VAR} 플레이스홀더 (git 커밋)
팀 공유 (Plugin 배포)
├── .env.example 팀원이 복사해서 .env 만들 때 참고
├── settings.json ${VAR} 플레이스홀더 포함 (커밋)
└── README.md OS별 설치 가이드 포함
CI/CD 환경
└── GitHub Secrets 토큰값을 환경변수로 주입
GITHUB_TOKEN, SLACK_BOT_TOKEN 등
→ settings.json의 ${VAR}가 여기서 해석됨
.env.example 예시 (git 커밋 가능):
GITHUB_TOKEN= # GitHub Personal Access Token
SLACK_BOT_TOKEN= # Slack Bot Token (xoxb-로 시작)
SLACK_TEAM_ID= # Slack 워크스페이스 ID (T로 시작)
MAIN_REPO_PATH= # 프로젝트 루트 경로
팀원은 이 파일을 복사해서 .env로 만들고 값을 채운다:
cp .env.example .env
~/.claude/hooks/(전역) 또는 .claude/hooks/(프로젝트) 둘 다 가능cmd /c 래퍼 필요.env.example(커밋) + .env(git 제외) 패턴으로 팀 배포 표준화Hooks를 통해 CLAUDE.md의 "main 커밋 금지" 규칙이 실제로 강제되는 걸 확인했다. 규칙을 쓰는 것(CLAUDE.md)과 규칙을 집행하는 것(Hooks)은 다른 레이어이며, 자동화 시스템에서 이 두 가지를 분리하는 설계가 중요하다. 크로스 플랫폼 문제는 처음 환경을 설계할 때 고려하지 않으면 배포 단계에서 반드시 다시 마주치게 된다.