저장소당 하나의 에이전트: 멀티 리포(Multi-Repo) AI 코딩을 위한 오케스트레이션 패턴
요약
멀티 리포(Multi-Repo) 환경에서 AI 코딩 어시스턴트의 성능 저하를 해결하기 위해 저장소별 전용 에이전트와 오케스트레이터 패턴을 제안합니다. 각 에이전트에게 스코프가 지정된 도구와 프롬프트를 부여하여 컨텍스트 희석을 방지하고 효율적인 업무 라우팅을 구현하는 방법을 다룹니다.
핵심 포인트
- 저장소당 하나의 특화된 에이전트를 배치하여 컨텍스트 관리 효율화
- 오케스트레이터가 업무를 라우팅하고 각 에이전트의 답변을 병합
- 쓰기 도구 제거를 통해 읽기 전용 저장소에 대한 권한 제어 구현
- Claude Code의 서브 에이전트 개념을 활용한 확장 가능한 설계
당신의 AI 코딩 어시스턴트는 단일 저장소(Repo) 내에서는 매우 뛰어납니다. 하지만 이를 수십 개의 저장소가 있는 DevOps 워크스페이스에 던져 넣으면 무너지는 모습을 보게 될 것입니다.
이 어시스턴트는 한 저장소의 절반과 다른 저장소의 절반을 훑어보고, 어떤 것이 읽기 전용(Read-only)인지 잊어버리며, 서로 다른 컨벤션(Convention)을 뒤섞고, 규칙을 기억하는 데에만 컨텍스트 윈도우(Context window)의 대부분을 소비합니다. 당신이 여러 저장소에 걸친 질문을 하나 던지면, 잘못된 파일들로부터 짜깁기된, 확신에 차 있지만 절반은 틀린 답변을 받게 됩니다. 더 어려운 문제는 모델의 성능이 아니라, 당신이 하나의 뇌에 한꺼번에 수십 가지의 업무를 맡겼다는 점입니다.
해결책이 있습니다. 이는 숙련된 팀이 이미 업무를 나누는 방식과 유사합니다. 각 저장소에 특화된 개별 에이전트를 부여하고, 하나의 오케스트레이터(Orchestrator)가 업무를 라우팅(Routing)하고 답변을 병합하게 하는 것입니다.
그게 전부입니다. 이 포스트의 나머지 내용은 그 '이유(Why)'와 '방법(How)'에 대해 다룹니다. 전체 설계를 조용히 지배하는 단 하나의 제약 조건, 오늘 바로 적용할 수 있는 복사-붙여넣기 가능한 스타터 에이전트(Starter agents), 그리고 피해야 할 실패 모드(Failure modes)를 설명합니다. 예제에는 Claude Code 서브 에이전트(Subagents)를 사용하지만, 이 아이디어는 에이전트별 프롬프트(Prompt)와 도구 스코핑(Tool scoping)이 가능한 모든 에이전트 런타임(Agent runtime)에 적용될 수 있습니다.
요약 (TL;DR)
- 저장소당 하나의 AI 에이전트를 배치하며, 각 에이전트는 스코프가 지정된 프롬프트와 **스코프가 지정된 도구(Scoped tools)**를 가집니다.
- 단일 오케스트레이터(메인 세션)가 업무를 라우팅하고 답변을 병합합니다. 서브 에이전트는 서브 에이전트를 생성할 수 없으므로, 별도의 "라우터 에이전트(Router agent)"가 될 수는 없습니다.
- 읽기 전용 저장소는 정중하게 요청하는 것이 아니라, 쓰기 도구(Write tools)를 제거함으로써 읽기 전용으로 만듭니다.
- 저장소 간 질문은 **여러 에이전트에게 병렬로 팬 아웃(Fan out)**되며, 각 에이전트는 깨끗한 컨텍스트 내에서 작업합니다.
- 몇 개의 마크다운(Markdown) 파일만 있으면 됩니다. 프레임워크는 필요하지 않습니다.
문제점
대부분의 DevOps 및 플랫폼 팀이 익숙할 만한 레이아웃인, 여러 개의 독립적인 저장소가 나란히 체크아웃되어 성장한 시스템을 상상해 보십시오:
| Repo | 역할 (Role) | 수정 정책 (Edit policy) |
|---|---|---|
backend | 디버깅 대상이 되는 애플리케이션 서비스, 동작 및 스택 트레이스(stack traces)의 진실의 원천 (source of truth) | 읽기 전용 (read only) |
| ... |
이것은 DevOps 관점에서의 세계관입니다: infrastructure와 deploy는 당신이 변경할 수 있지만, 프로덕션(production)에 영향을 미치므로 에이전트는 반드시 당신에게 차이점(diff)을 보여주고 승인을 받은 후에만 변경 사항을 적용해야 합니다. backend와 frontend는 다른 팀의 소유입니다. 당신은 디버깅을 위해 이를 읽을 수는 있지만, 절대 수정해서는 안 됩니다. docs는 자유롭게 변경할 수 있지만, 반드시 풀 리퀘스트(pull request)를 거쳐야 합니다. 세 개의 계층이 존재하며, 각 계층은 서로 다른 에이전트가 됩니다.
하나의 AI 세션에 이 모든 것을 가리키게 하면 세 가지 벽에 부딪힙니다.
컨텍스트 희석 (Context dilution). 모든 저장소의 컨벤션(conventions), 레이아웃(layout), 수정 정책(edit policy)을 하나의 컨텍스트 윈도우(context window)에 로드하면 실제 문제 해결을 위한 정보가 밀려나게 되며, 모델은 한 저장소의 규칙을 다른 저장소로 유출하기 시작합니다 (예: "여기서는 수정해도 괜찮다"라는 규칙이 읽기 전용 저장소로 번지는 현상).
직렬 조사 (Serial investigation). _"이것이 코드 버그인가, 아니면 환경 설정 오류인가?"_와 같은 질문은 두 개의 저장소에 걸쳐 있습니다. 하나의 세션이 첫 번째 저장소를 읽고, 그다음 두 번째 저장소를 읽으면, 두 가지를 동시에 머릿속에 유지하는 데 어려움을 겪습니다.
취약한 가드레일 (Weak guardrails). "이 저장소는 읽기 전용이다"라는 내용이 단지 산문 형태의 지침 파일에만 존재한다면, 이는 모델이 부하가 걸렸을 때 잊어버릴 수 있는 정중한 요청일 뿐, 강제된 경계가 아닙니다. 공유 워크스페이스(shared workspace)에서 이런 방식은 잘못된 저장소를 수정하게 만드는 원인이 됩니다.
핵심 논지: 저장소당 하나의 특화된 에이전트를 두고, 작업을 관련 에이전트들에게 병렬로 라우팅(routing)하며 그 결과들을 병합하는 단일 오케스트레이터(orchestrator)를 두는 것입니다.
이 패턴에는 이름이 있습니다: 오케스트레이터-워커 (orchestrator-worker)
이는 당신이 팀을 운영하는 방식과 닮아 있습니다. 한 명의 엔지니어에게 백엔드, Helm 차트, 문서를 모두 맡겨 모든 규칙을 머릿속에 담아두게 하지는 않을 것입니다. 대신 각 영역에 담당자를 지정하고, 업무를 라우팅하는 책임자를 둘 것입니다. 이것이 바로 오케스트레이터-워커 (orchestrator-worker) 패턴(매니저-워커(manager-worker) 또는 컨덕터(conductor)라고도 불림)이며, 이미 잘 알려진 방식입니다. 여기서 반복되는 교훈은 다음과 같습니다:
- 저장소당 하나의 에이전트는 저장소당 집중된 컨텍스트 윈도우 (context window)를 의미합니다. 백엔드 에이전트는 백엔드 컨벤션(conventions)만 읽고, 배포 에이전트는 배포 컨벤션만 읽습니다. 오케스트레이터(orchestrator)는 조정할 뿐 세부 사항을 보유하지 않습니다.
- 멀티 리포(multi-repo) 레이아웃에서 이 방식이 빛을 발합니다. 독립적인 저장소들은 서로 다른 저장소의 에이전트들이 파일 충돌을 일으키지 않음을 의미하며, 이는 병렬 편집이 충돌하는 모노레포(monorepo)와는 대조적입니다.
- 매니저(manager)는 계획을 보유하고, 워커(worker)는 실행합니다. 하나의 저장소에 하나의 작업(task)을 할당합니다.
이 중 새로운 것은 없습니다. 멀티 에이전트 오케스트레이션(multi-agent orchestration)과 오케스트레이터-워커(orchestrator-worker) 패턴에 대해서는 이미 많은 글이 작성되어 있으며, 빠르게 검색만 해봐도 충분한 정보를 얻을 수 있습니다. 다음에 이어지는 내용은 실제 멀티 리포 워크스페이스에서 이를 실제로 작동하게 만드는 저만의 방식입니다.
모든 것을 결정짓는 단 하나의 제약 사항
어떤 구조를 설계하기 전에 이 점을 반드시 내재화하십시오. 가장 직관적인 설계를 무너뜨리기 때문입니다:
하위 에이전트(subagent)는 다른 하위 에이전트를 생성할 수 없습니다.
유혹적인 모델은 3단계 파이프라인입니다. 라우터(router) 에이전트가 리포(repo) 에이전트들에게 작업을 할당하고, 이들이 다시 별도의 답변(answer) 에이전트에게 보고하는 방식입니다. 이는 두 단계의 위임(delegation)을 의미하며, 두 번째 단계는 허용되지 않습니다.
따라서 오케스트레이터는 하위 에이전트가 될 수 없습니다. 오케스트레이터는 대화 그 자체여야 합니다. 당신이 대화하는 세션이 바로 컨덕터(conductor)입니다. 세션은 당신의 요청을 읽고, 어떤 리포 에이전트를 호출할지 결정하며(유일한 하위 에이전트 단계), 그들의 보고를 수집하여 답변을 합성합니다.
당신
| (당신의 요청)
v
...
아래의 모든 선택은 이 제약 사항 안에서 이루어집니다.
구성 요소: 저장소당 하나의 에이전트 정의
Claude Code에서 각 에이전트는 YAML 프론트매터(frontmatter)가 포함된 작은 마크다운(Markdown) 파일이며, .claude/agents/ 디렉토리에 위치합니다:
---
name: backend-expert
description: >
...
세 가지 프론트매터 필드가 설계를 결정합니다:
| 필드 (Field) | 역할 (Role) | 참고 (Note) |
|---|---|---|
description | 오케스트레이터(orchestrator)가 요청을 매칭할 때 사용하는 라우팅 신호 (Routing signal) | "...할 때 사용하세요 (use when ...)" 형식으로 작성 |
| ... |
오케스트레이터 자체는 별도의 파일이 없으며, 메인 루프 (main loop) 역할을 합니다. 오케스트레이터의 라우팅은 각 에이전트의 description과, 이슈 유형을 리포지토리(repo)에 매핑하는 최상위 지침 파일(CLAUDE.md) 내의 짧은 **트리아지 가이드 (triage guide)**를 통해 이루어집니다. 에이전트 하나를 매칭하여 작업을 위임합니다. 이는 여러 리포지토리에 걸쳐 작동하며, 병렬로 확산(fan out)된 후 결과를 조정(reconcile)합니다.
바로 복사해서 사용할 수 있는 스타터 에이전트 (Copy-pasteable starter agents)
위의 읽기 전용 (read-only) 예시(backend-expert)는 한 단계의 계층입니다. 즉, 단순히 쓰기 도구 (write tools)가 없는 상태입니다. DevOps 엔지니어로서 당신은 또한 몇 개의 리포지토리를 관리하므로, 여기에는 편집 가능한 두 가지 계층이 있습니다. 읽기 전용 에이전트와의 핵심적인 차이점은 tools와 프롬프트(prompt)의 정책(policy) 라인에 있습니다.
deploy-expert.md (편집 가능, 먼저 확인 필요)
---
name: deploy-expert
description: >
...
(infrastructure 리포지토리도 동일한 형태를 가집니다: 편집은 허용되지만, Terraform 변경 사항은 프로덕션(production)에도 영향을 미치므로 적용 전 확인(confirm-before-apply) 과정을 거칩니다.)
docs-editor.md (PR을 통해 편집)
---
name: docs-editor
description: >
...
편집 정책을 실제 경계(boundaries)로 전환하기
이 부분은 팀들이 과소평가하는 지점입니다. 단일 모놀리식(monolithic) 에이전트는 모든 곳에 쓰기 권한을 가지며, 어떤 디렉토리가 금지 구역인지 오로지 _기억하는 것_에만 의존합니다. 리포지토리당 모델 (per-repo model)에서는 경계가 구조적이며, 다음과 같은 세 가지 계층으로 나뉩니다:
- 읽기 전용 (Read-only, hard wall).
backend나frontend처럼 디버깅 용도로만 사용하는 리포지토리에는Edit/Write도구가 전혀 없는 에이전트가 할당됩니다. 해당 기능 자체가 없기 때문에 사용자가 요청하더라도 수정할 수 없습니다. 메모리 (Memory)도 필요하지 않습니다. - 수정 후 우선 확인 (Edit, confirm first, 프로덕션 리포지토리).
infrastructure나deploy와 같이 프로덕션 환경에 영향을 미치는 소유 리포지토리에는 쓰기 도구가 제공되지만, 프롬프트 (Prompt)를 통해 변경 사항의 차이(diff)를 보여주고 승인을 받기 전에는 아무것도 적용하지 못하도록 제한하며, 승인 없이 푸시(push)하거나 배포(roll out)하는 것을 금지합니다. - PR을 통한 수정 (Edit via PR, 저위험 리포지토리).
docs와 같은 리포지토리는 자유롭게 작성하되, 직접적인 푸시 (direct push) 대신 풀 리퀘스트 (Pull Request, PR)를 통해 변경 사항을 반영합니다.
이중 안전장치 (Belt and suspenders)입니다. 공유 워크스페이스 (shared workspace)에서 AI가 잘못된 것을 조용히 수정하거나 배포하는 가장 위험한 실패 모드 (failure mode)를 설계 단계에서 차단했습니다. 읽기 전용 에이전트는 할 수 없고, 프로덕션 에이전트는 승인 없이는 하지 않습니다.
실제 작동 예시, 엔드 투 엔드 (end to end)
요청: "프로덕션에서 서비스가 시작 시점에 충돌(crash)합니다. 애플리케이션 코드 문제인가요, 아니면 배포 설정 (deployment config) 문제인가요?"
1. 오케스트레이터 (Orchestrator)의 라우팅. 요청이 두 에이전트와 일치하므로, 병렬로 팬 아웃 (fan out)됩니다:
orchestrator
|--> backend-expert : "시작 시점의 충돌을 추적하세요. 코드가 실제로 잘못되었나요?"
`--> deploy-expert : "배포 설정이 원인일 수 있나요? 프로덕션에는 무엇이 설정되어 있습니까?"
2. 워커 (Workers)가 각각 깨끗한 컨텍스트 (context)에서 독립적으로 조사합니다.
backend-expert는 시작 과정을 추적하여, 서비스가 필수 환경 변수 (env var)를 읽으려 시도하고 해당 변수가 없으면 정상적으로 종료된다는 것을 발견합니다. 코드는 올바릅니다. 설계된 대로 빠르게 실패(failing fast)하고 있는 것입니다.deploy-expert는 프로덕션 Helm 값 (values)에 해당 환경 변수가 설정되어 있지 않음을 발견합니다 (스테이징 값에만 존재함). 근본 원인은 코드가 아니라 배포 설정 (deployment-config)의 공백입니다.
3. 오케스트레이터가 결과를 병합하여 답변합니다:
그것은 코드 버그가 아니라 배포 설정 (deployment-config)의 공백입니다. 해당 서비스는 시작 시 환경 변수
X를 필요로 하지만 (backend, file:line), 운영(prod) Helm values에는 설정되어 있지 않고 스테이징(staging)에만 설정되어 있습니다 (deploy, file:line). 운영 values에 이를 추가해 드릴 수 있습니다. 여기 한 줄짜리 diff가 있습니다. 적용할까요?
이것이 계층(tiers) 구조를 통해 얻는 보상입니다. backend는 읽기 전용(read-only)이었기에 설명만 할 수 있었습니다. 반면 deploy는 수정 권한이 있으므로, 공백을 찾아낸 동일한 에이전트가 이를 수정할 수 있습니다. 다만 해당 저장소(repo)가 운영 환경(production)에 영향을 미치기 때문에, 에이전트는 작업을 멈추고 먼저 diff를 사용자에게 보여줍니다. 사용자는 에이전트에게 운영 환경에 대한 무방비한 쓰기 권한(write access)을 부여하지 않고도, 단 한 번의 동작으로 수정 사항을 얻을 수 있습니다.
단일 세션(single session)이었다면 두 저장소를 순차적으로 읽고 두 가지 규칙 세트를 동시에 다루느라 분주했을 것입니다. 분리된 구조는 각 작업자(worker)의 전문성을 유지하면서 조사를 병렬로 수행하게 합니다.
일상적인 흐름: 단일 저장소 조회는 하나의 에이전트에게 전달되고, "이 내용을 작성해줘"라는 요청은 문서 에이전트(docs agent)에게 전달되어 PR(Pull Request)로 생성됩니다. 또한 사용자는 언제든 경로를 강제할 수 있습니다: "deploy-expert에게 서비스 Y의 운영 values에 어떤 환경 변수가 설정되어 있는지 물어봐줘."
운영, 튜닝 및 주의사항
성패를 결정짓는 몇 가지 요소는 다음과 같습니다:
- 라우팅(Routing)은
description에 달려 있습니다. 잘못된 라우팅은 모호한 설명에서 비롯됩니다. 설명을 명확히 하고, 설명이 서로 겹치지 않게 관리해야 라우팅이 동전 던지기처럼 무작위로 변하는 것을 방지할 수 있습니다. - 각 에이전트는 전체 시스템이 아닌 자신의 저장소를 설명합니다. 또한 에이전트별로
model을 조절할 수 있습니다 (추론에는 강력한 모델을, 조회에는 저렴한 모델을 사용). - "만약을 대비해서" 쓰기 도구(write tools)를 부여하지 마세요. 이는 가장 큰 안전상의 이점을 조용히 포기하는 행위입니다. 기본값은 읽기 전용(read-only)으로 설정하십시오.
- 작업자들은 메모리를 공유하지 않습니다. 서로의 상태(state)를 볼 수 없으므로, 오케스트레이터(orchestrator)가 전달해야 할 모든 정보를 넘겨주는 역할을 수행합니다.
한계점 및 더 무거운 대안
- 깊은 중첩(deep nesting) 불가. 작업자는 단일 계층으로만 존재합니다. "작업자에게 작업자가 필요한" 구조라면 오케스트레이터의 _단계(phases)_로 재구성하십시오.
- 작업자 간 공유 메모리 없음. 조정(Reconciliation)은 오케스트레이터의 몫입니다.
- 라우팅은 휴리스틱(heuristic) 방식이며, 엄격한 디스패치 테이블(dispatch table)이 아닙니다.
결정론적(deterministic), 대규모 팬아웃(fan-out), 예를 들어 "모든 리포지토리를 X에 대해 감사(audit)하라" 또는 모든 리포지토리에 걸친 마이그레이션과 같은 작업이 필요할 때는 **스크립트 기반 워크플로우(scripted workflow)**를 활용하세요. 이는 명시적인 루프(loop), 병렬 단계(parallel stages), 그리고 구조화된 출력(structured outputs)을 통해 에이전트들을 오케스트레이션(orchestrate)하는 코드를 의미합니다. 경험 법칙(Rule of thumb)은 다음과 같습니다: 디버깅과 탐색을 위해서는 대화형 서브 에이전트(interactive subagents)를 사용하고, 반복 가능하며 대규모로 병렬 처리가 필요한 패스(pass)가 필요할 때는 스크립트 기반 워크플로우를 사용하세요.
5분 만에 도입하기
- 워크스페이스 루트(workspace root)에
.claude/agents/디렉토리를 생성합니다. - 리포지토리당 하나의 에이전트 파일을 추가합니다 (위의 스니펫부터 시작하세요). 디버깅만 수행하는 리포지토리의 경우:
Edit/Write권한을 제외합니다. 프로덕션(production)에 영향을 미치는 소유 리포지토리의 경우: 해당 권한을 추가하고, 적용 전 확인(confirm-before-apply) 정책을 추가합니다. 문서(docs)와 같이 위험도가 낮은 리포지토리의 경우: 해당 권한을 추가하고, PR을 통한 반영(land-via-PR) 정책을 추가합니다. - 최상위
CLAUDE.md파일에 이슈 유형을 리포지토리에 매핑하는 짧은 분류(triage) 가이드를 추가합니다. - 각
description을 "...할 때 사용" 형식으로 작성하여 서로 중복되지 않게 유지합니다. - 여러 리포지토리에 걸친 질문을 던지고 오케스트레이터(orchestrator)가 작업을 위임(delegate)하는 것을 지켜보세요.
저는 멀티 리포(multi-repo) 워크스페이스에서 이 방식을 실행해 왔으며, 이는 저의 디버깅 방식을 바꾸어 놓았습니다. 모든 리포지토리에 빠져 허우적거리는 한 명의 제너럴리스트(generalist)는 코디네이터(coordinator)를 갖춘 소수의 스페셜리스트(specialist) 팀을 이길 수 없습니다. 사람에게도 마찬가지이며, 에이전트에게도 마찬가지입니다. 각 리포지토리에 소유자를 지정하고, 하나의 세션이 책임을 맡게 하며, 프로덕션에 접근하는 에이전트는 행동하기 전에 반드시 확인을 요청하도록 만드세요.
여러분은 이를 어떻게 구조화하고 계신가요? 리포지토리당 하나의 에이전트인가요, 도메인당 하나인가요, 아니면 다른 방식인가요? 댓글로 알려주세요. 👇
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기