Tips & Tricks (업데이트: 2026. 6. 1.)

Claude Code 권한 설정 완벽 가이드 | settings.json·Hooks·allowlist 철저 해설

Claude Code 권한 설정 완벽 해설. allow/deny/ask 구분 사용법, Hooks를 활용한 자동화, 환경별 settings.json, 실전 패턴 모음까지 동작하는 코드로 소개합니다.

Claude Code 권한 설정 완벽 가이드 | settings.json·Hooks·allowlist 철저 해설

Claude Code는 매우 강력한 파일 조작·명령 실행 능력을 갖추고 있습니다. 그 힘을 안전하게 제어하는 것이 권한 설정(permissions) 입니다. “대충 쓰고 있는” 상태에서 벗어나, 의도한 대로 동작하는 Claude Code를 설계해 봅시다.

이 글에서는 .claude/settings.json의 전체 설정 항목, Hooks 구현 패턴, 환경별 권한 설계까지 동작하는 코드로 철저히 해설합니다.

권한 설정의 전체 구조

Claude Code의 권한은 3단계로 제어합니다.

단계동작
허용allow확인 다이얼로그 없이 자동 실행
확인ask매번 사용자 승인 요구
거부deny일절 실행 불가 (에러로 차단)

설정은 공식적으로 settings.json 중심으로 관리합니다. 사용자 전체 설정은 ~/.claude/settings.json, 팀과 공유하는 프로젝트 설정은 .claude/settings.json, 개인 오버라이드는 .claude/settings.local.json에 둡니다. ~/.claude.json에는 MCP 같은 일부 상태가 남을 수 있지만, 권한 규칙은 settings.json 기준으로 설명하는 것이 안전합니다.

우선순위 (높은 순):
Managed settings
    > 명령줄 인수
        > Local .claude/settings.local.json
            > Project .claude/settings.json
                > User ~/.claude/settings.json

permission rules는 scope를 넘어 병합되고 deny -> ask -> allow 순서로 평가됨

settings.json 기본 구조

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Bash(npm run *)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force*)"
    ],
    "ask": [
      "Write(**)",
      "Edit(**)",
      "Bash(git commit*)"
    ]
  },
  "hooks": {
    "PreToolUse": [],
    "PostToolUse": []
  }
}

툴 이름과 패턴 문법

권한 설정에 쓰는 것은 「툴 이름(인수 패턴)」 형식입니다.

주요 툴 목록

툴 이름내용
Read파일 읽기
Write파일 신규 생성
Edit기존 파일 부분 변경
Bash셸 명령 실행
Glob파일 패턴 검색
Grep내용 검색
WebFetchURL 취득
Agent서브 에이전트 시작

패턴 문법

"Read(**)"          // 전체 파일 읽기 허용
"Read(src/**)"      // src/ 이하만 허용
"Read(*.md)"        // .md 파일만 허용
"Bash(npm run *)"   // npm run 으로 시작하는 명령만 허용
"Bash(git *)"       // git 명령 전반 허용
"Bash(rm -rf *)"    // rm -rf 거부

**는 디렉토리를 포함한 전체 경로에, *는 단일 세그먼트에 매치됩니다.

실전 패턴 모음

패턴 1: 개인 개발 (비교적 자유롭게)

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Bash(npm *)",
      "Bash(git log*)",
      "Bash(git diff*)",
      "Bash(git status*)",
      "Bash(git add*)",
      "Bash(node *)",
      "Bash(echo *)",
      "Bash(cat *)",
      "Bash(ls *)"
    ],
    "deny": [
      "Bash(rm -rf /)",
      "Bash(rm -rf ~*)",
      "Bash(git push --force *main*)",
      "Bash(git push --force *master*)"
    ],
    "ask": [
      "Write(**)",
      "Edit(**)",
      "Bash(git commit*)",
      "Bash(git push*)",
      "Bash(rm *)"
    ]
  }
}

