Tips & Tricks

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

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

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

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

권한 설정의 전체 구조

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

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

설정은 .claude/settings.json에 작성합니다. 프로젝트 루트에 두면 git으로 팀과 공유할 수 있고, ~/.claude.json에 두면 글로벌 설정이 됩니다.

우선순위 (높은 순):
프로젝트 .claude/settings.json
    > 글로벌 ~/.claude.json
        > 기본값 (전체 ask)

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=.claude/settings.production.json claude로 지정합니다.

패턴 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 자동 실행이나 완전히 파악한 자동화 스크립트에서만 사용하고, 일상적인 대화에서는 피해야 합니다.

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

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

~/.claude.json              ← 글로벌 (전체 프로젝트 공통)
    +
.claude/settings.json       ← 프로젝트 (git 관리)
    +
.claude/settings.local.json ← 개인 덮어쓰기 (gitignore 권장)
    =
머지된 설정이 적용됨

개인용 추가 설정.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. 프로덕션 환경 설정을 수동으로 전환하는 것을 잊기

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

# ✅ 프로덕션 작업 전에 명시적으로 설정을 전환
CLAUDE_SETTINGS=.claude/settings.production.json claude

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

# ~/.bashrc or ~/.zshrc
alias claude-prod='CLAUDE_SETTINGS=.claude/settings.production.json claude'

설정 디버그 방법

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

# 현재 설정 확인
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

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.

개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.

Masa

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.