
Claude Code를 여러 리포지토리와 문맥에서 안전하게 운용하기 위한 실무 가드레일
요약
여러 리포지토리를 병행 사용하는 환경에서 Claude Code를 안전하게 운용하기 위한 실무 가드레일 전략을 다룹니다. 권한 설정(Permission mode)과 훅(Hook)을 활용하여 불가역적이고 고위험인 조작을 단계적으로 차단하는 다층 방어 구조를 제안합니다.
핵심 포인트
- Claude Code의 빠른 속도보다 중요한 것은 사고 없는 안전한 운용임
- 읽기 및 소규모 편집은 허용하되, 고위험 조작은 단계적으로 차단하는 구조 필요
- Permission mode와 Hook을 결합한 다층적 가드레일 구축 권장
- 문맥 오인으로 인한 잘못된 커밋 및 삭제 등 불가역적 사고 방지
이 기사는 Claude Code를 「공략하는」 이야기가 아닙니다.
이미 업무에 도입하여 여러 리포지토리와 여러 문맥을 병행하여 돌리기 시작한 사람이, 사고를 치지 않기 위해 무엇을 준비해야 하는지에 대한 운용 이야기입니다.
계기는 제가 여러 서비스를 병행하여 개발 및 유지보수하게 된 것이었습니다.
본업 리포지토리, 부업 리포지토리, 개인 개발 리포지토리.
각각 별도의 브랜치 운용, 별도의 배포처, 별도의 기밀 정보가 있습니다.
Claude Code는 이러한 「병행 작업」과 궁합이 좋은 반면, 문맥이 섞이는 순간 사고를 치는 범위도 넓어집니다.
도입 직후에는 「어쨌든 빠르다」는 점에 눈이 가기 쉽습니다.
하지만 운용이 한 바퀴 돌고 나면, 관심은 조용히 옮겨갑니다.
「어떻게 하면 빠르게 돌릴 것인가」에서 「어떻게 하면 사고 없이 돌릴 것인가」로 말입니다.
이 기사는 후자에 집중하고 있습니다.
- 가드레일 없이 Claude Code를 돌렸을 때 일어나기 쉬운 사고의 유형
- 사고를 기계적·운용적으로 방지하는 가드레일을 7가지 항목으로 제시 (각 항목에 「좋지 않은 예 → 대처 → 왜 좋아지는가」를 첨부합니다)
- 각 가드레일이 효과적인 이유를 공격 면, 실패 구조, 인간의 리뷰와의 대비를 통해 한 단계 더 깊게 파고듭니다
- 여러 리포지토리만의 함정 (문맥 오인 · 잘못된 커밋 대상)
- 가드레일을 도입한 후 운용이 어떻게 변했는지, 남은 과제
반대로, Claude Code의 설치 방법이나 기본 조작은 다루지 않습니다.
그 부분은 공식 문서가 잘 갖춰져 있으므로 그쪽에 맡기겠습니다.
- Claude Code를 업무에서 사용하기 시작하여 여러 리포지토리를 횡단하고 있는 분
- AI에게 맡기는 범위를 넓히고 싶지만, 불가역적 조작 (삭제 · 공개 · 전송 · 과금)이 두려운 분
- 팀에 도입함에 있어 안전 운용을 위한 초안이 필요한 분
전제로서, 몇 가지 양해 말씀을 먼저 드립니다.
- Claude Code는 업데이트가 빠른 도구입니다. hook이나 permission mode의 사양은 변할 수 있으므로, 설정 예시는 「생각의 최소 샘플」로 읽어주시기 바랍니다. 실제로 적용할 때는 공식 문서에서 최신 사양을 확인해 주시기 바랍니다.
- 여기서 소개하는 것은 「내 환경에서는 이렇게 운용하여 안착했다」라는 하나의 사례입니다. 베스트 프랙티스(Best Practice)는 상황에 따라 다르므로 단정 짓지 않겠습니다. 맞지 않는 부분은 상황에 맞게 해석해 주세요.
- 코드, 설정, 디렉토리 이름은 모두 가상의 것으로 대체했습니다. 실재하는 환경을 재현한 것이 아닙니다.
- 설정 예시의 일부는 생성 AI의 도움을 받아 정리했지만, 사양은 공식 문서로 확인하였으며 내 환경에서 의도한 대로 동작함을 확인했습니다.
동작 확인의 전제는 Claude Code의 2026년 6월 시점의 공식 문서 (Hooks / Permissions / Subagents)입니다. hook 스크립트 예시는 bash와 jq를 전제로 하고 있습니다. 동작은 버전에 따라 변할 수 있으므로 최신 사양 확인을 권장합니다.
그럼, 먼저 「가드레일이 없으면 어떤 일이 일어나는가」부터 살펴보겠습니다.
먼저, 앞으로 만들 안전 장치의 전체상을 한 장으로 보여드립니다.
요점은 「읽기나 작은 편집은 빠른 상태로 통과시키고, 불가역적·고위험 조작만을 단계적으로 차단한다」는 다층 구조입니다.
그림과 같이, 확인의 강도를 결정하는 permission mode와 기계적으로 차단하는 hook을 겹칩니다.
한쪽이 느슨해지더라도 다른 한쪽이 남는 것이 목적입니다.
가드레일 이야기에 들어가기 전에, 막고자 하는 대상을 구체화하겠습니다.
추상적으로 「위험합니다」라고 말해도 납득하기 어려우므로, 자신이나 주변에서 실제로 목격한 「아찔했던 순간」을 유형화하여 나열합니다.
개별 고유명사는 숨기고, 재현 조건만 남깁니다.
가장 무서운 것은, 사라지면 되돌릴 수 없는 조작입니다.
「불필요한 로그를 정리해 줘」라고만 부탁했더니, 예상보다 넓은 범위를 한꺼번에 지우려고 했던 상황이 있습니다.
Claude Code 자체는 확인 과정을 거치는 설계이지만, bypassPermissions나 넓은 allow 규칙으로 권한을 완화한 상태에서 돌리고 있으면 그 확인 과정이 생략됩니다.
git 관리하의 파일이라면 복구할 수 있는 경우가 많습니다.
하지만 .env나 생성물, git 관리 외의 로컬 데이터는 되돌릴 수 없습니다.
「되돌릴 수 있다고 생각했던 것이 사실은 되돌릴 수 없었다」가 사고의 본질입니다.
여기에는 AI 특유의 실패 구조가 한 단계 더 있습니다.
인간이라면 rm -rf logs/를 입력하기 전에 logs/ 안의 내용을 잠시 상상합니다.
AI도 문맥에 따라 확인을 하지만, 지시가 모호하면 「정리해 줘」를 가장 솔직하게 충족하는 명령어로 치우칠 수 있습니다.
의도의 최소 충족이 최대의 파괴로 이어질 수 있습니다. 여기가 바로 함정입니다.
여러 리포지토리(Repository)를 가로질러 작업하다 보면, 어떤 문맥에서 본 기밀 정보가 다른 문맥의 출력에 섞여 들어갈 위험이 발생합니다.
예를 들어, API 키나 클라이언트 이름이 조사 메모나 커밋 메시지, 기사 초안에 남게 되는 경우입니다.
이것은 "AI가 악의적으로 유출하는" 문제가 아닙니다.
인간이 붙여넣은 것을 AI가 그대로 이어받아 버리는 문제입니다.
입력에 기밀 정보가 섞여 있다면 출력에도 섞일 수 있습니다. 이 부분을 구조적으로 차단해야 합니다.
공격 표면(Attack Surface)의 관점에서도 이 유형은 간과할 수 없습니다.
AI에게 외부 문서나 코드를 읽게 하면, 그 안에 "기밀 정보를 출력하라"는 취지의 문장이 섞여 있을 수 있습니다.
이른바 프롬프트 인젝션 (Prompt Injection)입니다.
인간의 주의력만으로는 이러한 "읽은 내용에 의해 조작되는" 경로를 완전히 막을 수 없습니다.
"이 파일의 버그를 수정해 줘"라고 요청했을 때, 파일 전체를 읽지 않고 일부만 수정하면 다른 전제 조건을 망가뜨릴 수 있습니다.
예를 들어, 어떤 함수의 반환값(Return Value) 형식을 변경했더니, 같은 파일 내의 다른 호출부(Caller)가 조용히 고장 나는 패턴입니다.
이는 AI 특유의 문제는 아니며, 인간의 리뷰에서도 발생합니다.
다만, AI는 "빠르게 작성할 수 있는" 만큼, 읽지 않고 작성하려는 유혹이 강합니다.
속도가 빠른 도구일수록, 읽는 과정을 건너뛰었을 때의 피해가 빠르게 확산됩니다.
구현이 끝난 직후, 테스트도 빌드도 타입 체크(Type Check)도 거치지 않고 "완료했습니다"라고 보고합니다.
그대로 믿고 커밋(Commit)하면, 나중에 CI가 실패합니다.
컨텍스트(Context)가 길어져 남은 용량이 부족해지면, 이러한 "검증 건너뛰기"가 일어나기 쉬워집니다.
"동작할 것이다"라는 추측만으로 멈추는 것이 가장 위험합니다.
동작의 근거(테스트 결과, 빌드 로그)가 나오기 전까지는 완료된 것이 아닙니다.
이 부분은 후반부에서 자세히 다루겠지만, 여러 리포지토리를 열어두고 있으면,
"A 리포지토리에 대해 이야기하고 있다고 생각했는데, B 리포지토리의 파일을 건드리고 있는" 상황이 발생합니다.
커밋 대상을 잘못 지정하면, 다른 프로젝트의 이력에 무관한 변경 사항이 섞이게 됩니다.
이상의 5가지 유형이 앞으로 소개할 가드레일(Guardrail)이 방지하고자 하는 대상입니다.
공통점은 "빠르기 때문에 확인, 검증, 문맥 확인이라는 느린 과정을 건너뛰기 쉽다"는 것입니다.
따라서 가드레일은 그 느린 과정을 기계와 운영을 통해 강제적으로 되돌리는 메커니즘이 됩니다.
불가역적 조작(Irreversible Operation)이란, 한 번 수행하면 되돌릴 수 없거나(또는 되돌리는 비용이 극도로 높은) 조작을 말합니다.
구체적으로는 삭제, 덮어쓰기, 전송, 공개, 커밋/푸시(Commit/Push), 과금 등이 있습니다.
이 부분을 "AI의 판단만으로 진행하게 하지 않는 것"이 가드레일의 가장 기본이자 핵심입니다.
이유는 단순합니다. 오류의 기대값이 비대칭적이기 때문입니다.
읽기 오류는 다시 하면 됩니다.
하지만 삭제나 공개 오류는 되돌릴 수 없거나, 신뢰나 금전적 손실을 초래합니다.
기대값이 비대칭이라면, 대책의 무게 또한 비대칭으로 가져가는 것이 이치에 맞습니다.
권한을 전면적으로 완화하여 확인 없이 무엇이든 실행할 수 있게 만드는 운영 방식이 있습니다.
속도는 빠릅니다.
하지만 삭제, 출력, 전송, 과금까지 체크 없이 통과하게 되면, 단 한 번의 착오로 돌이킬 수 없는 상황이 됩니다.
# 좋지 않은 운영 이미지 (개념적 예시이며, 실제 명령어가 아닙니다)
모든 조작을 확인 없이 자동 승인함
→ 빠르지만, 삭제·공개·과금까지 그대로 통과해 버림
Claude Code에는 bypassPermissions라는, 확인 과정을 거의 건너뛰는 모드도 있습니다.
공식 문서에서도 컨테이너나 VM 등 "망가져도 영향이 국한되는 환경"에서의 이용으로 한정할 것을 주의사항으로 명시하고 있습니다.
일상적인 리포지토리 작업에서 상용하는 모드가 아니라는 점을 이해해야 합니다.
불가역적 조작만큼은 반드시 인간의 확인을 거치는 운영 방식을 취해야 합니다.
권한 모드를 "편집은 자동으로 수용하되, 위험한 조작은 확인한다" 정도로 설정하는 것이 현실적인 타협점입니다.
Claude Code에는 여러 가지 권한 모드(Permission Mode)가 있으며, 확인의 강도를 단계별로 선택할 수 있습니다.
2026년 6월 기준 대표적인 모드는 다음과 같습니다 (모드 구성은 업데이트될 수 있으므로 최신 버전은 공식 문서를 확인하십시오. 여기에 나열된 것은 대표적인 예시이며 전부가 아닙니다).
| 모드 | 대략적인 동작 | 적합한 상황 |
|---|---|---|
default | 도구의 첫 사용 시마다 확인 | 신중하게 진행하고 싶을 때 |
acceptEdits | 파일 편집 및 일부 안전한 작업을 자동 승인 | 편집 속도를 높이고 싶을 때 |
plan | 읽기 및 읽기 전용 명령만 수행. 소스는 편집하지 않음 | 먼저 읽고 계획하고 싶을 때 |
bypassPermissions | 확인을 거의 건너뜀 (일부는 안전장치 있음) | 격리된 환경으로 한정 |
이 중 plan 모드는 특히 유용합니다.
실제로 접하기 전에 "무엇을 할 것인지"를 제시하게 하며, 편집 계열 도구를 사용하지 못하게 하는 동작을 합니다.
비가역적(irreversible) 작업 전에 한 박자 쉬어가는 것, 즉 본 가드레일의 사상과 잘 맞닿아 있습니다.
permission mode는 "전체적인 확인의 강도"를 결정하는 거친 다이얼입니다.
반면, 특정 작업만 콕 집어 멈추고 싶다면 설정 파일의 권한 규칙(permission rules)을 사용할 수 있습니다.
규칙은 deny (거부) $\rightarrow$ ask (확인) $\rightarrow$ allow (허가) 순으로 평가되며, 가장 먼저 일치하는 것이 적용됩니다.
예를 들어, 편집이나 Bash는 허용하면서 강제 push만 확인하도록 지정하는 식입니다.
{
"permissions": {
"allow": [
...
더불어, 운영 규칙으로서 CLAUDE.md와 같은 프로젝트 지시 파일에 다음과 같은 한 문장을 넣어둡니다.
## 확인이 필요한 작업
이하는 실행 전에 반드시 사람에게 확인한다. AI 단독으로 진행하지 않는다.
- 삭제・덮어쓰기 (git 관리 외 포함)
...
CLAUDE.md의 지시는 "AI가 무엇을 하려고 하는가"를 형성하는 것입니다.
공식 문서에 나와 있듯이, 권한의 경계 그 자체는 권한 규칙이나 hook이 결정합니다.
자연어 주의 사항에만 의존하지 말고, 기계적인 메커니즘과 병용하십시오.
이렇게 하면 "빠르고 괜찮은 작업"과 "멈춰야 할 작업"이 분리됩니다.
읽기나 작은 편집은 빠른 상태로 진행되고, 비가역적 작업에만 인간의 리뷰가 들어갑니다.
속도와 안전의 트레이드오프 (trade-off)를 작업 종류별로 나누어 관리할 수 있다는 점이 효과적입니다.
한 가지 보충하자면,
이 규칙은 "AI를 신뢰하지 않기 때문"이 아닙니다.
사람 사이에서도 운영 환경 삭제나 릴리스에는 확인 과정을 거치는 것이 일반적입니다.
그와 같은 예법을 AI를 파트너로 삼았을 때도 그대로 가져오는 것뿐입니다.
가드레일 1은 "운영 규칙"과 "권한 규칙"이었습니다.
다만, 운영 규칙은 깨지기 마련입니다.
사람도 AI도 급할 때일수록 규칙을 건너뜁니다.
따라서 정말 위험한 것은 코드로 물리적으로 막는 것이 확실합니다.
여기서 사용할 수 있는 것이 Claude Code의 hook입니다.
hook은 도구 실행 전후 등 정해진 타이밍에 자체 스크립트를 삽입할 수 있는 메커니즘입니다.
공식 문서에는 PreToolUse나 PostToolUse를 비롯하여 많은 hook 이벤트가 나열되어 있습니다.
특히 PreToolUse hook은 도구가 실행되기 직전에 개입하여 실행을 거부할 수 있습니다.
권한 규칙과의 차이점도 파악해 두면 좋습니다.
권한 규칙은 "명령어 문자열 패턴"으로 허가·확인·거부를 결정합니다.
hook은 임의의 스크립트이므로, 패턴으로는 표현하기 어려운 조건(여러 요소의 조합이나 외부 상태)도 판정할 수 있습니다.
공식 문서에서도 인자(argument)로 제한하는 방식의 권한 규칙은 취약할 수 있다고 주의하며, 확실한 차단을 위해 hook을 권장하고 있습니다.
rm -rf와 같은 파괴적인 명령어를 규칙(자연어 주의 사항)만으로 막으려는 운영 방식입니다.
주의 사항이 효과가 있을 때도 있지만, 보장되지는 않습니다.
컨텍스트(context)가 길어지면 서두의 주의 사항이 약해지는 경우도 있습니다.
# 좋지 않은 운영 이미지
CLAUDE.md에 "rm -rf는 사용하지 말 것"이라고 적기만 함
$\rightarrow$ 지켜질 확률은 높아지지만, 기계적인 보장은 없음
PreToolUse hook을 통해 위험한 명령어를 감지하면 실행을 거부합니다.
공식 문서(2026년 6월 기준)에 따르면, PreToolUse hook에는 두 가지 중단 방법이 있습니다.
하나는 종료 코드(exit code) 2를 반환하는 방법, 다른 하나는 종료 코드 0으로 permissionDecision이 "deny"인 JSON을 반환하는 방법입니다.
먼저 settings.json 측의 설정입니다.
matcher
도구 이름(여기서는 Bash)을 지정하고, 훅 스크립트 (hook script)를 연결합니다.
여기서는 모든 Bash 호출을 스크립트를 통하게 하며, 위험 패턴 판정은 스크립트 측에서 일괄적으로 수행합니다.
{
"hooks": {
"PreToolUse": [
...
개별 핸들러(handler)에 if (권한 규칙 구문)를 추가하면 대상을 더욱 좁힐 수 있습니다.
단, if로 대상을 좁히면 해당 조건에 일치하지 않는 종류의 커맨드는 핸들러 자체가 실행되지 않습니다.
하나의 스크립트로 여러 패턴(rm -rf 및 force push 등)을 한꺼번에 검사하고 싶다면 if를 붙이지 마세요.
if를 사용한다면, 스크립트의 검사 대상과 커맨드 종류를 일치시켜야 합니다.
다음은 훅 스크립트의 최소 예시입니다.
표준 입력(stdin)으로 전달되는 JSON에서 커맨드 문자열을 추출하고, 위험 패턴에 해당하면 종료 코드(exit code) 2로 거부합니다.
종료 코드 2인 경우, 표준 에러 출력(stderr)의 텍스트가 '거부 이유'로서 AI에게 반환됩니다.
#!/usr/bin/env bash
# guard-bash.sh
# 위험한 Bash를 PreToolUse로 차단하는 최소 샘플 (가상/최소화)
...
JSON으로 반환하는 방식도 익혀두면, 거부 이유를 구조화하여 전달할 수 있습니다.
공식 문서의 예시를 따라 hookSpecificOutput 아래에 permissionDecision을 배치합니다.
이 방식에서는 종료 코드가 0인 상태를 유지하면서, JSON을 통해 판단을 전달합니다.
#!/usr/bin/env bash
# guard-bash-json.sh — JSON으로 deny를 반환하는 버전 (가상/최소화)
set -euo pipefail
...
이 스크립트를 실행 가능하게 만들어 배치하면, rm -rf를 포함한 Bash 호출은 실행 전에 차단됩니다.
실행 이미지는 다음과 같습니다.
# AI가 rm -rf를 포함한 커맨드를 내보내려고 하면…
Blocked by guard-bash.sh: matched /rm[[:space:]]+-rf/
→ 커맨드는 실행되지 않고, AI에게는 거부 이유가 반환됨
참고로 matcher는 도구 이름의 완전 일치 외에도, Edit|Write와 같은 파이프 구분자나 정규 표현식(regular expression)도 사용할 수 있습니다.
정규 표현식은 mcp__memory__.*와 같이 MCP 도구군을 한꺼번에 대상으로 지정할 수도 있습니다.
훅(hook)은 쉘(shell)의 구조까지 완전히 해석하지는 않습니다.
예를 들어, 커맨드를 변수를 경유하거나 별칭(alias)을 통해 구성할 수 있다면 단순한 문자열 패턴은 빠져나갈 수 있습니다.
훅을 '모든 것을 막는 벽'이라고 과신하지 말고, 후술할 권한 규칙이나 샌드박스(sandbox)와 병행하는 것이 안전합니다.
가장 큰 장점은 자연어 주의 사항보다 강제력이 높다는 점입니다.
주의 사항은 AI가 문맥을 놓치면 빠져나갈 수 있지만, 훅은 도구 호출 직전에 기계적인 검사를 끼워 넣을 수 있습니다.
훅은 실행 경로 사이에 위치하므로 문맥에 좌우되기 어렵습니다 (단, 후술하는 바와 같이 문자열 검사 자체가 만능은 아닙니다).
공식 문서에서도 종료 코드 2인 훅은 허가 규칙보다 우선하여 중단된다고 설명되어 있습니다.
또 하나, 훅은 '최후의 보루'로 배치하는 것이 요령이라고 생각합니다.
일상적인 확인은 가드레일 1(운영 + permission mode + 권한 규칙)로 받고,
그럼에도 불구하고 발생하는 정말 위험한 것들만 훅으로 걸러냅니다.
이중 구조로 만들면 한쪽이 느슨해지더라도 다른 한쪽이 남게 됩니다.
한 가지 주의할 점이 있습니다.
훅을 너무 많이 늘리면 정당한 작업까지 중단되어 업무가 지연됩니다.
차단 대상은 '비가역적이고 고위험(high-risk)'인 것에 집중하는 것이 오래 사용하는 데 있어 중요하다고 느낍니다.
유형 3에서 다룬 '읽지 않은 채 편집하는' 사고를 입구에서 막는 가드레일입니다.
원칙은 심플합니다. 변경하기 전에 대상 파일을 읽는 것입니다.
읽지 않은 파일은 편집하지 않습니다.
사람의 리뷰에서도 '차이점(diff)만 보고 주변을 보지 않으면' 사고가 발생합니다.
AI는 빠르게 작성할 수 있는 만큼, 이러한 방심이 나타나기 쉽습니다.
그렇기에 '읽고 나서 쓴다'를 명시적인 규칙으로 만듭니다.
실패의 구조를 한 단계 더 파고들면, 원인은 '국소 최적화(local optimization)'입니다.
요청받은 한 줄을 채우는 것이 목적이라면, 주변을 읽지 않아도 변경 사항을 작성할 수 있습니다.
하지만 코드는 반환 값의 형태나 인수의 개수와 같은 '계약(contract)'을 통해 다른 부분과 연결되어 있습니다.
계약을 읽지 않고 국소적인 부분만 수정하면, 계약의 너머가 조용히 망가집니다. 이것이 미독 편집 (Unread Editing)의 본질입니다.
파일 전체의 전제를 확인하지 않고, 지시된 부분만 다시 쓰는 패턴입니다.
코드 리뷰에서 자주 발생하는 "수정했다고 생각했는데 망가뜨린" 변경 사항을 최소 사례로 보여드리겠습니다.
// 변경 전: 호출 측은 { total }을 받는다는 전제
function summarize(items) {
const total = items.reduce((acc, x) => acc + x.price, 0);
...
여기서 "합계뿐만 아니라 건수도 반환해줘"라는 요청을 받고, 반환 값의 형태만 바꿨다고 가정해 봅시다.
// 좋지 않은 변경: 반환 값의 형태는 바꿨지만, 호출 측을 확인하지 않음
function summarize(items) {
const total = items.reduce((acc, x) => acc + x.price, 0);
...
호출 측은 { total }을 구조 분해 할당 (Destructuring assignment)으로 받는 전제였습니다.
이 변경으로 인해 total은 undefined가 되어, 표시가 망가집니다.
호출 측(읽지 않은 파일)을 읽었더라면 방지할 수 있었던 사고입니다.
"변경 전에 대상 파일과 그 호출 측을 읽는다"를 규칙화합니다.
프로젝트 지시 파일에 다음과 같은 원칙을 적어둡니다.
## 구현 원칙 (Research-first)
- 변경하기 전에 대상 파일을 반드시 읽는다.
- 읽지 않은 파일은 편집하지 않는다.
...
그 후에 반환 값의 형태를 바꾼다면, 영향 범위를 확인한 뒤 기존의 전제를 유지합니다.
// 좋은 변경: 기존의 { total }을 망가뜨리지 않고, 건수를 추가하는 형태로 만듦
function summarize(items) {
const total = items.reduce((acc, x) => acc + x.price, 0);
...
"읽는" 공정을 거치면 변경의 영향 범위가 보입니다.
공개 인터페이스 (Public Interface)를 변경할 때 참조 지점을 훑는 습관이 생겨, 조용히 망가지는 사고가 줄어듭니다.
이는 속도를 희생하는 것처럼 보이지만, 사실 장기적으로는 더 빨라집니다.
미독 편집으로 망가뜨리고, CI에서 이를 인지하고, 원인을 추적하여 수정하는 과정. 이 왕복 과정이 처음에 읽는 것보다 훨씬 더 많은 시간을 잡아먹기 때문입니다.
plan 모드는 이 공정과 궁합이 좋습니다.
읽기 전용 도구는 사용할 수 있지만 편집은 할 수 없으므로, "먼저 읽고 계획을 세운다"를 자연스럽게 강제할 수 있습니다.
유형 4 "검증 스킵"에 대한 대책입니다.
구현이 끝나더라도 테스트, 빌드, 타입 체크 (Type check)가 통과될 때까지는 완료가 아닙니다.
"동작할 것이다"에서 멈추지 말고, "동작한다는 증거"를 내놓게 합니다.
특히 컨텍스트 (Context)가 길어져 남은 용량이 힘들어지면, AI도 인간도 검증을 건너뛰고 싶어 합니다.
힘들 때일수록 이곳을 규율로 지키는 것이 효과적입니다.
왜 "동작할 것이다"가 위험할까요?
코드를 작성한 직후의 자신감은 작성한 본인(AI든 인간이든)의 내부 모델 (Internal model)에 의존합니다.
내부 모델이 올바르다면 동작하겠지만, 그것이 올바르다는 보장은 어디에도 없습니다.
테스트나 빌드는 그 내부 모델을 외부에서 검산하는 절차입니다.
검산을 건너뛰면, 자신감이 맞는지 아무도 확인하지 않은 채 앞으로 나아가게 됩니다.
테스트를 실행하지 않고 "구현이 완료되었습니다"라고 보고하고, 그대로 커밋 (Commit)으로 진행하는 흐름입니다.
# 좋지 않은 흐름
구현한다 → "완료했습니다" → 그대로 커밋
→ CI에서 타입 에러 · 테스트 실패가 발각됨
"완료"의 정의에 검증을 포함합니다.
프로젝트 지시 파일에 완료 조건을 명문화합니다.
## 완료의 정의
다음 사항이 모두 통과되어야 비로소 "완료"로 간주한다.
- 해당 범위의 테스트가 통과됨
...
마무리 검증을 한 개의 명령어로 묶어두면 건너뛰기 어려워집니다.
예를 들어, 타입 체크, Lint, 테스트를 직렬로 실행하는 가벼운 스크립트를 둡니다.
#!/usr/bin/env bash
# verify.sh — 완료 전에 한꺼번에 검증한다 (가상 · 최소화)
set -euo pipefail
...
실행 결과는 이런 형태로 남습니다.
이 로그가 나온 후에 완료하는 운용 방식으로 합니다.
==> typecheck
==> lint
==> test
...
나아가, PostToolUse 계열의 hook을 통해 "편집이 들어가면 자동으로 포맷팅 · Lint를 적용한다"는 운용을 해두면 검증의 일부를 자동화할 수 있습니다.
다만 무거운 처리를 매번 실행하면 느려지기 때문에, 대상과 빈도를 좁히는 것이 좋다고 느낍니다.
완료 정의에 검증을 넣으면, "동작할 것이다"라는 주관이 "동작했다"라는 객관으로 바뀝니다.
커밋 전에 CI와 동일한 체크를 로컬에서 마칠 수 있으므로, 나중에 작업을 되돌리는 리워크 (rework)가 줄어듭니다.
이것은 AI를 의심하는 문제가 아니라, 자신을 위한 안전망 (safety net)이기도 합니다.
사람이 작성하더라도 검증 없는 커밋은 실패합니다.
도구가 AI로 바뀌더라도, 안전장치는 동일한 것을 사용할 뿐입니다.
조사나 병렬 작업을 서브 에이전트 (sub-agent, 별도 컨텍스트의 작업자)에게 맡기면, 메인 컨텍스트 (context)를 절약할 수 있습니다.
공식 문서에 따르면, 서브 에이전트는 각각 독립된 컨텍스트 윈도우 (context window)에서 동작하며, 메인에 반환되는 것은 "요약"뿐입니다.
상세한 (verbose) 로그나 읽어들인 파일은 서브 에이전트 측의 컨텍스트에 남고, 부모에게는 반환되지 않습니다.
이 설계는 컨텍스트 절약에는 효과적이지만, 한편으로는 독특한 함정을 만들어냅니다.
중간 작업 로그가 부모에게 공유되지 않기 때문에, "조사했습니다", "완료했습니다"라는 말만 돌아오면 근거가 수중에 남지 않습니다.
근거 없는 결론은 검증할 수 없으므로 사용할 수 없습니다.
게다가 최신 버전에서는 서브 에이전트가 다시 서브 에이전트를 실행할 수 있으며, 부모에게 반환되는 것은 최상위 요약뿐이라는 동작도 있습니다.
계층이 깊어질수록 중간 근거는 부모로부터 보이지 않게 됩니다.
위임은 했으나 요약만 돌아오는 패턴입니다.
# 위임했는데 이렇게 돌아옴
"조사했습니다. 문제없었습니다."
→ 무엇을 읽었는지, 무엇이 근거인지 알 수 없음
이래서는 부모 측에서 그 결론을 믿어도 될지 판단할 수 없습니다.
제 운용 방식에서는 백그라운드 태스크 (background task)의 알림만 믿었다가, 결과물을 회수하지 못하는 경우가 있었습니다.
위임 시의 프롬프트 (prompt)에서 최종 리포트 형식을 강제합니다.
구체적으로는 결론, 근거, 미확인 사항을 나누어 전문을 반환하게 합니다.
## 서브 에이전트에게 요청 (최종 리포트 강제)
최종 메시지에 다음 내용을 전문 포함하여 반환해 주세요.
중간 로그만으로 끝내지 마세요.
...
중요한 조사나 백그라운드 실행 태스크는 결과를 파일로도 저장하게 하여 이중화합니다.
부모 측은 반환값이 비어 있더라도 파일에서 결과를 회수할 수 있습니다.
## 중요·백그라운드 태스크의 이중화
조사 결과를 outputs/ 디렉토리 하위 파일에도 저장해 주세요.
저장 후, 부모에게 "저장 경로 + 요약 + 미확인 사항"을 반환해 주세요.
...
최종 리포트를 강제하면 결론에 반드시 근거가 따라옵니다.
근거가 붙으면 부모 측에서 "이 결론을 채택해도 되는가"를 검증할 수 있습니다.
추측과 사실이 분리되어 있으므로, 미확인 상태로 진행되는 사고도 줄어듭니다.
경험상 빈 답변이 돌아오는 주요 원인은 세 가지라고 느낍니다.
지시 이행 능력이 약한 모델을 사용했을 때, 백그라운드 태스크의 알림에 본문이 실리지 않았을 때, 긴 작업의 마무리 문구만 반환될 때입니다.
이 세 가지 모두 "최종 메시지에 결과물을 전문 포함하기", "파일로 이중화하기"를 통해 크게 완화할 수 있었습니다.
위임은 1인당 1태스크로 좁히고, 독립적인 것들만 병렬로 실행하는 것이 다루기 쉽습니다.
무리하게 병렬화하면 컨텍스트가 섞여 통합이 오히려 어려워집니다.
유형 2 "기밀 혼입"에 대한 대책입니다.
여러 컨텍스트를 횡단하다 보면, 특정 컨텍스트의 기밀 값이 다른 컨텍스트의 출력에 섞여 들어갈 리스크가 높아집니다.
클라이언트 이름, 계약 내용, 금액, API 키.
이것들이 조사 메모나 커밋 메시지, 기사 초안에 남게 되면 그 영향은 외부까지 미칩니다.
기밀이 사고로 이어지기 쉬운 이유는 혼입 경로가 여러 개이기 때문입니다.
사람이 기밀 파일을 컨텍스트에 붙여넣거나, AI가 기밀 파일을 읽으러 가거나, 읽은 외부 문서의 지시에 따라 출력하는 경우입니다.
입구가 여러 개이므로 한 곳만 막는다고 해서 유출을 막을 수 없습니다.
따라서 "입구마다 층을 겹치는 것"이 기본 자세가 됩니다.
기밀 정보를 포함한 파일을 무방비하게 읽게 만드는 운용입니다.
입력에 기밀 값이 들어가면 출력에도 들어갈 수 있습니다.
# 좋지 않은 운용
.env 나 고객 리스트를 컨텍스트에 통째로 읽게 하여 요약에 포함해 버림
→ 클라이언트 이름이나 키가 메모나 커밋에 남음
층을 겹칩니다.
하나는 운용 규칙, 하나는 권한 규칙, 하나는 훅 (hook)입니다.
운용 규칙으로는 출력에 쓰지 않을 것을 명문화합니다.
## 기밀 취급
다음은 출력, 메모, 기사, 커밋 메시지의 어느 곳에도 쓰지 않는다.
- 클라이언트 이름 / 계약 / 금액
...
권한 규칙으로는 기밀 파일의 읽기를 deny로 차단할 수 있습니다.
공식 문서에 따르면, Read의 deny 규칙은 gitignore 형식의 패턴으로 작성할 수 있으며, 내장된 파일 읽기 도구뿐만 아니라 cat 등 파일을 읽는 Bash 도구도 대상이 됩니다.
{
"permissions": {
"deny": [
...
나아가 hook을 통해서도 동일한 계층의 방어막을 추가할 수 있습니다.
PreToolUse hook을 사용하여 기밀 파일에 대한 읽기 도구 호출을 차단하는 예시입니다.
다음 예시는 Read 도구의 file_path를 대상으로 합니다.
Grep · Glob이나 cat을 사용하는 Bash는 입력 필드가 다르기 때문에, matcher를 Read로 한정하거나 도구별로 분기 처리해야 합니다.
#!/usr/bin/env bash
# guard-secret-read.sh — 기밀 파일 읽기 거부 (가상/최소화 예시)
set -euo pipefail
...
여기에 더해, 리포지토리 측에서도 .gitignore와 비밀 탐지(커밋 전에 토큰으로 의심되는 문자열을 검출하는 메커니즘)를 병용하면 방어 계층이 더욱 두터워집니다.
권한 규칙이나 hook은 Claude Code의 내장 도구에는 적용되지만, Python이나 Node 스크립트가 파일을 직접 여는 경로까지는 차단하지 못합니다.
OS 레벨에서 모든 프로세스를 제약하고 싶다면, sandbox(샌드박스) 이용도 선택지가 될 수 있습니다.
운영 규칙만으로는 "실수"를 완전히 막을 수 없습니다.
권한 규칙과 hook을 추가하면 기밀 파일이 애초에 문맥(Context)에 포함되지 않으므로, 출력에 섞여 나올 경로를 차단할 수 있습니다.
"인간의 주의"와 "기계적 차단"을 중첩하는 것이 기밀 보호 측면에서 효과적입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기