Claude Code를 처음 쓰기 시작했을 때, 나는 터미널에서 AI가 직접 파일을 수정하고 명령어를 실행한다는 사실에 꽤 놀랐다. 편리한 건 분명하지만, 동시에 “이게 내 시스템에서 아무 명령어나 실행할 수 있다는 건가?” 하는 불안감이 들었다. rm -rf /를 때려버리면 어떡하지? 실수로 프로덕션 서버에 force push를 날리면? 이런 걱정이 한두 가지가 아니었다.
결론부터 말하면, Claude Code는 기본적으로 꽤 보수적인 권한 체계를 갖추고 있다. 하지만 “기본값이 안전하다”와 “내 환경에 맞게 보안을 설정했다”는 완전히 다른 이야기다. 특히 팀 프로젝트나 프로덕션 코드를 다루는 환경이라면, 권한 설정을 직접 손봐야 한다. 이 글에서는 내가 MacBook M1에서 실제로 적용하고 있는 Claude Code 보안 설정을 하나씩 정리한다.
📑 목차
Claude Code 권한 모드 3가지 이해하기
Claude Code는 도구(tool)를 사용할 때마다 사용자에게 승인을 요청하는 것이 기본 동작이다. 하지만 매번 “Yes”를 누르는 게 귀찮아서 자동 모드를 쓰는 사람이 많다. 문제는 각 모드가 어떤 권한을 부여하는지 정확히 이해하지 않고 쓰는 경우다. 관련 내용은 AI Agent API 키 보안 가이드에서도 다루고 있다.
Plan 모드
가장 보수적인 모드다. Claude가 코드를 분석하고 계획만 세운다. 파일을 수정하거나 명령어를 실행하지 않는다. 코드 리뷰를 요청하거나, 아키텍처 설계를 논의할 때 유용하다. Shift+Tab으로 모드를 전환하면 된다. 민감한 프로덕션 코드를 처음 분석할 때는 이 모드에서 시작하는 게 안전하다.
Auto-edit 모드 (기본값)
파일 읽기와 쓰기는 자동으로 수행하지만, Bash 명령어 실행 시에는 사용자 승인을 요청한다. 대부분의 개발 작업에 적합한 균형점이다. 파일 편집은 자유롭게 하되, npm install이나 git push 같은 시스템 명령은 확인을 거치니까 어느 정도 안전망이 있는 셈이다.
Full-auto 모드
모든 도구 사용을 자동으로 승인한다. Bash 명령어도 확인 없이 바로 실행된다. 편의성은 최고지만, 보안 리스크도 가장 크다. 나는 이 모드를 쓸 때 반드시 allowedTools 설정과 hooks를 함께 걸어둔다. 설정 없이 full-auto를 쓰는 건 브레이크 없는 차를 모는 것과 같다.
모드 전환은 Claude Code 세션 안에서 Shift+Tab을 누르면 된다. 또는 세션 시작 시 플래그로 지정할 수도 있다.
# Plan 모드로 시작 (분석만)
claude --mode plan
# Auto-edit 모드 (기본값, 파일 편집 자동 + 명령어 승인 필요)
claude --mode auto-edit
# Full-auto 모드 (모든 도구 자동 승인)
claude --mode full-auto
# 허용 도구를 지정한 full-auto (권장)
claude --mode full-auto --allowedTools "Edit,Read,Glob,Grep"
settings.json에서 allowedTools 설정

