Manual 모드로 일하는 평범한 한 시간입니다. 에이전트가 프로젝트를 읽고, 수정안을 제안하고, 테스트 실행을 요청하고, 다시 수정안을 내고, 또 테스트를 요청합니다. 스무 번째 확인쯤 되면 거의 보지도 않고 "Yes"를 누르게 됩니다. 그러다 그 질문들 사이로, 프로젝트에서 오래전부터 아무도 건드리지 않았고 앞으로도 건드리면 안 되는 legacy/ 폴더의 수정이 슬쩍 지나갑니다.

이 글에서는 결정권이 실제로 여러분에게 있는 곳에만 확인 절차를 남기는 방법을 다룹니다. Claude Code에는 이를 위한 권한 규칙이 있습니다. 설정 파일의 세 목록이 에이전트가 바로 실행해도 되는 작업, 물어봐야 할 작업, 아예 해서는 안 되는 작업을 지정합니다. 왜 필요한지, 어떻게 읽는지, 이미 있을 가능성이 높은 규칙 파일을 어떻게 정리하는지 살펴보겠습니다.

모든 작업에 하나의 설정

권한 모드는 세션 전체를 조절하는 하나의 다이얼입니다. 어느 쪽으로든 돌릴 수 있지만, 모든 작업에 한꺼번에 적용됩니다.

엄격한 Manual 모드에서는 에이전트가 수정할 때마다, 그리고 거의 모든 명령 전에 확인을 요청합니다. 확인 요청은 여러분이 읽는 동안에만 보호 역할을 합니다. 질문이 많아지면 읽지 않게 되고, 보호는 서류상으로만 남습니다.

느슨한 acceptEdits 모드에서는 확인 없이 수정이 진행되고, rm, mv 같은 파일 명령도 마찬가지입니다. 직접 작성한 코드에는 편리합니다. 하지만 이 모드는 폴더를 구분하지 않습니다. legacy/의 수정도 프로젝트의 다른 곳에서처럼 조용히 처리됩니다.

CLAUDE.md에 "legacy/를 건드리지 마세요"라고 적을 수 있습니다. 에이전트는 따르려고 하겠지만, 이는 모델에 대한 요청이지 금지 규칙은 아닙니다. 작업이 실행되기 전에 Claude Code 자체가 권한 규칙을 확인하며, CLAUDE.md나 요청에 적은 내용은 규칙에 영향을 주지 않습니다.

작업마다 실수의 대가는 다른데 모드는 모두 똑같이 처리합니다. 테스트 실행은 묻지 않고 진행해도 됩니다. 다른 사람이 맡은 모듈의 수정은 보고 싶습니다. 동결된 폴더에는 에이전트가 아예 들어가지 않아야 합니다. 서로 다른 세 작업에 필요한 답도 세 가지인데, 모드 하나로는 구분할 수 없습니다.

세 가지 목록

규칙이 바로 이 세 가지 답을 제공합니다.

  • allow - 확인 없이 실행합니다. 불필요한 확인을 없애 주므로 자주 쓰는 안전한 작업이 더 이상 흐름을 끊지 않습니다;
  • ask - 항상 확인을 요청합니다. 어떤 모드에서든, 가장 느슨한 모드에서도 결정권이 여러분에게 있는 작업에 확인 절차를 남깁니다;
  • deny - 실행하지 않습니다. 에이전트는 거부 응답을 받고, 여러분에게는 확인 창조차 표시되지 않습니다. 이 규칙도 모든 모드에서 적용됩니다.

규칙은 설정 파일, 예를 들어 프로젝트의 .claude/settings.json에 기록합니다. 도입부의 프로젝트라면 다음과 같이 쓸 수 있습니다.

{
  "permissions": {
    "allow": [
      "Bash(npm test)"
    ],
    "ask": [
      "Edit(src/partner/**)"
    ],
    "deny": [
      "Edit(legacy/**)",
      "Bash(git push *)"
    ]
  }
}

여기서 src/partner/는 조심해서 수정하는 다른 사람의 모듈이고, legacy/는 프로젝트에서 동결된 부분입니다. npm test는 에이전트가 작업 하나를 진행하는 동안 열 번씩 실행하는 검사입니다. 별표 두 개는 "폴더 안의 모든 항목, 하위 폴더 깊이와 관계없이"라는 뜻입니다.

