프런트엔드 AX 설계기 9편 - 외부 이슈 트래커 write 도구를 권한 경계 안에 가두기
트래커 MCP를 붙인 날, 공들여 세운 권한 경계가 조용히 뚫렸다
사내 워크플로에 외부 이슈 트래커(여기서는 'Jira류 이슈 트래커'로 부른다)의 MCP를 붙인 날이었다. 붙이자마자 에이전트는 플랜을 진행하다가 스스로 티켓을 만들고, 상태를 옮기고, 담당자를 바꿀 수 있게 됐다. 처음엔 좋아했다. "이제 플랜이랑 트래커가 알아서 붙는구나."
다음 날 아침에 불편해졌다. 나는 이 시리즈 1·2·8편에서 슬래시 커맨드·에이전트·자율 모드의 권한을 한 겹씩 좁혀 왔다. 그런데 방금 붙인 이 write 도구는 그 경계를 전부 우회해서 팀 바깥 시스템을 바꾸고 있었다. 내가 문 앞에 세워 둔 검문소를, 뒷문으로 들어온 손님이 그냥 지나친 셈이다.
MCP(Model Context Protocol)는 에이전트가 외부 도구·데이터에 붙는 표준 인터페이스다(2026년 6월 기준으로 생태계가 빠르게 커지는 중이다). 문제는 도구의 종류다. 읽기 도구는 상태를 안 바꾸지만, write 도구 — 티켓 생성·수정·전이 — 는 팀 바깥 상태를 바꾸는 부수효과(side-effect)다. 권한 등급으로 치면 이건 내가 가장 조심하던 쪽, 외부로 나가는 변경을 낼 수 있는 빌더급(우리 설정에서 쓰기 권한이 가장 넓은 실행 에이전트)이다.
write 도구의 기본값은 "게이트 우회"다
8편에서 나는 publishing gate를 세웠다. 되돌릴 수 없거나 팀 바깥으로 나가는 작업 앞에는 사람이 선다는 규칙이다. 그런데 외부 write 도구는 그 앞에 서지 않는다. 아무도 시키지 않아도 티켓을 만들 수 있다는 건, 정의상 게이트를 우회한다는 뜻이다.
더 위험한 건 번짐이다. 티켓을 자동으로 옮길 수 있으면 "이왕이면 커밋도, 이왕이면 푸시도, PR도 자동으로"라는 유혹이 자연스럽게 따라온다. write 권한 하나를 열어 두면 그 옆의 권한까지 슬금슬금 자동화되기 쉽다.
게다가 git에는 원래 제동 장치가 있다. push 하려면 원격이 있어야 하고, PR을 열려면 브랜치가 먼저 올라가 있어야 한다. 그런데 트래커 write 도구에는 그런 마찰이 없다. 토큰 하나면 티켓이 즉시 팀 바깥에 생긴다. 마찰이 없다는 건 도구가 경계를 대신 지켜 주지 않는다는 뜻이고, 그러면 경계는 내가 직접 세워야 한다.
그래서 열되, 딱 세 조건으로만 열기로 했다. 자동 실행은 (1) 플랜 lifecycle 이벤트에 묶여서, (2) registry에 allowlist 된 프로젝트에서만, (3) 문서에 미리 선언된 자동 side-effect로만 일어난다. 그리고 커밋·푸시·PR로는 절대 확장하지 않는다. 이 셋은 여전히 사람이 서는 게이트로 남긴다. 이 문장을 글로벌 설정 문서와 권한 경계 매트릭스에 그대로 성문화했다 — 코드가 아니라 문장으로 못을 박아 둬야, 다음에 도구를 하나 더 붙일 때 경계가 흔들리지 않는다.
상태 머신이 사람의 작업 흐름을 따라가게
허용한 자동화는 딱 하나의 상태 머신이다. 플랜 lifecycle을 트래커 상태에 이렇게 매핑했다.

