
Claude Code와 Codex의 반자동 개발 과정에서 빠진 6가지 함정
요약
Claude Code와 Codex를 활용한 반자동 앱 개발 워크플로우 구축 과정에서 직면한 기술적 제약과 해결 방안을 다룹니다. GitHub Free 플랜의 기능 제한과 계정 권한 문제 등 실제 구현 시 발생하는 6가지 함정 중 일부를 상세히 분석합니다.
핵심 포인트
- GitHub Free 플랜의 브랜치 보호 및 auto-merge 기능 부재 확인
- 프롬프트 규칙과 설정 파일(deny list)을 통한 이중 안전장치 구축
- GitHub App 발행 비용 문제로 인한 동일 계정 운용 방식 채택
- Self-review 금지 사양 우회를 위한 워크플로우 수정 필요성
이 기사의 전제
별도 기사인 『Claude Code와 Codex로 개인 앱 개발을 반자동화한 이야기』에서, Claude Code(기능 개발 담당)와 Codex(리뷰 담당)라는 두 가지 AI 에이전트에게 7일간 앱 개발을 맡긴 이야기를 썼습니다.
그 기사에서는 워크플로우(Workflow) 전체의 설계와 운용 결과를 정리했지만, 실제로 이 메커니즘을 구축하고 가동하는 과정에서는 설계 시점에는 보이지 않았던 예상치 못한 벽이 차례차례 나타났습니다.
본 기사는 개발 워크플로우 구축 및 운용에서 빠졌던 6가지 함정에 대해, 증상 → 원인 → 대응 순으로 정리한 것입니다. 비슷한 메커니즘을 구축하려는 분들이 같은 함정에 빠지지 않도록, 가능한 한 일반화된 형태로 남겨둡니다.
메커니즘의 전체상은 메인 기사인 『Claude Code와 Codex로 개인 앱 개발을 반자동화한 이야기』에, 개별 설계 판단의 근거는 『Claude Code와 Codex의 반자동 개발 워크플로우 설계 상세』에 정리되어 있으므로, 그것들도 함께 참조해 주시기 바랍니다.
함정 1: Free 플랜 제약으로 auto-merge도 branch protection도 사용할 수 없음
처음에 맞닥뜨린 벽은 설계의 전제 그 자체였습니다.
당초의 워크플로우 설계에서는 main 브랜치에 대해 「PR 필수」, 「CI green 필수」, 「Codex에 의한 리뷰 승인 필수」, 「conversation resolved 필수」라는 브랜치 보호(Branch Protection)를 설정할 계획이었습니다. 이에 더해, Codex가 리뷰 OK라고 판정한 PR은 GitHub의 auto-merge 기능으로 자동 머지(Merge)된다는 것이 상정된 흐름입니다.
하지만 검증해 본 결과, GitHub Free 플랜의 프라이빗 리포지토리(Private Repository)에서는 auto-merge도 branch protection도 애초에 이용할 수 없다는 사실이 판명되었습니다. Pro 이상의 유료 플랜으로의 전환이 필요합니다.
대응책으로는 이중 구조로 대체했습니다.
- 프롬프트 측 규칙: 「Claude Code는 자신의 PR을 머지하지 않는다」, 「Codex는 CI green을 확인한 후 머지한다」를 프롬프트에 명시하여 에이전트의 행동으로서 엄수하게 함
- Claude Code에 대해
.claude/settings.local.json의 deny 리스트에gh pr merge를 deny 등록하여, 오작동을 이중으로 방지함
GitHub 기능이 보장해 주어야 했던 안전장치를 사용할 수 없기 때문에, 프롬프트와 설정 파일의 2단계로 「머지해서는 안 되는 측이 머지하지 않는」 상태를 담보하는 형태가 되었습니다. Pro 플랜으로 전환하면 한 번에 해결될 문제이지만, 개인 개발의 동작 확인 수준이라면 이 다층 방지로도 충분히 실용성을 견딜 수 있었습니다.
함정 2: 별도의 GitHub App 발행을 포기하고, 동일 계정 운용으로 전환함
설계상의 전제를 무너뜨리는 것이 하나 더 있었습니다.
당초에는 Codex와 Claude Code를 각각 별도의 GitHub App으로 발행하여, Codex 측의 App이 PR의 review API에 승인 코멘트를 남김으로써, GitHub의 self-review 금지 사양에 의해 「구현자와 승인자가 다른 인격임」을 담보할 계획이었습니다.
하지만 검증 과정에서 개인 개발 수준에서는 GitHub App의 발행 및 운용 비용이 과도하다고 판단하여, Codex / Claude Code 양쪽 모두를 나의 로컬 Mac 상에서 동일 계정으로 구동하는 방침으로 전환했습니다.
이 전환으로 인해 한 가지 부작용이 발생합니다. 동일 계정으로 구동하는 이상, GitHub 측의 self-review 금지 사양이 「Claude Code가 자신의 PR을 승인하는」 케이스를 막아주지 않습니다.
대응책으로, PR review API를 통한 승인 게이팅(Gating) 자체를 포기하고 다음과 같은 운용으로 변경했습니다.
- 리뷰는 GitHub의 review API가 아니라 PR 코멘트로 남김
- 머지는 Codex의 PR 리뷰 배치(Batch)가 OK 판정 시
gh pr merge --merge --delete-branch로 직접 실행함 - 「Claude Code는 자신의 PR을 머지하지 않는다」를 프롬프트 측 규칙으로서 엄수하게 함
GitHub이 본래 방지해 주어야 할 안전밸브를 프롬프트의 행동 제약으로 대체하는 형태입니다. 에이전트가 순종적일 것이라는 의존도가 높아지기 때문에, 프롬프트를 작성하는 측의 책임이 한 단계 더 무거워지는 구조적 변화가 일어났습니다.
함정 3: GitHub API가 아니라 gh CLI로 통일할 필요가 있었다
각 배치(Batch) 구현 당초, Codex의 자동화는 GitHub API를 직접 호출하는 구성을 일부 시도했습니다. 하지만 운용하는 과정에서 인증 에러 / 권한 스코프(Permission Scope)의 불일치 / 환경 차이에 따른 응답 동작의 차이가 산발적으로 나타나기 시작했고, 배치가 중단되는 원인 상위에 자리 잡았습니다.
대응책으로 Codex와 Claude Code 양측의 모든 GitHub 조작을 gh CLI 기반으로 통일했습니다. 구체적인 구조는 다음과 같습니다.
- 배치 시작 시
gh auth status -h github.com의 성공을 반드시 확인하고, 실패할 경우 처리를 중단 git remoteURL이 예상대로인지 확인GIT_TERMINAL_PROMPT=0 git pull --ff-only origin main을 통해 인증 프롬프트가 필요한 상태가 되면 확실히 중단
gh CLI로 통일하면 인증은 OS의 키체인(Keychain)으로 집약되며, API의 차이도 CLI 측에서 흡수해 줍니다. 직접 API를 호출하는 설계보다 CLI를 한 단계 거치는 것이 AI 에이전트를 통한 운용과는 궁합이 더 좋다고 느꼈습니다.
함정 4: 코멘트 서두의 인덴트(Indent)로 인해 마커 행이 작동하지 않게 됨
이 워크플로우에서는 Codex의 상세화 결과나 리뷰 결과를 Issue / PR의 **코멘트(Comment)**로 추가하며, 그 서두에 다음과 같은 마커(Marker) 행을 배치합니다.
## [Codex 상세화 (2026-05-22)]
Claude Code는 이 마커를 통해 최신 Codex 코멘트를 기계적으로 검색하도록 설계되어 있습니다.
그런데 어느 날, Issue의 상세화 코멘트를 Claude Code가 제대로 포착하지 못하는 현상이 발생했습니다. 원인을 추적해 보니, Codex가 임시 파일을 생성할 때 인덴트가 포함된 heredoc(셸 스크립트 등에서 여러 줄의 텍스트를 다루는 표기법)을 사용하고 있었고, 파일 전체의 각 행 시작 부분에 공백이 들어간 상태로 gh issue comment --body-file에 전달되고 있었다는 사실이 밝혀졌습니다.
GitHub의 마크다운 렌더러(Markdown Renderer)는 행 시작 부분에 공백이 4개 이상인 행을 코드 블록으로 취급하기 때문에, 본래 H2로 해석되어야 할 ## [Codex 상세화 ...] 행이 장식 없는 일반 텍스트(코드 블록 내의 한 줄)로 취급되었습니다. Claude Code 측의 검색 로직은 H2 구조로 남아 있는 마커 행을 기대하고 있었기에, 매칭되지 않는 코멘트만 양산되는 결과로 이어졌던 것입니다.
대응책으로 Codex의 프롬프트에 다음과 같은 고정 절차를 추가했습니다.
- 임시 파일 쓰기 시 인덴트가 포함된 heredoc /
textwrap.dedent를 전제로 하는 문자열을 사용하지 말 것 cat > /tmp/xxx.md <<'EOF'이후에는 행 시작부터 마커 행을 시작할 것- 게시 전
head -n 1 /tmp/xxx.md의 출력이 마커 행과 완전히 일치하는지 확인할 것
단순히 행 시작 공백의 문제처럼 보일 수 있지만, AI 에이전트 간의 문자열 기반 연동에서는 표기법의 미세한 차이가 곧바로 기능의 결여로 직결됩니다. 에이전트 간의 통신 프로토콜은 인간용 문서의 관습보다 더 엄격하게 준수하도록 만들어야 한다는 깨달음을 얻었습니다.
함정 5: 브랜치명이 명명 규칙을 따르지 않게 됨
Claude Code를 스케줄된 작업(Scheduled Task)으로 구동할 때, worktree 모드(태스크 기동 시 격리된 브랜치와 디렉토리를 자동으로 생성하는 모드)를 사용했습니다. 이 모드에서는 태스크 기동 시 claude/*나 cc/*로 시작하는 자동 명명 브랜치가 생성됩니다.
어느 날 배치가 완료된 후, 원격 저장소에 claude/abc123과 같은 명명 규칙 위반 브랜치가 남아 있는 것을 발견했습니다. 본래는 feat/YYYYMMDD-issue-12-15-18이라는 명명 규칙을 따라야 합니다.
원인은 단순했습니다. Issue 구현 배치 (Batch)의 프롬프트가 "현재 브랜치가 main이라면 신규 브랜치를 생성한다"라는 전제로 작성되어 있어서, worktree 모드로 실행된 자동 명명 브랜치에 대해서는 git checkout -b 대신 리네임 (Rename) 처리가 전혀 포함되어 있지 않았던 것입니다. 결과적으로 Claude Code는 자동 명명 브랜치인 상태로 구현을 진행했고, 마지막에 그대로 그 이름으로 push 해버린 것입니다.
대응책으로, 프롬프트의 작업 전 체크를 다음과 같이 분기했습니다.
- 현재 브랜치가
main인 경우:git checkout -b <목표 브랜치명>으로 신규 브랜치를 생성 - 현재 브랜치가
claude/*또는cc/*로 시작하는 자동 명명 브랜치인 경우:git branch -m <목표 브랜치명>으로 현재 브랜치를 리네임한다
여기에 더해, push 전에 정규 표현식 ^feat/[0-9]{8}-issue-[0-9-]+$로 브랜치명을 검증하고, 명명 규칙 위반 시에는 push 하지 않고 중단하는 체크도 추가했습니다.
에이전트(Agent)를 구동하는 실행 환경 (worktree 모드)의 동작을 프롬프트 측에서 모르는 상태로 실행하면, 출력 (원격에 남는 브랜치명)에 부작용이 발생합니다. 프롬프트는 "자신이 동작하는 실행 환경"을 전제로 작성해야 한다는 당연한 사실을 실례를 통해 재확인한 셈입니다.
함정 6: 1일 1회로 안정적이라서, 6배로 올린 순간 나타난 문제
마지막 함정은 운영 초기 단계의 신중함이 그대로 지연으로 이어진 이야기입니다.
첫 시험 운영에서는 각 배치를 1일 1회부터 시작했습니다. 폭주나 정체가 일어나지 않는 것을 이틀 정도 확인한 뒤, 셋째 날에 1일 6회 (4시간 주기)로 전환하는 것이 당초 계획이었습니다.
실제로 해보니 1일 1회의 시험 운영은 확실히 안정적으로 보였습니다. 하지만 빈도를 6배로 올린 순간, 지금까지 언급한 함정 4 (마커 행의 인덴트 문제)와 함정 5 (worktree 모드의 브랜치 문제)가 연속해서 현재화되었습니다.
이유를 생각해 보면 단순합니다. 1일 1회의 빈도에서는 이러한 문제들을 우연히 밟지 않았거나, 혹은 밟았더라도 단 1건뿐이었기에 제가 문제로 인식하지 못했을 뿐이었습니다. 6배로 늘리면 동일한 확률의 문제가 6배의 빈도로 표출되기 때문에, 재현성 있는 문제로 파악할 수 있게 됩니다. 저빈도에서는 잠복하는 문제가 빈도를 높이면 단번에 표면화되는 현상이었습니다.
여기서 얻은 교훈은 빈도 설계(Frequency Design)를 2단계로 생각했어야 했다는 점입니다. 저빈도 운영은 워크플로우가 폭주하지 않는지 확인하는 데에는 확실히 유효했습니다. 반면, 발생 빈도가 적은 잠복 버그(Latent Bug)는 좀처럼 알아차릴 수 없습니다. 시험 운영에 저빈도 스테이지만 두면, 후자를 놓친 채 본격적인 운영으로 들어갈 리스크가 있습니다.
제 경우에는 결과적으로 빈도를 높인 날에 함정 4와 함정 5가 나타나 그날 안에 수정할 수 있었습니다. 다만, 만약 시험 운영 기간을 일주일로 늘렸다면 이러한 함정들은 본격 운영에 들어간 후에 나타났을 가능성이 높습니다. 단기간이라도 고빈도 운영의 부하 테스트 (Load Test)를 포함시킨다는 스테이지를 시험 운영에 포함시키는 것이 이번 회고를 통해 남은 관점입니다.
함정은 성격에 따라 3가지 레이어로 나뉜다
6가지 함정을 되돌아보면, 성격이 다른 3가지 레이어로 나뉜다는 것을 알 수 있습니다.
- 설계 전제의 붕괴: 함정 1 (Free 플랜 제약) / 함정 2 (GitHub App 발행 포기)
- 에이전트 간의 통신 프로토콜과 실행 환경의 전제: 함정 3 (gh CLI 통일) / 함정 4 (마커 행의 인덴트) / 함정 5 (worktree 모드의 브랜치)
- 시험 운영의 빈도 설계: 함정 6 (1일 1회 → 6배의 함정)
각각의 함정은 단독으로 보면 매우 사소한 것이지만, 6 사이클/일로 동작하는 배치에서는 눈에 띄기 쉬워집니다. 역으로 말하면, 저빈도 배치에서는 이러한 문제들이 발견되지 않은 채 잠복한다는 뜻이기도 합니다.
반자동 개발의 빈도를 높이면 설계의 왜곡이 솔직하게 드러납니다. 이는 운영해 보기 전까지는 예상하지 못했던 부수적인 효과였습니다.
전체적인 시스템 설계와 이러한 함정들이 어떤 맥락에서 발생했는지에 대해서는 메인 기사인 『Claude Code와 Codex로 개인 앱 개발을 반자동화한 이야기』에 정리해 두었습니다. 개별 설계 판단의 근거를 심층적으로 다룬 『Claude Code와 Codex의 반자동 개발 워크플로우 설계 상세』와 함께 읽으시면, 동일한 시스템을 구축할 때의 해상도가 한 단계 높아질 것입니다.
이 기사와 함께 참조해 주세요.
Discussion

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