패턴 2: 팀 개발 (보안 중시)

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Bash(npm run lint)",
      "Bash(npm run test)",
      "Bash(npm run typecheck)",
      "Bash(git log*)",
      "Bash(git diff*)",
      "Bash(git status*)",
      "Bash(git branch*)"
    ],
    "deny": [
      "Bash(rm -rf*)",
      "Bash(git push --force*)",
      "Bash(git push -f*)",
      "Bash(git reset --hard*)",
      "Bash(git rebase *main*)",
      "Bash(git rebase *master*)",
      "Bash(DROP *)",
      "Bash(TRUNCATE *)",
      "Bash(curl * | bash)",
      "Bash(wget * | sh)"
    ],
    "ask": [
      "Write(**)",
      "Edit(**)",
      "Bash(git commit*)",
      "Bash(git push*)",
      "Bash(git add*)",
      "Bash(npm install*)",
      "Bash(*deploy*)"
    ]
  }
}

패턴 3: 프로덕션 환경 (읽기 전용)

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Bash(git log*)",
      "Bash(git diff*)",
      "Bash(git status*)",
      "Bash(git show*)",
      "Bash(cat *)",
      "Bash(ls *)",
      "Bash(ps *)",
      "Bash(df *)",
      "Bash(top *)"
    ],
    "deny": [
      "Write(**)",
      "Edit(**)",
      "Bash(git push*)",
      "Bash(git commit*)",
      "Bash(git reset*)",
      "Bash(rm *)",
      "Bash(mv *)",
      "Bash(*deploy*)",
      "Bash(*restart*)",
      "Bash(*kill *)"
    ],
    "ask": []
  }
}

프로덕션이나 프로덕션에 가까운 작업 트리에서는 이 설정을 현재 .claude/settings.json으로 두고, claude --permission-mode plan 또는 사전 승인된 dontAsk로 시작합니다. 별도 settings 파일을 환경 변수로 바꾸는 방식보다 공식 scope와 permission mode 모델에 맞아 리뷰하기 쉽습니다.

패턴 4: 콘텐츠 생성 전용 (이 사이트에서 사용하는 패턴)

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Glob(**)",
      "Grep(**)",
      "Write(site/src/content/**)",
      "Write(content/**)",
      "Edit(site/src/content/**)",
      "Edit(content/**)",
      "Bash(git log*)",
      "Bash(git diff*)",
      "Bash(git status*)",
      "Bash(node scripts/*)",
      "Bash(QIITA_TOKEN=* node scripts/qiita-publish.mjs)"
    ],
    "deny": [
      "Bash(rm -rf*)",
      "Bash(git push --force*)",
      "Edit(.env*)",
      "Read(.env*)"
    ],
    "ask": [
      "Bash(git add*)",
      "Bash(git commit*)",
      "Bash(git push*)",
      "Bash(bash scripts/deploy.sh*)"
    ]
  }
}

