
주의력이 아닌 설정으로 고정하기 — 코딩 에이전트의 스코프와 권한 가드레일
요약
코딩 에이전트가 여러 리포지토리를 넘나들며 작업할 때 발생할 수 있는 실수와 권한 문제를 방지하기 위한 가드레일 메커니즘을 소개합니다. 프롬프트에 의존하는 대신 설정 파일을 통해 스코프와 권한을 명시적으로 선언하여 기술적으로 작업 범위를 제한합니다.
핵심 포인트
- 에이전트의 횡단 작업 시 발생할 수 있는 오작동 방지
- 세션(Session) 단위를 통한 스코프와 권한의 명시적 선언
- read, write-local, write-push의 3단계 권한 체계 도입
- 허가 리스트(Allowlist) 방식의 스코프 관리
코딩 에이전트(Claude Code와 같은)에게 작업을 시킬 때, 정말로 효과를 발휘하는 것은 여러 리포지토리를 횡단하는 상황입니다. 어떤 리포지토리의 구현을 참조하면서 다른 리포지토리에 작성한다. 공통 라이브러리를 수정하면서, 그것을 사용하는 쪽의 리포지토리에서 동작을 확인한다. 설정 리포지토리를 읽으면서, 생성물의 리포지토리를 업데이트한다. 단일 기능 수정보다, 이러한 「걸쳐 있는」 작업이 에이전트의 속도를 더 잘 살려줍니다.
하지만 횡단 작업은 사고의 가능성도 그만큼 넓힙니다. 참조만 하려던 리포지토리를 실수로 편집하거나, 다른 리포지토리의 main에 직접 push 해버리거나, 목적과 관계없는 파일을 건드리는 경우입니다. 리포지토리를 넘나들수록 「지금 무엇을 만져도 되는지, 어디까지 허용되는지」의 경계가 모호해지며, 에이전트는 그 경계를 매번 처음부터 다시 읽어야 하므로 인간의 주의력에 상당하는 것이 구조적으로 존재하지 않습니다. 따라서 「프롬프트로 주의 깊게 부탁하기」로는 근본적으로 방지할 수 없습니다.
그래서, 주의력이 아닌 설정 파일로 가드레일(Guardrails)을 강제하는 메커니즘을 구축했습니다. 생각은 단순합니다. 작업을 시작하기 전에 「걸쳐도 되는 리포지토리」와 「각각의 권한」을 선언하고, 그 외에는 기술적으로 불가능하게 만드는 것입니다. 횡단하는 범위 그 자체를, 처음에 설정으로서 고정해 버리는 것입니다.
세션 = 스코프 + 권한의 선언