플랜을 등록하면 티켓을 만들되 담당자는 비워 둔다. 아직 누가 잡을지 모르니까. 플랜이 진행으로 전이하는 순간 상태를 "In Progress"로 옮기고, 그때 실제 작업자로 담당자를 지정한다. cleanup 단계에서 리뷰로 전이한다. 핵심은 담당자를 "미지정 → 실제 작업자"로 바꾸는 지점이 '진행 전이'라는 것이다. 트래커의 상태 머신이 사람의 작업 흐름을 그대로 따라간다.
그리고 이 화살표 어디에도 커밋·푸시·PR은 없다. 그림에서 아래쪽 벽 바깥에 있는 것들이다. 자동으로 번질 뻔한 부수효과를, 다이어그램 한 장으로 "여기까지"라고 눈에 보이게 그어 뒀다.
경로가 아니라 이름으로 프로젝트를 가둔다
처음엔 체크아웃 경로로 프로젝트를 식별했다. 곧 깨졌다. 팀원마다 저장소를 클론 한 위치가 달라서, 경로 기반 규칙은 내 노트북에서만 동작했다. 그래서 registry를 프로젝트 이름 → 트래커 매핑으로 다시 짰다. 이름은 사람마다 같지만 경로는 사람마다 다르다.
// plan-sync.registry.json
// 프로젝트를 "이름"으로 식별한다(경로 X: 체크아웃 위치가 사람마다 다르므로 이식성 0).
{
"projects": {
"project-a": { // 실제 내부 프로젝트명 대신 가명
"tracker": "B-system", // A 프로젝트 → B 시스템 라벨(가명 매핑)
"project_key": "BSYS",
"sync": true // allowlist 플래그: 여기 없거나 false면 자동 동기화 skip
}
},
"assignees": { // 팀원 매핑도 registry로 관리하고
"alice": "alice@example.com", // 설정 저장소의 배포 명령으로 각 머신에 동일하게 뿌린다
"bob": "bob@example.com"
}
}
고정된 인원이라 "굳이 JSON이냐, 코드에 박으면 안 되냐"를 실제로 따져봤다. 결론은 JSON이었다. 매핑이 코드가 아니라 데이터일 때 배포·수정·리뷰가 가장 단순했다. 그리고 lifecycle 후킹은 이렇게 선언한다. 문장으로 먼저 못을 박고, 훅은 그 문장을 그대로 옮긴 것뿐이다.
# 선언된 자동 side-effect (권한 경계 문서에 명문화)
# registry.sync == true 인 프로젝트에 한해, 플랜 lifecycle 이벤트에서만 트래커 write 허용.
on plan.register(project):
if not registry[project].sync: return # allowlist 밖 → 조용히 skip (에러로 멈추지 않음)
ticket = tracker.create(title=plan.title, assignee=None) # 등록: 담당자 미지정
on plan.progress(project, worker):
if not registry[project].sync: return
tracker.transition(ticket, "In Progress")
tracker.assign(ticket, registry.assignees[worker]) # 진행 전이: 실제 작업자로 지정
on plan.cleanup(project):
if not registry[project].sync: return
tracker.transition(ticket, "In Review") # 리뷰로 전이
# 경계: commit / push / PR 는 이 훅 어디에도 없다 — 영원히 사람 게이트로 남긴다.
edge case를 두 개 심어 뒀다. 하나는 allowlist 밖 프로젝트를 만났을 때 에러로 파이프라인을 멈추지 않고 조용히 skip하는 것 — 동기화는 부가 기능이지 본류가 아니니, 없으면 없는 대로 지나가야 한다. 다른 하나는 다음 절의 함정이다.
상류가 바뀌면 나도 바뀐다 — 감수하기로 한 결합
처음엔 조용히 어긋났다. 플랜은 진행으로 넘어갔는데 트래커의 티켓은 등록 상태 그대로였다. 로그를 열어 보니 어느 날부터 create가 실패하고 있었다 — 트래커 운영 방식이 바뀌어 티켓 생성에 필수 필드가 하나 늘어 있었던 것이다. 나는 훅을 고쳐 그 필드를 채워야 했다. 외부 write에 자동 권한을 준다는 건, 상류 시스템의 운영 변경에 내 워크플로가 묶인다는 뜻이다. 공짜 자동화는 없었다.
그래도 감당할 만했던 이유는 경계를 좁혀 뒀기 때문이다. 자동 side-effect가 lifecycle 세 지점에만 있으니, 고칠 곳도 딱 그 세 군데였다. 만약 write 도구를 열어 두고 여기저기서 자유롭게 부르게 놔뒀다면, 상류 한 번 바뀔 때마다 어디가 깨졌는지 찾아 헤맸을 것이다. 경계는 권한을 위한 것이면서, 동시에 유지보수를 위한 것이기도 했다.
정리하며
외부 MCP write 도구는 기본이 빌더급 권한이라, 붙이는 순간 공들여 세운 내부 게이트를 우회한다. 자동 실행은 "lifecycle 이벤트 + allowlist 프로젝트 + 문서에 선언된 side-effect" 안에서만 안전하고, 커밋·푸시·PR로는 절대 번지지 않게 벽을 세워야 한다. 편의(자동 동기화)와 경계(자동으로 넘지 못할 선)는 따로 오지 않는다 — 같이 설계하는 것이다.
지금 연결해 둔 write 도구를 하나 골라, 그게 자동으로 실행돼도 되는 lifecycle 이벤트를 딱 한 줄로 적어 보라. 그리고 나머지 부수효과는 전부 사람 게이트로 남겨라.
이 편은 시리즈 1·2·8편에서 세운 내부 권한 경계(슬래시 커맨드·에이전트·자율 모드)를, 팀 바깥으로 나가는 외부 MCP로 확장한 기록이다. 사람이 쓰는 화면이든 에이전트가 만지는 외부 표면이든, "어디까지 자동이고 어디서 사람이 서는가"를 정하는 일은 결국 인터페이스 설계다. 그래서 외부 도구에 권한을 나눠 주는 이 일도, 여전히 프런트엔드의 일이다.
2026.07.01 - [AI 엔지니어링] - 프런트엔드 AX 설계기 8편 - 자율 모드에서 설계한 건 자동화가 아니라 멈추는 지점이었다