Write(site/src/content/**) 처럼 쓰기를 특정 디렉토리에 제한하는 것이 포인트입니다.

Hooks: 권한 전후에 처리 삽입하기

Hooks는 툴 실행의 전후에 자동으로 명령을 실행하는 구조입니다. 보안 체크나 자동 포맷에 활용할 수 있습니다.

PreToolUse: 실행 전 훅

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(git add*)",
        "hooks": [{
          "type": "command",
          "command": "git diff --cached --name-only | grep -E '^\\.env' && echo '🚨 .env 추가 감지!' && exit 1 || exit 0"
        }]
      },
      {
        "matcher": "Bash(git commit*)",
        "hooks": [{
          "type": "command",
          "command": "node scripts/secret-scan.mjs"
        }]
      },
      {
        "matcher": "Bash(rm*)",
        "hooks": [{
          "type": "command",
          "command": "echo '⚠️ 삭제 명령 감지. 5초 후 실행됩니다. Ctrl+C로 중단.' && sleep 5"
        }]
      }
    ]
  }
}

훅 명령이 exit code 1을 반환하면 툴 실행이 차단됩니다. 이것이 가장 중요한 포인트입니다.

PostToolUse: 실행 후 훅

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{
          "type": "command",
          "command": "npx tsc --noEmit 2>&1 | head -20 || true"
        }]
      },
      {
        "matcher": "Bash(git commit*)",
        "hooks": [{
          "type": "command",
          "command": "git log --oneline -3"
        }]
      }
    ]
  }
}

PostToolUse는 실행 후 확인이나 부작용에 사용합니다. 파일 편집 후 자동으로 타입 체크, 커밋 후 최신 3건의 로그 표시 등에 활용합니다.

실용적인 Hooks 레시피 모음

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(npm install*)",
        "hooks": [{
          "type": "command",
          "command": "echo '📦 패키지를 추가합니다. package.json을 확인해 주세요.'"
        }]
      },
      {
        "matcher": "Bash(*deploy*)",
        "hooks": [{
          "type": "command",
          "command": "read -p '🚀 배포를 실행합니다. 계속하시겠습니까? [y/N] ' ans && [ \"$ans\" = 'y' ] || exit 1"
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write(*.ts)|Edit(*.ts)",
        "hooks": [{
          "type": "command",
          "command": "npx eslint --fix $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true"
        }]
      }
    ]
  }
}

Permission Modes: 시작 시 권한 레벨

claude 명령 시작 시 모드를 지정할 수도 있습니다.

# 일반 모드 (settings.json에 따름)
claude

# 모든 조작 자동 승인 (위험! 신뢰할 수 있는 환경에서만)
claude --dangerously-skip-permissions

# 특정 조작만 스킵
claude --allowedTools "Read,Grep,Glob"

# 비대화형 모드 (CI/CD에서 사용)
claude -p "테스트를 실행하고 결과를 보고해" --dangerously-skip-permissions

--dangerously-skip-permissionsCI/CD 자동 실행이나 완전히 파악한 자동화 스크립트에서만 사용하고, 일상적인 대화에서는 피해야 합니다.

2026년판: 안전한 permission budget과 팀 리뷰

여기서 말하는 permission budget은 공식 기능명이 아니라, “첫 1주일 동안 자동 실행을 허용할 범위”를 정하는 운영 기준입니다. 2026년 6월 1일 기준 공식 Docs에서는 permission rules가 deny -> ask -> allow 순서로 평가됩니다. PreToolUse hooks는 런타임 검사를 추가할 수 있지만, hook의 allow가 이미 매칭된 denyask를 덮어쓰지는 못합니다. 그래서 처음에는 속도보다 되돌릴 수 있는 범위를 우선해야 합니다.

초기 budget은 좁게 잡습니다. allow에는 읽기와 검증만 넣습니다: Read, Glob, Grep, git status, git diff, git log, npm run lint, npm run test. ask에는 의도를 바꾸는 작업을 넣습니다: Edit, Write, git add, git commit, git push, npm install, deploy 관련 명령. deny에는 되돌리기 어렵거나 민감한 작업을 넣습니다: .env, secrets/**, rm -rf, git reset --hard, git push --force, curl | bash, wget | sh, 프로덕션 DB 명령. bypassPermissions는 컨테이너, VM, devcontainer처럼 손상되어도 격리되는 환경에서만 사용합니다.

팀에서는 .claude/settings.json을 애플리케이션 코드처럼 PR 리뷰합니다. 리뷰어는 세 가지만 보면 됩니다. allow가 반복 실행되어도 안전한 명령만 포함하는지, ask가 쓰기/커밋/배포처럼 인간의 의도 확인이 필요한 지점을 잡는지, deny가 secrets, 강제 push, 삭제, 프로덕션 작업을 막는지입니다. 조직 관리자가 있다면 server-managed settings에서 disableBypassPermissionsModedisableAutoMode"disable"로 설정하고, allowManagedPermissionRulesOnly로 사용자나 프로젝트가 permission rules를 추가하지 못하게 할 수 있습니다.

위험한 실패 사례는 Bash(git *) 또는 Bash(node *)를 넓게 allow하고, Read(.env*) deny만으로 secrets가 보호된다고 생각하는 것입니다. 공식 문서는 Read/Edit deny가 Claude Code의 내장 파일 도구와 인식 가능한 파일 명령에는 적용되지만, 임의의 Node/Python 서브프로세스가 직접 파일을 여는 것까지 막지는 않는다고 설명합니다. 비밀 정보를 지키려면 넓은 Bash allow를 피하고, sandbox, OS 파일 권한, PreToolUse 검사를 함께 써야 합니다.

복구 절차도 미리 정합니다. 원치 않는 편집이 들어오면 먼저 /permissions로 활성 규칙과 출처를 확인하고 git diff를 봅니다. 되돌릴 때는 git restore -p로 hunk 단위로 되돌리고, 추적되지 않는 파일은 git clean -n으로 먼저 미리 봅니다. secrets가 읽혔을 가능성이 있으면 .claude/settings.json만 고치는 것으로 끝내지 말고 token이나 key를 교체합니다.

이 글을 실제 저장소 정책으로 옮기려면 permission budget looppermission audit checklist로 먼저 점검하세요. 복사해 쓸 수 있는 교재와 템플릿이 필요하면 Claude Code 제품 페이지를 보고, 팀 도입과 권한 리뷰를 같이 설계해야 한다면 consultation page가 더 현실적인 다음 단계입니다.

설정 파일의 우선순위와 덮어쓰기

여러 설정 파일이 존재하는 경우:

~/.claude/settings.json     ← User (모든 프로젝트 공통)
.claude/settings.json       ← Project (git 관리)
.claude/settings.local.json ← Local (개인 오버라이드, gitignore)
managed-settings.json       ← Managed (조직 정책, 최우선)

permission rules는 병합되며 deny가 항상 allow보다 먼저 평가됨

개인용 추가 설정.claude/settings.local.json에 작성하고 gitignore합니다. 팀의 deny 리스트를 개인 설정으로 덮어쓸 수 없도록, deny는 settings.json에만 작성하는 것이 안전한 설계입니다.

# .gitignore에 추가
.claude/settings.local.json

함정 5가지

1. 패턴의 와일드카드를 잘못 쓰기

// ❌ 이것은 "git"이라는 단일 명령에만 매치됨
"Bash(git)"

// ✅ git 이후의 인수도 포함해서 매치
"Bash(git *)"
"Bash(git*)"  // 공백 없이도 동작하지만 * 로 명시하는 게 더 안전

2. deny가 ask보다 우선된다는 것을 잊기

// 이 설정에서 Bash(rm -rf /tmp/test)는 deny에 걸려 차단됨
// ask에는 도달하지 않음
{
  "deny": ["Bash(rm -rf*)"],
  "ask": ["Bash(rm*)"]  // ← rm -rf는 deny 쪽에서 처리됨
}

3. Hooks의 exit code를 의식하지 않기

# PreToolUse 훅 명령이 항상 exit 0을 반환하면
# 스캔이 실패해도 차단할 수 없음

# ❌ 에러가 나도 통과됨
"command": "node scan.mjs"

# ✅ 명시적으로 exit code 제어
"command": "node scan.mjs || exit 1"

4. settings.json을 gitignore해 버리기

팀에서 공유하기 위해 git 관리하고 싶은 settings.json을 실수로 .gitignore에 넣어버리는 케이스가 있습니다. 프로젝트 설정은 git 관리, 개인 설정인 settings.local.json만 gitignore가 정답입니다.

5. 프로덕션 환경 설정을 수동으로 전환하는 것을 잊기

# ❌ 평소 사용하던 설정 그대로 프로덕션 작업을 해버림

# ✅ 프로덕션 작업 전에는 plan mode로 시작하고 작업 트리의 .claude/settings.json을 프로덕션 안전 설정으로 둔다
claude --permission-mode plan

별칭을 등록해 두면 잊기 어려움:

# ~/.bashrc or ~/.zshrc
alias claude-prod='claude --permission-mode plan'

설정 디버그 방법

“왜 이 명령이 차단되는지”를 모를 때:

# 현재 설정 확인
claude --print-settings 2>/dev/null || cat .claude/settings.json

# 어떤 규칙에 매치되는지 확인 (verbose 모드)
claude --verbose -p "git push를 실행해"

정리: 설정 설계의 베스트 프랙티스

1. 먼저 deny부터 결정
   → 절대로 실행시키고 싶지 않은 명령을 열거
   → rm -rf, git push --force, DROP TABLE은 필수

2. 다음에 ask를 설정
   → 본인 확인이 필요한 "쓰기계·배포계"

3. 나머지를 allow
   → 읽기계·CI계는 전부 allow로 효율화

4. Hooks로 보안 자동화
   → 커밋 전 스캔, 타입 체크 후 자동 실행

5. 환경별 설정 파일 준비
   → settings.json (개발), settings.production.json (프로덕션)

권한 설정을 적절히 구성하면, “승인 버튼을 기계적으로 누르는” 습관이 없어지고, 정말로 확인이 필요한 조작에만 집중할 수 있게 됩니다. 처음 30분에 설계해 두면, 그 후의 수백 시간 작업이 안전해집니다.

관련 글

참고 자료

#claude-code #permissions #security #hooks #settings #configuration
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.