부모가 스코프와 권한을 선언하여 세션을 생성한다. 횡단하는 각 리포지토리는 권한에 따라 취급이 달라지며, 「읽기 전용 리포지토리」는 구조적으로 다시 쓸 수 없다.
최소 단위를 「세션(Session)」이라고 부릅니다. 1 세션 = 1개의 작업 단위이며, 기동하는 시점에 다음 두 가지가 확정됩니다.
- 스코프 (Scope)… 어느 리포지토리 / 디렉토리를 만져도 되는가
- 권한 (Permission)… 대상마다 어디까지 허용할 것인가
권한은 3단계입니다.
read… 참조만 가능write-local… 편집 + 로컬 커밋까지. push 불가write-push… push까지 허용 (PR 생성 포함)
포인트는 권한을 대상마다 가질 수 있다는 점입니다. 횡단 작업에서는 1 세션 안에서 신뢰도가 다른 리포지토리가 당연하게 섞입니다. 「설정 리포지토리는 read로 참조만 하고, 생성물의 리포지토리는 write-push 하여 반영한다」, 「공통 라이브러리는 write-local로 수정하지만 push는 하지 않는다, 이용 측 리포지토리는 read로 동작 확인만 한다」——이러한 조합을 하나의 작업 단위로 다룹니다. 그래서 세션은 대상과 레벨의 쌍을 나열한 선언(manifest)을 한 장 가지고, 스코프·권한·동일성 판정을 모두 그곳에 집약합니다.
실제로 이 기사도 그러한 횡단 작업으로 작성하고 있습니다. 메커니즘의 출처인 리포지토리를 read로 참조하면서, 문장은 기사의 리포지토리에 write-push로 작성합니다. 읽는 쪽을 절대 바꾸지 않는다는 보장이 설정으로 작동하고 있기 때문에, 참조원을 망가뜨릴 걱정 없이 작성할 수 있습니다.
스코프는 「열려 있지 않은 것은 보이지 않는다」로 만든다
스코프는 허가 리스트(Allowlist) 방식입니다. 에이전트에게 전달할 액세스 가능한 디렉토리 목록에, 허가한 대상만 열거합니다. 열거되지 않은 리포지토리는 참조조차 할 수 없습니다. 심볼릭 링크를 거는 방식은 채택하지 않고, 설정 파일만으로 스코프가 완결되도록 했습니다.
「만질 수 있는 것을 더해가는 것」이지 「만질 수 있는 것에서 빼가는 것」이 아니라는 방향성이 중요합니다. 기본값이 닫혀 있기 때문에, 설정을 쓰는 것을 잊은 대상은 안전한 쪽(=보이지 않는 쪽)으로 기울어집니다.
핵심 1: 권한 정의 자체를 에이전트가 건드리지 못하게 한다
이 부분이 가장 효과적인 설계 판단입니다. 세션 내부에서 스코프와 권한의 정의 파일 자체를 수정할 수 없도록 만드는 것입니다.
이유는 이렇습니다. 에이전트에게 read 권한을 줄 때, 그 「무엇이 read인가」를 적은 설정 파일 또한 파일입니다. 만약 그것을 편집할 수 있게 되면, 에이전트는 「이 deny를 제거하면 쓸 수 있다」는 것을 깨닫고 스스로 가드레일을 완화할 여지가 생깁니다. 권한을 가진 주체가 자신의 권한 정의를 수정할 수 있어서는 안 됩니다. 따라서 권한 변경은 반드시 한 단계 위(세션을 생성하는 부모)에서만 수행할 수 있는 구조로 만들었습니다.
다만, 여기에는 구현상의 함정이 있습니다. 지키고 싶은 것은 「권한과 스코프의 정의 파일 그 자체」입니다. 그렇다고 해서 「Edit / Write...
「Edit / Write...을 통째로 금지하면 된다」라고 생각하면 실패합니다. 그렇게 하면 에이전트는 자신의 **작업 로그 (work log)**조차 쓸 수 없게 됩니다. 필요한 것은 「툴 단위의 금지」가 아니라 「특정 경로만 금지」하는 것입니다. 지키고 싶은 정의 파일만 지목하여 deny 하고, 세션의 작업 영역은 쓸 수 있는 상태를 유지합니다.
// 지키고 싶은 것은 세션 자신의 권한/스코프 정의. 그 부분만 지목하여 금지한다
"deny": [
"Write(.claude/**)", // 권한 정의 (settings)
...
반면, read 권한의 대상 리포지토리는 반대로 통째로 금지합니다. 읽기만 하는 것이므로, 해당 트리 전체를 Write / Edit 대상에서 제외하는 것이 가장 솔직하고 안전하기 때문입니다. 문제는 이 「통째로 금지」와, 아까의 「자신의 로그는 쓸 수 있게 한다」가 충돌했을 때 발생했습니다.
실제로 한 번 실수를 한 적이 있습니다. 세션 자신의 작업 디렉토리를 read 대상 리포지토리의 **하위에 중첩(nest)**하여 두었던 것입니다. read 대상을 트리째로 금지한 결과, 그 안에 있던 세션의 로그까지 휘말려 에이전트가 자신의 로그를 쓸 수 없게 되었습니다.
여기서 효과가 있었던 것은 「deny를 좁히는 것」이 아니라, 작업 영역과 스코프 대상을 물리적으로 분리하는 것이었습니다. 세션의 작업 디렉토리(로그나 임시 메모)를 애초에 보호 대상 트리 외부로 빼버리는 것입니다. 그렇게 하면 read 대상은 안심하고 통째로 금지할 수 있고, 세션은 자신의 로그를 자유롭게 쓸 수 있습니다. 지키는 범위를 깎아서 앞뒤를 맞추는 것이 아니라, 지킬 대상과 쓰는 장소가 겹치지 않도록 무대를 나누는 방식의 해결책이었습니다.
클라이맥스 2: push는 이중으로 단단하게
가장 무서운 것은 push입니다. read 리포지토리로 push 하는 것, main으로 직접 push 하는 것. 둘 다 되돌리기 어렵습니다.
단순하게는, push를 허용하는 커맨드의 **허가 리스트 (allowlist)**를 좁히면 막을 수 있습니다. 이는 에이전트의 부주의한 사고에는 효과적입니다. 하지만 견고함 측면에서는 불충분합니다. 허가 리스트는 「git push라는 형태의 커맨드인가」만을 보기 때문에, git -C <다른 리포지토리> push와 같이 작업 디렉토리를 전환하는 변종이나, 어느 리포지토리로・어느 브랜치로 push 하려고 하는지까지는 판정할 수 없습니다. 커맨드 형태의 대조만으로는 목적지의 권한까지는 지킬 수 없는 것입니다.
그래서 2단계로 구성했습니다.
- 허가 리스트로 「push 할 수 있는 대상」을 좁힘…
push를 허용하는 것은write-push권한을 가진 세션뿐입니다.read나write-local만 가진 세션에는 애초에push커맨드를 주지 않습니다. - 실행 직전 훅 (pre-execution hook)… 커맨드 문자열을 검사하여,
push대상 리포지토리의 경로로부터 권한을 가져와 판정합니다.
견고한 것은 두 번째 단계입니다. 실행되려는 커맨드 문자열 그 자체를 보고, git -C <path>나 작업 디렉토리로부터 push 대상 경로를 도출하여 매니페스트(manifest, 경로→권한)와 대조합니다. read / write-local 경로로의 push는 전부 차단하고, write-push 경로라도 main / master로의 직접 push는 차단합니다. push 대상을 특정할 수 없을 때는 안전한 쪽을 택해 거부하고, 「대상을 git -C <절대 경로>로 명시하라」고 유도합니다. 커맨드 형태의 허가로는 닿지 않는 「목적지별 권한」을 훅이 물리적으로 단단하게 굳히는 형태입니다.
끊겨도 재개할 수 있도록 하기
에이전트의 세션은 끊깁니다. 문맥(context)과 함께 사라지면 매번 처음부터 다시 시작해야 하므로, 작업 로그를 2층 구조로 남기고 있습니다.
- 스냅샷 (snapshot) (항상 최신·덮어쓰기)… 「우선 이것만 읽으면 재개할 수 있다」는 한 장의 요약. 현재의 태스크, 대상 리포지토리와 작업 브랜치, 수정한 파일, 다음 단계, 미결정 사항.
- 태스크 로그 (task log) (시계열·추가)… 경위와 결정 이유를 남기는 이력.
그리고 재개 시, 기동 훅을 통해 스냅샷을 자동으로 컨텍스트에 주입합니다. 에이전트는 아무런 지시를 받지 않아도 「이어서」 시작할 수 있습니다. 가드레일이 사고를 방지하는 메커니즘이라면, 이것은 작업을 잃지 않게 하는 메커니즘입니다.
현재의 위치와 앞으로
솔직히 말씀드리면, 이것은 아직 개인용입니다. 자신의 머신에서 자신의 에이전트에 가드레일을 걸기 위해 구축한 것이며, 실제 운영 시스템(production system)은 아닙니다.
단순히 「세션 = 스코프(Scope) + 권한(Permission)의 선언」이라는 형태 그 자체를 개인용으로만 한정할 필요는 없습니다. 향후에는 이를 리포지토리(Repository)로서 공개하여, 전역적으로 설치하고 누구의 머신에서든 동일하게 세션을 생성할 수 있도록 하는 방안을 검토하고 있습니다. 코딩 에이전트(Coding Agent)를 일상적으로 사용하는 사람이 늘어날수록, 「에이전트에게 무엇을 허용하고 무엇을 허용하지 않을 것인가」를 선언으로서 외부에 고정하려는 수요가 반드시 나타날 것이며, 이것이 공통의 기반이 되기를 바라고 있습니다.
에이전트를 똑똑하게 제어하려고 하다 보면, 자기도 모르게 똑똑한 프롬프트(Prompt)를 작성하고 싶어집니다. 하지만 사고를 진정으로 막아주는 것은 대개 똑똑함이 아니라, 「애초에 불가능하게 만들어 두는」 설정이었습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기