이제 세션에서 달라지는 점을 살펴보겠습니다. 같은 다섯 가지 작업을 세 가지 설정으로 비교합니다.

작업규칙 없는 Manual규칙 없는 acceptEdits규칙이 있는 acceptEdits
나머지 src/ 수정확인 요청확인 없음확인 없음
src/partner/ 수정확인 요청확인 없음확인 요청
legacy/ 수정확인 요청확인 없음거부
npm test확인 요청확인 요청확인 없음
git push확인 요청확인 요청거부

마지막 열을 보세요. 세션은 더 빠른 모드로 진행됩니다. 여러분의 코드는 멈춤 없이 수정하고 테스트도 자동으로 실행합니다. 확인 요청은 하나만 남았습니다. 실제로 수정 내용을 살펴보고 싶은 부분에 대한 요청입니다. 그리고 허용되지 않은 두 작업은 저녁에 얼마나 피곤한지에 더 이상 좌우되지 않습니다.

이것이 규칙을 두는 이유입니다. 에이전트에 권한을 더 주려는 것이 아니라, 실수의 대가에 따라 권한을 나누려는 것입니다. 그러면 더 자유로운 모드에서도 중요한 부분을 놓치지 않고 작업할 수 있습니다.

하나의 작업이 여러 규칙에 동시에 해당할 수 있으므로 Claude Code는 목록을 정해진 순서대로 확인합니다.

규칙 확인 순서입니다. 에이전트의 작업을 먼저 deny 목록과 비교합니다. 일치하면 거부됩니다. 일치하지 않으면 ask 목록을 확인합니다. 일치하면 어떤 모드에서든 확인을 요청합니다. 다음으로 allow 목록을 확인합니다. 일치하면 확인 없이 실행됩니다. 어느 목록에도 해당하지 않으면 모드가 결정합니다. Manual에서는 에이전트가 확인을 요청하고, acceptEdits에서는 수정이 자동으로 진행됩니다.에이전트 작업명령, 수정, 읽기deny와 일치하나요?ask와 일치하나요?allow와 일치하나요?거부작업이 실행되지 않음확인 요청모든 모드에서확인 없이바로 실행일치 항목 없음 - 모드가 결정Manual에서는 에이전트가 묻고, acceptEdits에서는 수정이 자동 진행예예예아니요아니요아니요
위에서부터 확인해 처음 일치하는 규칙이 결정합니다

도식은 위에서 아래로 읽고, 에이전트의 각 작업은 매번 처음부터 확인합니다. 규칙이 일치하는 순간 확인이 끝나며, 더 구체적인 규칙이라고 우선순위를 얻지는 않습니다. deny에 Bash(git push *)가 있으면 allow의 어떤 규칙도 push를 허용할 수 없습니다. 실수로 금지를 덮어쓸 수 없다는 점은 다행입니다.

아래쪽 사각형은 따로 기억해 두세요. 작업이 어느 목록에도 해당하지 않는다고 해서 "허용됨"을 뜻하지는 않습니다. 모드가 결정합니다. Manual에서는 에이전트가 묻고, acceptEdits에서는 수정이 자동으로 진행됩니다. 즉, 규칙은 모드를 없애는 게 아니라 여러분이 지정한 곳에서 모드를 구체화합니다.

규칙 읽는 법

거의 맞게 쓴 규칙은 규칙이 없는 것보다 나쁩니다. 규칙이 없으면 경계가 없다는 걸 알고 있습니다. 하지만 부정확한 규칙은 경계가 있다고 확신하게 만들면서도, 생각한 것과 다른 대상을 막습니다.

규칙 형식은 모두 같습니다. 도구 이름 뒤 괄호 안에 적용 대상을 적습니다. 괄호가 없으면 해당 도구의 모든 호출에 규칙이 적용되므로, deny의 Bash는 에이전트의 터미널 사용을 전부 막습니다.

파일

