
당신의 CLAUDE.md는 경계가 아니라 브리핑입니다
요약
1인 개발자가 Claude Code와 Claude Desktop, MCP를 활용해 다수의 프로젝트를 관리하는 멀티 에이전트 워크플로우를 구축한 사례와 그 과정에서 겪은 시행착오를 다룹니다.
핵심 포인트
- Claude Code 세션과 Claude Desktop 프로젝트를 감독관-실행자 관계로 분리
- MCP를 활용해 실제 코드와 대조하며 검토하는 구조 구축
- 상태 파일과 스크립트를 이용한 프로젝트 우선순위 자동 인덱싱
- 멀티 에이전트 시스템 구축 시 발생할 수 있는 관리 복잡성과 충돌 위험 경고
누구도 저에게 이렇게 하라고 말하지 않았습니다. 블로그 포스트에서 읽은 것도 아니고 누군가의 설정을 복사한 것도 아닙니다. 어느 날 저녁 문득 생각이 떠올라 이것이 작동할지 확인해보고 싶어서 직접 이것저것 만져보며 만든 것입니다.
설정: 저는 약 10개의 프로젝트를 가진 1인 개발자이며, 한 번에 하나씩 프로젝트를 처리하는 방식을 그만두었습니다. Mac Studio에서 iTerm 탭을 저장소(repository)당 하나씩 열어두고, 각 탭에서 개별적인 Claude Code 세션을 실행합니다. 제가 탭 사이를 이동하는 동안 보통 두세 개는 동시에 실제 작업을 수행하고 있습니다.
그다음 저는 두 번째 레이어를 추가했습니다. 하나가 좋다면 더 많을수록 더 좋을 것이기 때문입니다. Claude 데스크톱 앱(Claude desktop app)에서 저장소당 하나의 프로젝트(Project)를 생성했고, 각 프로젝트에는 다음과 같은 취지의 지침을 설정했습니다: "당신은 내 Mac Studio의 이 경로에 있는 저장소의 감독관(supervisor)입니다. 구현자(implementer)는 그 안에서 작동하는 Claude Code 세션입니다. 당신은 검토하고 조언하되, 직접 코드를 작성하지는 마십시오." 이 프로젝트들은 각각 실제 파일을 읽을 수 있는 MCP(Model Context Protocol) 액세스 권한을 가지고 있으므로, 제가 요약한 내용이 아니라 현재 코드와 직접 대조하며 논쟁합니다. 다른 작업자의 손길 대신 외부의 의견이 필요할 때, 저는 그곳에 질문을 던지고 유용한 부분을 터미널에 다시 붙여넣습니다. 다소 투박한 인터페이스이지만 효과는 있습니다. 복사 및 붙여넣기는 결정하는 주체와 실행하는 주체 사이의 놀라울 정도로 효과적인 에어 갭(air gap) 역할을 합니다.
그 모든 것 위에 저는 master-supervisor라는 또 하나의 저장소를 두었습니다. 이 저장소의 유일한 임무는 다른 프로젝트들을 관찰하고 제가 어디에 집중해야 할지 알려주는 것입니다. 이것이 작동하는 이유는 모든 프로젝트가 프론트매터(frontmatter)에 한 줄의 상태와 next step 필드가 포함된 docs/projekt-status.md 파일을 유지하기 때문입니다. 스크립트가 모든 저장소를 순회하며 해당 파일들과 최근 커밋(commit)을 읽어 인덱스(index)를 구축합니다. 하루에 세 번, 제가 '레이더(radar)'라고 부르는 또 다른 스크립트가 실행됩니다. 이 스크립트는 인덱스를 가져와 프로젝트의 순위 목록을 작성하며, 각 줄에는 두세 문장의 근거를 함께 적습니다. 저는 그 목록을 수동으로 편집하지 않고, 별도의 상태 파일과 집중 우선순위 재정의(focus overrides) 파일을 통해 조종합니다. 그리고 프로젝트당 하나씩 존재하는 오더 파일(order files)이 있는데, 이것이 감독관(overseer)이 프로젝트에 무언가를 요청하는 방식입니다.
제가 이 점을 언급하는 이유는 문제의 형태를 설명하기 위해서입니다. 이것은 10개의 도구를 가진 단일 에이전트(agent)가 아닙니다. 10개의 에이전트, 몇 개의 채팅(chats), 몇 개의 예약된 스크립트(scheduled scripts), 그리고 이들 사이를 이동하며 모두 동일한 파일 시스템(filesystem)을 건드리는 한 명의 인간이 존재하는 구조입니다.
저는 제 스스로가 꽤 만족스러웠습니다. 그러다 시스템이 스스로를 갉아먹기 시작했고, 저는 무언가를 구축하는 대신 이 엉망진창이 된 상황을 해결하느라 이틀을 보냈습니다.
따라서 이것은 제가 권장하는 방법이 아닙니다. 만약 여러분이 같은 방향으로 시도하고 있다면, 제가 빠졌던 함정들의 목록이라고 생각하십시오.
세 가지 충돌 방식
첫 번째 방식은 저의 운영(production) 시간을 낭비하게 만들었습니다. 서버에는 여러 프로젝트가 공유하는 설정 파일(config file)이 있는데, 지출 한도를 나타내는 작은 JSON 맵(map)입니다. 이 파일은 특정 리포지토리(repository)에 속해 있지 않아서, 제가 현재 작업 중인 어떤 프로젝트에서든 수정될 수 있습니다. 약 하루 동안, 서로 다른 두 리포지토리에서 발생한 세 번의 세션(session)이 각각 이 파일을 읽고, 자신의 부분을 변경한 뒤, 파일 전체를 다시 썼습니다.
모든 데이터베이스 강의에서 경고하는 전형적인 갱신 손실(lost update) 문제입니다. 한 세션은 프로젝트와 무관한 무언가를 정리하던 중, 어떤 프로젝트의 한도가 의심스러울 정도로 높게 설정된 것을 보고, 그것이 테스트용 찌꺼기라고 올바르게 결론지어 삭제했습니다. 하지만 그것은 찌꺼기가 아니었습니다. 그것은 바로 전날, 해당 프로젝트가 자체 AI 기능에서 차단되는 것을 막기 위해 의도적으로 추가된 예외 사항이었습니다. 차단 현상이 다시 발생했습니다. 제가 알아차릴 때까지 고객 대상 기능이 작동하지 않았습니다.
이 상황에서 저를 괴롭히는 점은 이것입니다: 누구도 비합리적인 행동을 하지 않았다는 것입니다. 모든 수정 사항은 해당 세션이 볼 수 있었던 정보에 근거했을 때 충분히 방어 가능한 것이었습니다. 파일에 소유자(owner)도 없고 잠금(lock)도 없었기 때문에, 마지막에 쓴 사람이 승리했고 이전 쓰기 작업의 논리는 보이지 않게 된 것입니다.
두 번째 사례는 더 심각했습니다. 인간이 전혀 개입되지 않았기 때문입니다. 저는 매주 종속성 스캔 (dependency scan)을 실행하는 LaunchAgent와, 그 결과를 가져와서 아무것도 놓치지 않도록 친절하게도 각 영향을 받은 프로젝트의 docs/TODO.md에 TODO 항목을 추가하는 작은 스크립트를 실행하고 있었습니다. 이 스크립트는 자신이 소유하지 않은 9개의 저장소 (repositories)에 내용을 작성했습니다. 그 후 해당 프로젝트 중 하나를 열었을 때, 에이전트 (agent)가 해당 세션의 누구도 수정하지 않은 내용을 해당 세션이 소유한 파일에서 발견하게 되면, 예상 가능한 혼란이 정확히 발생합니다. 즉, 앞뒤가 맞지 않는 머지 (merges)와 히스토리 (history)와 일치하지 않는 상태가 나타나는 것입니다.
이전 상태. 모든 액터 (actor)는 자신이 소유하지 않은 저장소를 포함하여 어디든 쓸 수 있었습니다. 각 수정 사항은 로컬 (locally)로는 올바른 상태였습니다.
세 번째는 더 친근한 얼굴을 한 동일한 실수였습니다. 저는 받은 편지함을 분류 (triaging)하기 위한 Claude Code 스킬을 가지고 있는데, 다른 프로젝트와 관련된 메일을 발견하면 해당 프로젝트의 docs/TODO.md에 내용을 작성하여 작업을 넘기도록 지시받았습니다. 인간 비서에게는 합리적인 행동입니다. 하지만 해당 파일에 단 하나의 정당한 작성자 (writer)만 존재하는 시스템에서는 나쁜 행동입니다.
기록하는 것이 왜 해결책이 되지 못했는가
저의 첫 번째 본능은 당연한 것이었습니다. 규칙을 문서화하는 것이었죠. CLAUDE.md에 세션은 자신의 저장소 외부에는 작성해서는 안 된다는 한 줄을 추가하는 것이었습니다.
그것은 작동하지 않았고, 저는 왜 그런지 받아들이기까지 몇 차례의 시행착오를 겪어야 했습니다. 파일을 읽고, 그 내용을 올바르게 추론하며, 개선된 버전을 다시 쓰는 에이전트(Agent)는 자신의 세션(Session) 내부에서는 모든 것을 올바르게 수행하고 있는 것입니다. 에이전트는 다른 저장소(Repo)에 있는 다른 세션이 90분 전에 왜 그런 값을 작성했는지, 자신이 접근할 수 없는 대화 속에 담긴 이유를 알 수 없습니다. 세션 내부의 주의(Care)만으로는 세션 간의 조정 문제(Coordination problem)를 해결할 수 없습니다. 동일한 세션이 자유롭게 해석할 수 있는 규칙 또한 마찬가지이며, 규칙은 해석되기 마련입니다. 특히 규칙을 따르는 것이 불편할 때는 더욱 그렇습니다.
문제의 나머지 절반은 예약된 작업(Scheduled jobs)입니다. 크론 스크립트(Cron script)는 CLAUDE.md를 전혀 읽지 않습니다. 산문(Prose) 형태로 존재하는 그 어떤 규칙도 시스템 내의 상당수 행위자(Actors)들에게는 보이지 않습니다.
실제로 문제를 해결한 방법
이 엉킨 실타래를 푸는 데 이틀이 걸렸습니다. 무언가를 만드는 데 이틀이 걸린 것이 아니라, 실제로 어떤 일이 어떤 순서로 일어났는지, 그리고 내 앞에 놓인 변화 중 무엇이 진짜인지 파악하는 데 대부분의 이틀을 보냈습니다. 다른 사람의 글에서 읽고 싶은 숫자가 바로 이것이기에, 저도 이렇게 적어둡니다. 결과적으로 핵심은 모든 것에 소유자(Owner)를 부여하는 것이었습니다.
저장소당 한 명의 작성자. 세션은 오직 자신의 git 저장소 내부의 파일만 작성할 수 있습니다. 무엇이든, 어디서든 읽는 것은 항상 허용되었고 지금도 그렇습니다. 저장소 경계를 넘나드는 쓰기 작업은 PreToolUse 훅(repo-grenze-guard.sh)에 의해 차단되며, 이 훅은 쓰기(Write), 편집(Edit) 또는 셸 리다이렉션(Shell redirect)이 파일에 접근하기 전에 0이 아닌 종료 코드(Non-zero exit)를 반환하며 종료됩니다. 문서에 적힌 경고가 아니라, 기계적인 차단입니다. 만약 제가 진심으로 다른 프로젝트에서 작업해야 한다면, 저는 그 프로젝트로 가서 작업할 것입니다.
공유 리소스당 하나의 소유자. 어떤 리포지토리(repo)에도 속하지 않는 소수의 파일들(지출 설정, 포트 매트릭스, 검증된 사실 파일 등)은 이제 제가 mit-lock이라고 부르는 작은 래퍼(wrapper)를 통해 처리됩니다: mit-lock budgets -- <command>. 이 래퍼는 리소스에 락(lock)을 걸고, 명령어를 실행한 뒤, 프로젝트, PID, 호스트 및 커맨드 라인(command line) 정보를 포함한 모든 시도 내역을 ~/.claude/logs/ressourcen-audit.log에 추가합니다. 두 번째 PreToolUse 훅(hook)이 해당 경로에 대한 직접적인 쓰기를 거부하므로, 사용자가 락 사용을 굳이 기억할 필요가 없습니다.
교차 프로젝트 작업을 위한 단일 채널. 더 이상 어떤 작업도 다른 프로젝트에 직접 쓰지 않습니다. 대신 메일박스(mailbox)로 들어갑니다: ~/.claude/vorschlaege/<source>.md. 이는 추가 전용(append-only)이며, 파일당 하나의 작성자만 존재합니다. 감독(overseer) 리포지토리가 이 메일박스를 읽고, 해당 항목들을 자신의 리포지토리 내 auftraege/<project>.md에 있는 명령(orders)으로 변환하며, 각 프로젝트는 거기서 자신의 명령을 가져옵니다. 한 번에 직접 건너뛰는 대신 두 단계를 거치는 방식은 관료주의처럼 들릴 수 있지만, 이것이 히스토리(history)를 읽기 쉽게 유지해 주는 핵심입니다. 현재 10개의 명령 파일이 그곳에 있으며, 그중 모든 파일은 누가 무엇을 요청했는지 추적할 수 있습니다.
이후의 모습. 행위자도 같고 작업도 같지만, 모든 변경 가능한(mutable) 요소는 정확히 하나의 소유자를 가지며, 중요한 두 가지 규칙은 선의가 아닌 훅(hook)에 의해 강제됩니다.
9개의 리포지토리에 직접 쓰던 위임(delegation) 스크립트는 LaunchAgent 레벨에서 비활성화되었습니다. 이제 해당 스크립트는 작성자(writer)가 아닌, 메일박스로 들어오는 제안자(proposer)로서 돌아옵니다. 인박스(inbox) 스킬의 핸드오버(handover) 지침도 동일한 방식으로 재작성되었습니다.
제가 가장 가치 있다고 예상하지 못했던 부분은 감사 로그(audit log)입니다. 이제 공유 파일이 잘못된 것처럼 보일 때, 세 개의 별도 세션 트랜스크립트(session transcripts)를 재구성할 필요 없이 한 곳에서 어떤 프로젝트가 언제 해당 파일을 건드렸는지 확인할 수 있습니다.
구체적인 구성 요소
"에이전트 오케스트레이션 (agent orchestration)"에 관한 모호한 게시물들이 저에게 전혀 도움이 되지 않았기 때문에, 실제로 무엇이 실행되고 있는지 알려드리겠습니다. 이 중 영리한 것은 하나도 없으며, 바로 그것이 핵심입니다.
- Mac Studio의 iTerm 탭에서 각각 10개의 리포지토리(repos)를 하나의 Claude Code 세션으로 실행합니다. 한 번에 2~3개가 활성화됩니다. 이를 위해 워크트리 (worktrees)를 사용하지 않고, 단순히 별도의 리포지토리를 사용합니다.
- **리포지토리당 하나의
CLAUDE.md**와~/.claude/에 있는 하나의 글로벌 파일을 사용합니다. 이 파일은 이유(why), 컨벤션 (conventions), 그리고 제가 사용하는 트리거 문구 (trigger phrases)를 설명합니다. 이것은 브리핑 (briefing)이며 진정으로 유용합니다. 이것은 경계 (boundary)가 아닙니다. 그것이 바로 모든 오해의 핵심이었습니다. - 반복적인 작업(수신함 분류, 인보이스 발행, 배포)을 위한 **스킬 (Skills)**입니다. 스킬은 목적이 있는 프롬프트 (prompt)이므로 동일한 문제를 가집니다. 즉, 지침이 암시하는 바에 따라 건드려서는 안 될 리포지토리에 접근하는 것을 포함하여, 시키는 대로 수행할 것입니다.
~/.claude/hooks/에 있는 **두 개의 PreToolUse 훅 (hooks)**으로, 합쳐서 약 150줄의 bash 코드입니다. 하나는 세션 작업 디렉토리의 git 루트를 확인하고, 그 외부에서의 쓰기 (Write), 편집 (Edit) 또는 셸 리다이렉션 (shell redirection)을 거부합니다. 다른 하나는 소수의 공유 경로에 대한 직접적인 쓰기를 거부하고 대신 락 (lock)을 가리킵니다. 경계를 정당하게 넘나드는 두 프로세스를 위해 승인된 작성자 (sanctioned-writer) 환경 변수가 존재합니다.mit-lock, 작은 래퍼 (wrapper)입니다: 리소스에 락을 걸고, 명령을 실행하고, 시도를 기록합니다. 로그 라인에는 타임스탬프, 프로젝트, PID, 호스트, 명령어가 포함됩니다.- 데스크톱 앱의 Claude Projects, 리포지토리당 하나씩 사용하며, Mac Studio의 특정 경로에 대한 읽기 전용 감독자 (read-only supervisors)로 정의하는 지침과 코드를 읽기 위한 MCP를 사용합니다. 저는 이들 사이와 터미널 사이를 수동으로 복사하여 붙여넣습니다.
- 메일함 디렉토리 (
~/.claude/vorschlaege/)와 감독자(overseer) 리포지토리 내의 주문 (orders) 디렉토리입니다. 둘 다 일반 마크다운 (markdown) 형식이며, 추가 전용 (append-only)이고, 파일당 하나의 작성자만 존재합니다. 큐 (queue), 브로커 (broker), 데몬 (daemon)은 없습니다. 오직 마크다운 파일과 누가 추가할 것인지에 대한 규칙만 있을 뿐입니다.
이 목록에서 단 하나만 가져가고 싶다면, 훅 (hooks)을 가져가십시오. 훅은 제가 계속해서 재설명해야 했던 규칙을, 제가 보고 있지 않을 때도 유지되는 규칙으로 바꿔 놓은 핵심 요소입니다.
제가 여기서 얻은 것
만약 한 번에 하나 이상의 에이전트 (agent)를 실행하고 있다면, 유용한 멘탈 모델 (mental model)은 "보조자 (assistants)"가 아니라 "동시 작성자 (concurrent writers)"입니다. 모든 가변적인 것 (mutable thing)은 정확히 하나의 소유자 또는 잠금 (lock)을 가져야 합니다. 이는 50년 동안 동시성 시스템 (concurrent systems)에서 사실이었습니다. 변한 점은 이제 동시 작성자들이 저의 여러 세션 (sessions)이며, 모두 빠르고 자신감이 넘치며, 각자 부분적인 관점 (partial view)에서 작업하고, 그들 모두가 고립된 상태에서는 완전히 합리적으로 보이는 변경 사항을 생성한다는 것입니다.
그리고 강제성 (enforcement)은 에이전트보다 낮은 단계에 존재해야 합니다. 즉, 훅 (hook)에, 잠금 (lock)에, 또는 0이 아닌 종료 코드 (non-zero exit code)를 반환하는 무언가에 존재해야 합니다. CLAUDE.md는 '왜'를 설명하는 곳이며, 작성할 가치가 있지만, 그것은 브리핑 (briefing)이지 경계 (boundary)가 아닙니다. 그것은 지금 당장 도움이 되도록 최적화하려는 행위자 (actor)에게 주는 조언이며, 시스템 내의 모든 LaunchAgent와 크론 잡 (cron job)에게는 보이지 않습니다.
이것이 현재 어떤 상태인지 솔직하게 말씀드리고 싶습니다. 왜냐하면 이런 종류의 포스트들은 대개 아키텍처 다이어그램 (architecture diagram)과 만족스러운 어조로 끝나곤 하기 때문입니다. 이 작업은 일주일 전의 일입니다. 저는 조정 레이어 (coordination layer)를 재구축하고 모든 것을 재시작했으며, 이것이 일반적인 업무 주간 동안 유지되는지 여부를 여전히 알아가는 중입니다. 여기에 완성된 것은 아무것도 없으며, 그 어떤 것도 템플릿 (template)이 아닙니다. 이것은 제가 진행하면서 즉석에서 만들어낸 것에 대한 수리 작업입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기