모드보다 더 세밀한 제어가 필요할 때는 settings.json을 직접 편집한다. 이 파일에서 각 도구별로 허용 여부를 지정할 수 있다. 설정 파일 위치는 두 곳이다.
- 글로벌 설정:
~/.claude/settings.json— 모든 프로젝트에 적용 - 프로젝트 설정:
.claude/settings.json(프로젝트 루트) — 해당 프로젝트만 적용
프로젝트 설정이 글로벌 설정보다 우선순위가 높다. 겹치는 항목이 있으면 프로젝트 설정이 덮어쓴다.
// ~/.claude/settings.json (글로벌 설정)
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Edit",
"Write",
"Bash(git status)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(npm test*)",
"Bash(npm run lint*)",
"Bash(ls*)"
],
"deny": [
"Bash(rm -rf*)",
"Bash(git push --force*)",
"Bash(sudo*)",
"Bash(chmod 777*)",
"Bash(curl*|*sh)",
"Bash(wget*|*sh)"
]
}
}
이렇게 설정하면 파일 읽기/쓰기 도구는 자동으로 사용 가능하지만, Bash 명령어는 허용 목록에 있는 것만 자동 실행되고, 나머지는 승인을 요청한다. deny 목록에 있는 명령어는 아예 실행이 차단된다.
패턴에서 *는 와일드카드다. Bash(git diff*)는 git diff, git diff --staged, git diff HEAD~1 등 git diff로 시작하는 모든 명령을 허용한다. 반대로 Bash(rm -rf*)는 rm -rf로 시작하는 모든 명령을 차단한다.
Bash 명령어 허용/차단 패턴
Bash 도구가 가장 신경 쓰이는 부분이다. Claude Code는 파일을 읽고 쓰는 것과 별개로, 터미널에서 직접 명령어를 실행할 수 있다. npm install 같은 무해한 명령어부터 rm -rf / 같은 재앙까지 스펙트럼이 넓다.
허용할 명령어 패턴 (안전 카테고리)
내가 실제로 허용해두고 쓰는 패턴들이다. 이 명령어들은 시스템에 변경을 가하지 않거나, 가하더라도 되돌리기 쉬운 것들이다.
Bash(git status),Bash(git log*),Bash(git diff*)— 조회 전용 git 명령Bash(git add*),Bash(git commit*)— 로컬 커밋 (push 제외)Bash(npm test*),Bash(npm run lint*),Bash(npm run build*)— 빌드/테스트Bash(ls*),Bash(cat*),Bash(head*),Bash(wc*)— 파일 조회Bash(python*)— Python 스크립트 실행 (프로젝트에 따라)
반드시 차단해야 할 명령어 패턴
이건 실수로라도 실행되면 안 되는 명령어들이다. deny 목록에 반드시 넣어두자.
Bash(rm -rf*)— 재귀적 삭제. 복구 불가능Bash(git push --force*),Bash(git push -f*)— 원격 히스토리 덮어쓰기Bash(git reset --hard*)— 로컬 변경사항 완전 삭제Bash(sudo*)— 관리자 권한 명령Bash(chmod 777*)— 전체 권한 개방Bash(curl*|*sh),Bash(wget*|*sh)— 원격 스크립트 다운로드 후 실행Bash(npx*)— 확인 없이 패키지 다운로드/실행 가능Bash(ssh*)— 원격 서버 접속
hooks 설정으로 위험 명령 사전 차단
Claude Code 1.0.17부터 hooks 기능이 도입됐다. 이건 도구가 실행되기 전(PreToolUse) 또는 후(PostToolUse)에 커스텀 스크립트를 실행하는 기능이다. git의 pre-commit hook과 비슷한 개념인데, Claude Code의 모든 도구에 적용할 수 있다.
hooks는 settings.json에 정의한다. 핵심은 PreToolUse hook에서 위험한 패턴을 감지하면 exit code 2를 반환해서 실행을 차단하는 것이다.
// ~/.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/Users/kyunghunkim/.claude/hooks/guard-dangerous-commands.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/Users/kyunghunkim/.claude/hooks/log-file-changes.sh"
}
]
}
]
}
}
hook 스크립트는 표준 입력(stdin)으로 JSON을 받는다. 이 JSON에는 실행하려는 도구 이름과 파라미터가 들어 있다. 스크립트에서 이걸 파싱해서 위험한 패턴이면 차단하는 구조다.
#!/bin/bash
# ~/.claude/hooks/guard-dangerous-commands.sh
# PreToolUse hook: 위험한 Bash 명령어를 사전 차단
# stdin에서 JSON 입력 읽기
input=$(cat)
# 실행하려는 명령어 추출
command=$(echo "$input" | jq -r '.tool_input.command // empty')
if [ -z "$command" ]; then
exit 0
fi
# 위험 패턴 정의
dangerous_patterns=(
"rm -rf /"
"rm -rf ~"
"rm -rf \*"
"git push --force"
"git push -f"
"git reset --hard"
"sudo rm"
"sudo chmod"
"chmod 777"
"mkfs"
"> /dev/sda"
"dd if="
":(){ :|:& };:"
)
for pattern in "${dangerous_patterns[@]}"; do
if echo "$command" | grep -qF "$pattern"; then
# exit code 2 = 도구 실행 차단
echo "BLOCKED: 위험한 명령어가 감지되었습니다 — $pattern"
echo "이 명령어를 실행하려면 hook을 비활성화하거나 수동으로 실행하세요."
exit 2
fi
done
# 안전한 명령어는 통과
exit 0
이 스크립트를 만든 뒤 실행 권한을 부여해야 한다.
chmod +x ~/.claude/hooks/guard-dangerous-commands.sh
exit code의 의미는 다음과 같다. 0은 통과(도구 실행 허용), 2는 차단(도구 실행 거부 + 메시지 전달), 그 외 코드는 hook 자체의 에러로 처리된다. hook 에러 시에는 도구 실행이 진행되므로, 방어 로직은 반드시 exit 2를 사용해야 한다.
프로젝트별 .claude/settings.json 분리
모든 프로젝트에 같은 보안 설정을 적용하는 건 비효율적이다. 개인 사이드 프로젝트와 회사 프로덕션 코드의 보안 요구사항이 같을 리 없다. Claude Code는 프로젝트 루트에 .claude/settings.json을 두면 해당 프로젝트에서만 적용되는 설정을 지정할 수 있다.
나는 프로젝트 성격에 따라 3단계로 나눠서 관리한다.
개인 프로젝트 (느슨한 설정)
// my-side-project/.claude/settings.json
{
"permissions": {
"allow": [
"Read", "Write", "Edit", "Glob", "Grep",
"Bash(git*)",
"Bash(npm*)",
"Bash(node*)",
"Bash(python*)"
],
"deny": [
"Bash(rm -rf*)",
"Bash(sudo*)"
]
}
}
팀 프로젝트 (중간 설정)
// team-project/.claude/settings.json
{
"permissions": {
"allow": [
"Read", "Edit", "Glob", "Grep",
"Bash(git status)", "Bash(git diff*)", "Bash(git log*)",
"Bash(git add*)", "Bash(git commit*)",
"Bash(npm test*)", "Bash(npm run lint*)"
],
"deny": [
"Bash(rm -rf*)",
"Bash(git push*)",
"Bash(git reset*)",
"Bash(sudo*)",
"Write"
]
}
}
프로덕션 인프라 (엄격한 설정)
// infra-repo/.claude/settings.json
{
"permissions": {
"allow": [
"Read", "Glob", "Grep",
"Bash(git status)", "Bash(git diff*)", "Bash(git log*)",
"Bash(terraform plan*)",
"Bash(kubectl get*)", "Bash(kubectl describe*)"
],
"deny": [
"Edit", "Write",
"Bash(terraform apply*)",
"Bash(kubectl delete*)", "Bash(kubectl exec*)",
"Bash(git push*)", "Bash(git reset*)",
"Bash(rm*)", "Bash(sudo*)",
"Bash(ssh*)", "Bash(scp*)"
]
}
}
프로덕션 인프라에서는 아예 파일 수정 도구(Edit, Write)를 차단하고, 조회 명령만 허용한다. terraform apply나 kubectl delete처럼 실제 인프라를 변경하는 명령도 차단 목록에 넣어둔다. Claude Code를 코드 리뷰와 분석 용도로만 사용하는 셈이다.
이 설정 파일은 .gitignore에 넣지 않는 것을 권장한다. 팀원들도 같은 보안 설정을 공유해야 하기 때문이다. 다만, 개인 취향이 강하게 반영된 설정이라면 .claude/settings.local.json으로 분리하고 이건 .gitignore에 넣는 방법도 있다.
파일 접근 범위 제한 — 민감 디렉토리 보호
Claude Code는 기본적으로 프로젝트 디렉토리(현재 작업 디렉토리) 내의 파일에 접근한다. 하지만 절대 경로를 사용하면 프로젝트 외부 파일도 읽을 수 있다. 이건 편리하지만, .env 파일이나 SSH 키 같은 민감한 파일에 접근할 위험이 있다.
CLAUDE.md에서 접근 금지 경로를 명시하는 것이 첫 번째 방어선이다. 하지만 CLAUDE.md는 지시사항이지 강제 규칙이 아니다. 더 확실한 방법은 hook으로 파일 접근을 검사하는 것이다.
#!/bin/bash
# ~/.claude/hooks/guard-sensitive-paths.sh
# Read, Write, Edit 도구에서 민감 경로 접근 차단
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
# 도구에 따라 경로 추출
case "$tool_name" in
Read)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
;;
Write)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
;;
Edit)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
;;
*)
exit 0
;;
esac
if [ -z "$file_path" ]; then
exit 0
fi
# 민감 경로 패턴
sensitive_paths=(
"$HOME/.ssh"
"$HOME/.aws"
"$HOME/.gnupg"
"$HOME/.env"
"$HOME/.npmrc"
"$HOME/.netrc"
"/etc/passwd"
"/etc/shadow"
".env"
".env.local"
".env.production"
"credentials.json"
"serviceAccountKey.json"
)
for pattern in "${sensitive_paths[@]}"; do
expanded=$(eval echo "$pattern")
if echo "$file_path" | grep -qF "$expanded"; then
echo "BLOCKED: 민감한 파일 접근이 차단되었습니다 — $file_path"
exit 2
fi
done
exit 0
이 hook을 settings.json의 PreToolUse에 Read, Write, Edit 매처로 등록하면 된다. 한 가지 주의할 점은, .env 파일을 읽지 못하면 Claude Code가 환경변수 관련 작업을 도와주기 어렵다는 것이다. .env.example은 허용하되 실제 값이 들어 있는 .env는 차단하는 식으로 조정하면 된다.
CLAUDE.md에서 보안 규칙 명시하기
CLAUDE.md는 프로젝트 루트에 두는 지시사항 파일이다. Claude Code가 세션을 시작할 때 이 파일을 자동으로 읽고 지시를 따른다. 기술적 강제력은 hooks보다 약하지만, Claude가 작업을 수행하는 방식 자체를 제어하는 데 효과적이다.
내가 프로젝트에 실제로 넣어둔 보안 관련 CLAUDE.md 항목들이다.
# CLAUDE.md
## 보안 규칙 (반드시 준수)
### 금지 행위
- .env, .env.local, .env.production 파일을 절대 읽거나 수정하지 마세요
- credentials.json, serviceAccountKey.json 등 인증 파일 접근 금지
- git push, git push --force 실행 금지 — 푸시는 항상 사용자가 직접 합니다
- rm -rf 명령어 사용 금지 — 파일 삭제가 필요하면 삭제할 파일 목록만 알려주세요
- sudo 명령어 사용 금지
- npm publish, yarn publish 실행 금지
### 파일 수정 규칙
- src/ 디렉토리 외의 설정 파일(package.json, tsconfig.json 등)은 수정 전 반드시 확인 요청
- database migration 파일은 생성하지 마세요 — 스키마 변경 제안만 해주세요
- .github/workflows/ 파일 수정 시 변경 내용을 먼저 설명하고 승인을 받으세요
### git 규칙
- 커밋 메시지는 Conventional Commits 형식을 따릅니다
- amend 대신 항상 새 커밋을 생성하세요
- main, master 브랜치에 직접 커밋하지 마세요 — 항상 feature 브랜치에서 작업합니다
- force push는 어떤 상황에서도 금지입니다
CLAUDE.md의 장점은 팀원들과 공유하기 쉽다는 것이다. settings.json은 각자 로컬 환경에서 설정하지만, CLAUDE.md는 리포지토리에 포함되니까 모든 팀원이 동일한 규칙을 적용받는다. 다만, 악의적인 프롬프트 인젝션이나 극단적인 상황에서는 CLAUDE.md를 무시할 가능성이 있으므로, 정말 중요한 차단은 반드시 hooks로 처리해야 한다.
실수로 실행된 위험 명령 복구 방법
아무리 설정을 잘 해도 실수는 일어난다. Claude Code가 의도치 않은 변경을 가했을 때 복구하는 방법을 알아두는 게 중요하다.
git 기반 복구 (가장 일반적)
Claude Code가 파일을 잘못 수정한 경우, git이 가장 확실한 안전망이다. 작업 전에 커밋을 해두는 습관이 있다면 복구는 간단하다.
# 수정된 파일 확인
git status
git diff
# 특정 파일만 원래대로 복구
git checkout -- src/index.ts
# 모든 변경사항 되돌리기 (staged + unstaged)
git checkout -- .
# 최근 커밋으로 완전 복구 (주의: 커밋되지 않은 변경 전부 소실)
git reset --hard HEAD
# 특정 커밋 시점으로 복구
git log --oneline -10 # 커밋 해시 확인
git reset --hard abc1234
Time Machine 복구 (MacBook)
git으로 추적하지 않는 파일이 삭제된 경우, macOS의 Time Machine이 마지막 방어선이다. M1 MacBook에서 Time Machine을 외장 SSD에 설정해두면 1시간마다 자동 백업이 된다. Finder에서 해당 디렉토리로 이동한 뒤 메뉴바의 Time Machine 아이콘을 클릭하면 과거 시점의 파일을 복구할 수 있다.
Undo 기능 활용
Claude Code 자체에 undo 기능이 있다. 세션 내에서 Claude가 수행한 파일 변경은 추적되며, /undo 명령으로 마지막 변경을 되돌릴 수 있다. 다만 Bash 명령어로 인한 시스템 변경(패키지 설치, 파일 삭제 등)은 undo가 안 되므로, 이런 작업은 사전 차단이 답이다.
팀 환경에서의 권한 관리 전략
혼자 쓸 때는 본인만 조심하면 되지만, 팀에서 Claude Code를 쓸 때는 구성원마다 보안 인식 수준이 다르다는 걸 전제해야 한다. 내가 팀에서 적용한 전략은 3가지다.
1. 프로젝트 settings.json을 리포지토리에 커밋
.claude/settings.json을 리포지토리에 포함시키면 모든 팀원이 동일한 권한 설정으로 작업한다. PR 리뷰 때 설정 변경도 확인할 수 있으니 보안 감사 효과도 있다.
2. CLAUDE.md에 팀 공통 규칙 명시
코딩 컨벤션과 마찬가지로, Claude Code 사용 규칙도 문서화해서 공유한다. “우리 팀에서 Claude Code는 이렇게 쓴다”는 기준을 세워두면, 새 팀원이 합류해도 보안 수준이 유지된다.
3. CI에서 설정 검증
GitHub Actions나 CI 파이프라인에서 .claude/settings.json의 내용을 검증하는 스텝을 추가할 수 있다. 예를 들어, deny 목록에 rm -rf와 git push --force가 반드시 포함되어 있는지 체크하는 스크립트를 돌리는 식이다. 누군가 실수로 보안 설정을 느슨하게 바꾸면 CI에서 잡아낸다.
#!/bin/bash
# scripts/validate-claude-settings.sh
# CI에서 Claude Code 보안 설정을 검증하는 스크립트
SETTINGS_FILE=".claude/settings.json"
if [ ! -f "$SETTINGS_FILE" ]; then
echo "ERROR: $SETTINGS_FILE 파일이 없습니다."
exit 1
fi
# 필수 차단 패턴 확인
required_denials=("rm -rf" "git push --force" "git push -f" "sudo")
for pattern in "${required_denials[@]}"; do
if ! jq -e ".permissions.deny[]" "$SETTINGS_FILE" 2>/dev/null | grep -q "$pattern"; then
echo "ERROR: deny 목록에 '$pattern' 패턴이 없습니다."
echo "보안 정책상 이 패턴은 반드시 차단해야 합니다."
exit 1
fi
done
# Write 도구가 allow에 있으면 경고
if jq -e '.permissions.allow[]' "$SETTINGS_FILE" 2>/dev/null | grep -q '"Write"'; then
echo "WARNING: Write 도구가 허용되어 있습니다. 프로덕션 리포에서는 권장하지 않습니다."
fi
echo "Claude Code 보안 설정 검증 통과"
exit 0
보안 설정 체크리스트
마지막으로, Claude Code를 새 프로젝트에 도입할 때마다 확인하는 체크리스트를 정리했다. 나는 이걸 Notion에 템플릿으로 만들어두고, 프로젝트 시작할 때마다 복사해서 쓴다.
초기 설정
- ☐ 글로벌
~/.claude/settings.json설정 완료 - ☐ 프로젝트
.claude/settings.json생성 및 커밋 - ☐
deny목록에rm -rf,git push --force,sudo포함 확인 - ☐ CLAUDE.md에 보안 규칙 섹션 추가
- ☐
.env파일이.gitignore에 포함되어 있는지 확인
hooks 설정
- ☐ 위험 명령 차단 hook 스크립트 작성 및
chmod +x - ☐ 민감 파일 접근 차단 hook 스크립트 작성
- ☐ hook 스크립트에
jq의존성 확인 (brew install jq) - ☐ hook 동작 테스트 — 의도적으로 차단 대상 명령 실행해보기
팀 환경 추가 항목
- ☐
.claude/settings.json이.gitignore에 포함되지 않음 확인 - ☐ CI에 설정 검증 스크립트 추가
- ☐ 팀 위키나 README에 Claude Code 사용 가이드 링크
- ☐ PR 리뷰 시
.claude/디렉토리 변경사항 확인 규칙 수립
정기 점검 (월 1회)
- ☐ Claude Code 버전 업데이트 확인 (
claude --version) - ☐ 새로 추가된 보안 기능 확인 (릴리즈 노트)
- ☐
allow목록에 불필요하게 추가된 항목 정리 - ☐ hook 스크립트가 정상 동작하는지 테스트
- ☐ 팀원들의 글로벌 설정이 최소 기준을 충족하는지 확인
보안 설정은 한 번 해두면 끝이 아니다. Claude Code가 계속 업데이트되면서 새로운 도구나 기능이 추가되기 때문에, 주기적으로 설정을 리뷰하는 습관이 필요하다. 특히 메이저 버전 업데이트 후에는 반드시 릴리즈 노트를 확인하고, 새로 추가된 도구에 대한 권한 설정을 추가해야 한다.
결국 AI 에이전트 보안의 핵심은 “신뢰하되 검증하라(Trust but verify)”이다. Claude Code는 충분히 안전하게 설계되어 있지만, 추가 방어 레이어를 깔아두면 예상치 못한 상황에서도 시스템을 보호할 수 있다. 특히 팀 환경에서는 가장 보안 인식이 낮은 구성원의 수준에 맞춰서 설정해야 한다는 걸 잊지 말자. 자세한 내용은 Claude Code 공식 문서를 참고하자.