파일에는 두 가지 도구가 필요합니다. 모든 수정을 위한 Edit와 읽기를 위한 Read입니다. 경로는 .gitignore처럼 패턴으로 씁니다. 별표 하나는 한 폴더 안의 임의 이름, 별표 두 개는 깊이에 상관없는 모든 하위 경로를 뜻합니다. 앞에 슬래시가 없는 경로는 세션을 시작한 폴더를 기준으로 하므로 프로젝트 루트에서 Claude Code를 실행하세요.

흔한 실수는 Write(legacy/**)라고 쓰는 것입니다. 에이전트에게 그런 이름의 도구가 있기는 하지만, Claude Code는 파일 규칙을 Edit와 Read에 대해서만 확인합니다. Write 규칙은 받아들이면서 시작할 때 경고만 표시하고 이후에는 확인하지 않습니다. 반면 Edit는 새 파일 만들기를 포함해 파일을 변경하는 모든 내장 방식을 다룹니다.

또 한 가지 편리한 점이 있습니다. 예를 들어 deny에 Read(./.env)를 넣어 읽기를 금지하면 에이전트가 그 파일을 수정하는 것도 막을 수 있습니다.

명령

Bash(...) 규칙은 명령 텍스트를 비교합니다. 별표는 공백을 포함한 모든 텍스트를 대신합니다. 규칙이 일치하는지는 다음 세 가지에 달려 있습니다.

  • 별표가 없으면 규칙은 한 명령과 글자 단위로 정확히 일치합니다;
  • 별표는 하위 명령 뒤에 둡니다. Bash(git log *)는 어떤 플래그가 붙어도 git log만 허용하지만, Bash(git *)는 push를 포함한 모든 git 명령을 허용합니다;
  • 별표 앞의 공백도 규칙의 일부입니다. Bash(ls *)는 lsof와 일치하지 않지만, Bash(ls*)는 일치합니다.
명령 규칙이 텍스트와 어떻게 일치하는지 보여 줍니다. Bash(git log *) 규칙에서는 git log 부분을 글자 단위로 비교하고, 별표가 --oneline main을 포함하므로 어떤 플래그가 붙든 git log만 허용합니다. Bash(git *) 규칙에서는 git만 글자 단위로 비교하고, 별표가 push origin main을 포함하므로 push를 포함한 모든 git 명령을 허용합니다.Bash(git log *)Bash(git *)git log만, 플래그는 무엇이든push를 포함한 모든 git 명령git log--oneline maingitpush origin main별표 앞: 글자 단위로 정확히별표 자리: 모든 텍스트
별표 앞의 텍스트가 규칙의 범위를 정합니다

파란 부분은 명령과 글자 단위로 비교하고, 주황 부분은 별표가 무엇이 들어 있든 전부 포함합니다. 따라서 규칙의 범위는 별표 앞에 적힌 내용으로 정해집니다. 첫 번째 규칙은 git log이므로 기록 조회 외에는 허용하지 않습니다. 두 번째 규칙은 git만 지정하므로 별표 뒤에는 push, 브랜치 삭제, 변경 사항 되돌리기가 올 수도 있습니다. 다른 사람의 규칙을 읽을 때는 이 질문부터 시작하세요. 별표 앞에 무엇이 있나요?

규칙일치불일치
Bash(npm test)npm testnpm test --watch
Bash(npm run *)npm run build, npm run lint --fixnpm install
Bash(git log *)git log, git log --onelinegit push
Bash(git *)모든 git 명령, git push 포함-
Edit(src/partner/**)src/partner/ 아래 모든 파일 수정src/app.js 수정
Read(./.env)프로젝트 루트의 .env 읽기.env.example 읽기

Claude Code는 a && b처럼 여러 부분으로 된 명령을 나눠서 확인합니다. allow는 모든 부분이 각각 허용 규칙에 맞아야 실행합니다. ask와 deny는 한 부분이라도 일치하면 적용됩니다. 따라서 npm test를 허용해도 npm test && git push까지 허용되지는 않습니다.

다른 도구를 위한 규칙도 있습니다. 예를 들어 mcp__github__*는 하나의 MCP 서버에 속한 모든 도구에 적용되고, WebFetch(domain:example.com)은 특정 사이트의 페이지를 가져올 때 적용됩니다. 원리는 같으니 여기서 따로 살펴보지는 않겠습니다.

규칙을 둘 파일

잘못된 파일에 규칙을 두면 조용히 문제가 생깁니다. 개인 파일에 금지를 넣으면 동료에게는 적용되지 않아, 동료의 에이전트가 legacy/에 아무렇지 않게 들어갑니다. 반대로 개인에게 편리한 허용 규칙을 공용 파일에 넣으면 프로젝트를 내려받는 모든 사람에게 전달됩니다.

파일누구에게 적용되는 규칙인가요?Git에 포함되나요?
~/.claude/settings.json이 컴퓨터의 모든 프로젝트에서 여러분에게 적용아니요. 홈 폴더에 있습니다
.claude/settings.json프로젝트에서 일하는 모든 사람이 공유예. 커밋합니다
.claude/settings.local.json이 프로젝트에서만 여러분에게 적용아니요

이 세 파일 외에도 조직에서 자체 설정 파일을 배포할 수 있으며, 그 파일의 규칙은 덮어쓸 수 없습니다. 학습용 프로젝트에는 보통 이런 파일이 없습니다.

서로 다른 파일의 목록은 충돌하는 대신 합쳐집니다. 여러분 파일의 allow가 공용 파일의 allow에 추가됩니다. 확인 순서는 모두 같고, 어느 파일의 deny든 다른 파일의 allow보다 우선합니다. 따라서 간단히 나눌 수 있습니다. legacy/, .env, push처럼 프로젝트의 경계에 해당하는 규칙은 저장소와 함께 전달되도록 공용 파일에 둡니다. 개인 작업 방식에 맞춘 편의 규칙은 로컬 파일에 둡니다.

공용 파일에는 보호를 위한 특징이 하나 있습니다. 폴더를 신뢰한다고 확인한 뒤에야 그 파일의 allow 규칙이 작동합니다. 프로젝트에서 처음 Claude Code를 실행하면 신뢰 여부를 묻고, 해당 파일이 허용하려는 내용을 보여 줍니다. ask와 deny는 제한만 하므로 바로 적용됩니다. 다른 사람의 저장소에 터미널 전체를 허용하는 규칙이 있어도 여러분이 동의하기 전에는 적용되지 않습니다.

실제로 적용 중인 규칙은 두 명령으로 확인할 수 있습니다. /permissions는 모든 규칙과 각 규칙이 저장된 파일을 보여 주며, 여기서 규칙을 추가하거나 삭제할 수도 있습니다. /status의 Setting sources 항목에는 불러온 파일이 나옵니다. Claude Code는 파일 변경 사항을 재시작 없이 반영합니다. 설정 파일은 엄격한 JSON입니다. // 주석이나 마지막 항목 뒤의 쉼표가 있으면 파일이 유효하지 않게 되며, Claude Code가 시작할 때 경고합니다.

자동으로 작성되는 파일

에이전트가 명령에 관해 물었을 때 "Yes, and don't ask again"을 선택하면 Claude Code가 allow 규칙을 .claude/settings.local.json에 기록합니다. 파일이 아직 없으면 새로 만들고 커밋에 포함되지 않도록 처리합니다. 파일 수정은 다릅니다. 그런 승인은 세션이 끝날 때까지만 유효하고 파일에는 기록되지 않습니다.

실제 settings.local.json 살펴보기

여러분에게 이 파일이 이미 있을 가능성이 높지만, 직접 작성한 파일은 아닐 겁니다. "다시 묻지 않기"를 선택할 때마다 한 줄씩 추가되고, 몇 주가 지나면 생각보다 많은 작업을 에이전트에 허용하게 됩니다. 다음은 Python 프로젝트에서 몇 주간 작업하며 실제로 쌓인 블록입니다.

{
  "permissions": {
    "allow": [
      "Bash(python3 *)",
      "Bash(PYTHONIOENCODING=utf-8 python3 *)",
      "Bash(git status *)",
      "Bash(git log *)",
      "Bash(git diff *)",
      "Bash(git show *)",
      "Bash(git branch *)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(git fetch *)"
    ]
  }
}

분석을 읽기 전에 직접 생각해 보세요. 어떤 줄은 유지하고, 어떤 줄은 범위를 좁히며, 어떤 줄은 없애야 할까요?

Bash(python3 *)
파일에서 가장 범위가 넓은 줄입니다. 어떤 스크립트든 실행할 수 있고 python3 -c로 어떤 코드든 실행할 수 있습니다. Python으로는 파일 삭제, 네트워크 접근, 다른 프로그램 실행까지 모두 가능합니다. 사실상 묻지 않고 무엇이든 실행하도록 허용하는 규칙입니다. 이 규칙이 생긴 이유인 스크립트로 범위를 좁히는 편이 좋습니다. 예를 들면 Bash(python3 scripts/build.py *)처럼 설정할 수 있습니다.

Bash(PYTHONIOENCODING=utf-8 python3 *)
앞에 환경 변수가 붙은 같은 명령입니다. allow 규칙은 미리 안전하다고 알려진 짧은 목록에 있는 변수만 이런 형태로 허용합니다. 목록에 변수가 없으면 Bash(python3 *)는 이 명령에 적용되지 않으므로 에이전트가 별도로 물어보고, 버튼을 누르면 규칙이 하나 더 저장됩니다. 아마 이런 식으로 이 줄이 생겼을 겁니다. 이 규칙도 특정 스크립트로 범위를 좁히면 됩니다.

git status, git log, git diff, git show
일반적인 형태로는 이 명령들이 읽기만 합니다. Claude Code는 읽기 전용 명령을 규칙 없이도 확인을 요청하지 않고 실행합니다. 이 줄들이 해를 끼치지는 않지만 추가하는 것도 거의 없습니다. 삭제하면 파일이 더 짧고 이해하기 쉬워집니다.

Bash(git branch *)
브랜치 목록만 보는 것이라 무해해 보입니다. 하지만 규칙은 텍스트를 비교하므로 git branch -D feature도 git branch로 시작합니다. 브랜치 삭제가 확인 없이 진행됩니다. 이 줄은 지우는 편이 좋습니다. 브랜치 목록에 대한 확인 요청이 거슬리기 시작하면 별표 없이 정확한 명령인 Bash(git branch)만 허용하세요.

git add와 git fetch
첫 번째는 이후 커밋에 포함할 변경 사항을 추가하고, 두 번째는 서버에서 최신 기록을 가져오지만 작업 파일은 건드리지 않습니다. 둘 다 쉽게 되돌릴 수 있으므로 유지해도 됩니다.

Bash(git commit *)
에이전트가 diff를 읽기도 전에 확인 없이 커밋합니다. 바로 이런 곳에 확인 절차가 필요합니다. 커밋은 변경 사항에 동의하는 시점이기 때문입니다. 이 줄은 ask로 옮기는 편이 좋습니다. 그냥 삭제하는 것만으로는 부족합니다. Manual에서는 어차피 에이전트가 물어보지만, ask는 가장 느슨한 모드를 포함해 모든 모드에서 확인을 유지합니다.

파일에 없는 규칙
git push입니다. 지금은 괜찮지만, 누군가 push에서 "다시 묻지 않기"를 누른 적이 없다는 데 기대고 있습니다. 금지 규칙을 명시적으로 적는 편이 더 안전합니다.

분석한 결과는 다음과 같습니다.

{
  "permissions": {
    "allow": [
      "Bash(python3 scripts/build.py *)",
      "Bash(PYTHONIOENCODING=utf-8 python3 scripts/build.py *)",
      "Bash(git add *)",
      "Bash(git fetch *)"
    ],
    "ask": [
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

열 줄 중 여섯 줄만 남았고, 각 줄은 의도한 역할을 합니다. 작업 중 확인 요청은 거의 늘지 않았습니다. 빌드는 자동으로 진행되고 에이전트는 커밋 전에 한 번만 묻습니다. push 금지가 팀 전체에 적용되어야 한다면 앞 절의 기준에 따라 공용 .claude/settings.json으로 옮기는 편이 좋습니다.

프로그램 전체에 대한 허용

Bash(python3 *), Bash(node *), Bash(npm *), Bash(npx *) 같은 규칙은 하나의 도구만 허용하는 것처럼 보입니다. 실제로는 그 도구가 실행할 수 있는 모든 코드를 허용합니다. 프로그램 전체가 아니라 특정 스크립트나 특정 명령을 허용하세요.

규칙으로 막을 수 없는 것

에이전트가 거부 응답을 받았습니다. 에이전트에게는 일반적인 도구 응답일 뿐이므로 작업은 계속됩니다. 때로는 같은 일을 할 다른 방법을 찾기도 합니다. 이때 규칙이 정확히 무엇을 확인하는지 알아야 합니다.

Bash(...) 규칙은 실행될 프로그램이 아니라 명령 텍스트를 비교합니다. 따라서 금지 규칙은 일반적인 명령 형태는 막지만 모든 형태를 막지는 못합니다.

  • deny의 Bash(git push *)는 git -C . push를 막지 못합니다. 같은 명령이지만 표기가 다릅니다;
  • Bash(rm *)는 /bin/rm이나 bash -c 'rm ...'를 막지 못합니다;
  • Read(...) 금지 규칙이 있어도 Python 스크립트가 직접 파일을 여는 것을 막지는 못합니다.

그렇다고 규칙을 포기할 필요는 없습니다. 에이전트는 거의 항상 명령을 일반적인 형태로 작성하며 deny는 바로 그 형태를 잡아냅니다. 다만 규칙은 완전한 방벽이 아닙니다. Claude Code 문서에도 분명히 나와 있습니다. 에이전트가 우회 방법을 찾지 않도록 CLAUDE.md에도 금지 이유를 설명하세요. "legacy/는 동결된 폴더입니다. 작업에 이곳의 수정이 필요하면 멈추고 확인을 요청하세요." 규칙은 막고, CLAUDE.md는 이유를 설명합니다. 둘을 함께 쓰면 각각 따로 쓸 때보다 효과적입니다.

경계를 확실하게 유지해야 한다면 다른 방법도 있습니다. 실행 전에 각 명령을 확인하는 hook과 시스템 수준에서 접근을 제한하는 sandbox입니다. 이는 별도로 다룰 주제입니다.

일상에서 쓸 규칙

몇 가지 습관으로 정리하면 다음과 같습니다.

  • deny에는 프로젝트에서 어떤 경우에도 허용할 수 없는 작업을 넣습니다. 동결된 폴더, .env, push 등이 해당합니다;
  • ask에는 여러분이 결정해야 하는 경계 사례만 넣습니다. 모든 작업에 ask를 적용하면 확인 요청에 지치는 상황이 다시 생깁니다;
  • allow에는 범위를 좁혀 반복되는 작업만 넣습니다. 구체적인 검사나 특정 스크립트가 좋습니다;
  • 프로젝트 경계는 공용 .claude/settings.json에, 개인 편의 설정은 .claude/settings.local.json에 둡니다;
  • 가끔 /permissions를 열어 "다시 묻지 않기"를 눌러 쌓인 규칙을 정리하세요;
  • 새 규칙을 추가한 뒤에는 에이전트에게 금지된 작업을 요청해 거부 응답이 오는지 확인하세요.
규칙 초안은 에이전트에게 맡길 수 있습니다

프로젝트의 각 영역을 에이전트에게 설명하세요. 어디는 자유롭게 수정해도 되고, 어디는 확인이 필요하며, 어디에는 들어가면 안 되는지 알려 주세요. permissions 블록을 제안해 달라고 한 다음, 위의 분석과 같은 질문을 떠올리며 저장 전에 JSON을 읽어 보세요. 규칙의 범위가 필요 이상으로 넓지는 않나요? 올바른 파일에 들어가나요?

모드는 작업의 전반적인 속도를 정하고, 규칙은 어디에서 속도를 늦출지 결정합니다. 둘을 함께 쓰면 어떤 모드 하나만으로는 얻을 수 없는 것을 갖게 됩니다. 실수의 대가가 낮은 곳에서는 빠르게 일하고, 대가가 큰 곳에서는 확인을 요청하거나 작업을 거부합니다.