서론
저는 소규모 사업자를 위한 업무 소프트웨어를 혼자 만들고 있습니다. 개발은 AI 에이전트(Claude Code)에게 맡기고 있습니다.
세 번째 글에서는 AI를 전용 Windows 계정으로 분리하고, GitHub 키를 가지지 못하게 한 이야기를 썼습니다. 마지막에 '푸시(push)의 담당은 이 형태 이후로도 더 변했습니다'라고 적었습니다. 이번 글이 그 후속 내용입니다.
AI 작업용 계정과 별도로, 푸시와 PR 생성만 전담하는 계정을 만들었습니다. 저는 이것을 Broker(중개자 역할)라고 부릅니다.
이전 형태: 푸시 담당은 두 번 바뀌었다
Broker에게 푸시를 맡기기까지 담당은 두 번이나 변경되었습니다.
이 글에서는 AI에게 작업을 요청하거나 결과를 확인하는, 정리 역할을 하는 Claude를 '오퍼레이터의 Claude Code(Operator's Claude Code)'라고 부릅니다.
단계 1: 오퍼레이터의 Claude Code가 정해진 스크립트로 푸시 (2026년 9월)
처음에는 푸시 전용의 작은 스크립트를 만들고, 오퍼레이터의 Claude Code에게 실행시키도록 했습니다. 병합(merge)은 제가 담당했습니다. 스크립트 내용은 다음과 같습니다.
- GitHub 키는 하나의 리포지토리에만 적용되는 fine-grained token입니다.
- 스크립트는 master가 아닌 브랜치만 푸시합니다. 강제 푸시, 브랜치 삭제, 태그(tag)는 거부합니다.
- GitHub Actions 설정(
.github/workflows/)을 변경하는 것도 거부합니다.
순수한 git push는 금지한 채, 이 스크립트 호출만 허용 목록에 넣었습니다. 그런데 오퍼레이터의 Claude Code가 호출하기 전에 다른 명령어를 연결하는 것만으로도 허용 목록과 맞지 않아 차단되는 경우가 있었습니다. 그럴 때는 제가 대신 실행했습니다. 이는 첫 번째 글에서 쓴 '규칙은 명령어 문자열을 대조할 뿐'이라는 내용의 반대 측면 이야기입니다.
여기서 깨달은 것은, token에는 브랜치 제한이 없다는 것이었습니다. '내용 작성' 권한만 있으면 브랜치 삭제나 병합(merge)도 할 수 있게 됩니다. 거부하는 역할은 스크립트에만 있었습니다. GitHub 측에서 브랜치를 보호하는 기능은 private 리포지토리에서는 유료 플랜이 필요하여 사용할 수 없었습니다.
단계 2: 제가 직접 푸시
10월 3일, AI의 작업을 전용 계정으로 옮겼습니다(세 번째 글). 이때 푸시 스크립트도 오퍼레이터의 Claude Code에게 맡기지 않고, 당분간은 제가 실행하기로 결정했습니다.
이로써 GitHub에 기록하는 것은 저 혼자만 하게 되었습니다. 하지만 AI 작업이 진행될수록, 푸시할 때마다 제 손이 필요합니다. 제가 자리를 비우면 그곳에서 멈춥니다.
단계 3: Broker가 푸시
그래서 푸시를 시스템에 맡기기로 했습니다. 그것이 바로 이 글의 Broker입니다.
세 개의 계정으로 분리했다
이에 따라 역할을 세 가지로 나누었습니다.
| 계정 | 역할 | GitHub 키 |
|---|
| 나 | 승인 · 병합(merge) · GitHub Actions 설정 변경 | 저의 것 |
| ... | | |
| Broker 역시 관리자가 아닌 표준 사용자입니다. Broker의 키는 Broker 계정만 읽을 수 있는 파일에 보관합니다. AI 계정이나 제가 평소 사용하는 계정에서도 읽을 수 없습니다. | | |
Broker의 키는 GitHub App으로 했다
Broker에게 token이 아닌, 직접 만든 GitHub App의 키를 가지게 했습니다. 이유는 권한을 세밀하게 제한할 수 있기 때문입니다.
- 포함되는 것은 이 리포지토리 단 하나뿐입니다.
- 권한은 내용 작성 · PR 작성 · 체크 결과 작성 및 몇 가지 읽기 전용만 부여했습니다.
GitHub Actions 설정 변경 권한과 리포지토리 관리 권한은 주지 않았습니다.
- 작업할 때마다, 그 작업에 필요한 최소한의 권한만을 가진 token을 만들어서 사용합니다.
Broker의 코드는 사용할 수 있는 GitHub 작업을 목록으로 정해 놓았습니다. 이 목록에는 merge를 넣지 않았습니다. 병합(merge)은 제가 합니다.
요청 흐름
AI가 Broker에게 요청하는 것은 파일을 통해 전달합니다.
- AI는 작업한 브랜치(branch)를 하나의 파일(git bundle)로 만들고, 요청 파일과 함께 Broker의 '접수' 폴더에 놓습니다.
- Broker가 접수 파일을 자신의 위치로 가져옵니다.
- Broker가 규칙에 맞는지 확인합니다.
- 적합하면 push하고, PR(Pull Request)을 만듭니다.
- 결과를 기록하고, AI가 읽을 수 있는 '알림' 폴더에 통지사항을 놓습니다.
접수 폴더는 AI가 파일을 놓을 수는 있지만, 삭제하거나 수정할 수는 없도록 했습니다. 한 번 놓인 요청은 나중에 AI가 변경할 수 없습니다.
Broker가 확인하는 8가지 규칙
Broker는 다음 8가지가 모두 성립될 때만 push합니다.
- 전달된 브랜치가 master가 아니며, 이름 규칙에 맞는 것
- bundle 파일이 손상되지 않은 것
- bundle 안에 브랜치가 정확히 1개이며, 그 시작 커밋(commit)이 요청에 적힌 것과 같은 것
- 실제로 가져온 커밋도 요청에 적힌 것과 같은 것
- master와의 분기점(divergence point)이 발견되는 것
- 차이점에 GitHub Actions 설정 변경 사항이 없는 것
- push 권한이 유효한 상태인 것
- 동일 커밋의 push를 3회 이상 시도하지 않은 것
Broker는 시스템이기 때문에 AI의 보고를 기계적으로 판단합니다. “테스트가 통과했습니다”, “이 커밋입니다”라고 요청에 적혀 있어도, 그것을 확인하는 자료로 삼지 않습니다. Broker가 bundle을 열고 커밋을 비교한 사실만으로 결정합니다. 두 번째 글의 'AI의 '통과'를 믿지 않기'를 시스템화한 형태입니다.
규칙에 맞는지 여부만을 기계적으로 봅니다. 규칙을 변경하는 것은 저(글쓴이)입니다.
바로 실전에 투입하지 않았다
처음에는 단 하나의 커밋만 push할 수 있도록 시범 운영 형태로 작동시켰습니다. 그 커밋으로 Broker가 push하고 PR을 만들 수 있음을 확인한 후에, 실제 운영 권한을 활성화했습니다.
이후 전체 시스템 구조를 재정비하여 전환했습니다. 시범 운영 과정에서 AI의 커밋이 Broker의 push, PR 생성, CI(Continuous Integration)까지 통과했습니다.
어려움을 겪었던 점
1. PC 시계가 4초 빨라 CI 결과를 버리고 있었다
Broker는 자신이 CI를 실행하라는 신호를 보낸 이후에 생성된 run만을 해당 요청의 결과로 계산합니다. 이전부터 존재하던 run을 혼동하지 않기 위해서입니다.
그런데 시범 운영 중, CI는 성공했음에도 불구하고 Broker가 '신호 이전에 발생한 run'으로 간주하여 버리고 기다렸습니다. 조사해 보니 PC 시계가 GitHub보다 약 4초 빨랐습니다. GitHub 시간으로 보면, 해당 run이 신호보다 먼저 생성된 것처럼 보였던 것입니다. 해결 방법은 두 가지입니다. 비교할 때 30초의 허용 오차 범위를 두고, PC 시계도 자동으로 맞추는 설정을 했습니다.
2. 제가 접수 폴더에 파일을 놓을 수 없었다
접수 폴더는 AI 계정만 파일을 놓을 수 있도록 되어 있었습니다. 그런데 저의 승인 증표(서명 파일)를 Broker에게 전달하는 상황에서, 저 자신이 접수에 놓을 수 없다는 것을 알게 되었습니다. 방어벽을 너무 단단하게 구축한 결과, 자신도 접근할 수 없게 된 것입니다. 권한을 완화하기보다는, 제가 서명 파일을 AI와 주고받는 폴더에 두고, AI가 그것을 접수로 옮기는 절차를 밟도록 했습니다.
승인 증표를 AI가 옮기게 하는 것은 언뜻 위험해 보입니다. 하지만 Broker는 누가 놓았는지 여부가 아니라, 서명이 올바른지 여부로 판단합니다. 서명은 저의 키(key)와 비밀번호 없이는 생성할 수 없습니다. 만약 AI가 도중에 내용을 수정한다면, 서명이 일치하지 않아 거부됩니다. 동일한 서명의 재사용이나 기한이 지난 서명도 받지 않습니다.
AI가 옮긴 후에, 주고받는 폴더와 접수 파일의 해시값(hash value)이 같은지도 확인하고 있습니다. 다만, 이것은 '올바르게 이동했는지'를 확인하는 것입니다. 방어의 핵심은 서명 쪽에 있습니다.
아직 남아있는 것
merge는 아직 제 손으로 진행하고 있습니다. 미래에는 제가 사전에 서명한 조건 범위 내에서 Broker가 merge하는 단계도 고려하고 있습니다 -
GitHub Actions 설정 변경은 지금도 제가 push합니다. Broker에게는 그 권한을 일부러 주지 않았습니다. 따라서 제가 push한 설정 변경의 PR에서는, Broker가 실행하는 검사가 자동으로 시작되지 않는 틈이 남아있습니다.
요약
- push의 키를 AI와 저의 일상 계정과는 별도의, 전용 푸시(push) 계정에 두었습니다.
- 키는 GitHub App으로 설정하고, 하나의 리포지토리/최소 권한/작업별 토큰에 한정했습니다.
- Broker는 AI의 보고를 믿지 않고, 스스로 확인한 사실만으로 8가지 규칙을 기계적으로 검토합니다.
- 시범 운행부터 시작하여, 시간 오차나 제가 접근할 수 없는 폴더 등, 실제 운영에서 처음 알게 되는 문제점들을 수정했습니다.
AI에게 맡기는 범위가 넓어질수록, '누가, 무엇을 근거로 GitHub에 기록하는지'를 명확히 하는 것이 중요하다고 느끼고 있습니다.
토론