4개의 에이전트, 77개의 프로젝트, 90분: 내가 프로덕션에서 실행하는 멀티 에이전트 Claude Code 패턴
요약
77개의 프로젝트를 90분 만에 감사하기 위해 4개의 Claude Code 에이전트를 활용한 멀티 에이전트 군집(swarm) 패턴을 소개합니다. 단일 에이전트의 컨텍스트 한계와 속도 문제를 해결하기 위해 TaskList와 이름 지정된 팀원을 활용한 조율 방식을 제안합니다.
핵심 포인트
- 단일 에이전트의 컨텍스트 윈도우 및 속도 한계 극복
- TeamCreate와 SendMessage를 활용한 에이전트 조율
- TaskList의 owner 필드를 이용한 작업 소유권 관리
- 멀티 에이전트 환경에서의 레이스 컨디션 주의사항
지난 화요일, 나는 지난 몇 년 동안 작성했던 모든 개발 프로젝트를 감사(audit)하기 위해 자리에 앉았다. 총 77개였다. 오래된 쿠리어-물류(courier-logistics) 모노레포(monorepos), 버려진 스타터(starters), 임상 추론 엔진(clinical reasoning engine), 대여섯 개의 Telegram 봇, 계속 재사용하고 있는 SaaS 프레임워크, 두 개의 스타트업, 그리고 Rust 프로세스 매니저까지. 스택도 다르고, 연차도 다르고, 고용주도 다르며, git 관리 상태(git hygiene)도 제각각이었다.
나는 이 작업을 병렬로 작동하는 4개의 Claude Code 에이전트에게 맡겼다. 그들은 약 90분 만에 작업을 끝냈다.
아래는 그들이 사용한 패턴이며, 왜 이 패턴의 변형된 버전이 현재 내 본업의 2,200개 커밋 규모의 프로덕션 코드베이스 내부에서 실행되고 있는지에 대한 이유이다.
왜 에이전트 하나로는 부족한가
단일 에이전트(Single-agent) Claude Code는 세 가지 벽에 부딪히기 전까지는 괜찮다.
첫째, 컨텍스트 윈도우(context window)가 거대한 코드베이스를 감당하지 못하고 막히기 시작한다. 둘째, 작업의 범위가 깊이보다 넓을 때, 순차적인 읽기 방식은 실제 시간(wall-clock)을 낭비한다. 셋째, 당신은 오케스트레이터(orchestrator)가 파일 차이점(file diffs)에 파묻히지 않고 판단력을 유지하기를 원한다.
군집(swarm) 방식은 이 세 가지를 모두 해결한다. 각 에이전트는 조각(slice)을 할당받는다. 리드(lead) 에이전트는 전체 지도(map)를 유지한다.
어려운 점은 에이전트를 생성하는 것이 아니다. 생성은 도구 호출(tool call) 한 번이면 된다. 진짜 어려운 점은 네 명의 에이전트가 서로 발을 헛디뎌 충돌하지 않도록 보장하는 것이다.
패턴
Claude Code에는 결합했을 때 깔끔한 군집(swarm)을 만들어주는 두 가지 기본 요소(primitives)가 포함되어 있다.
TeamCreate + 이름이 지정된 팀원들. 각 에이전트는 고유한 이름(analyst1, designer, metrics)을 가지며, 공유된 작업 목록(task list)을 가진 팀에 합류한다.
조율을 위한 SendMessage. 리드는 이름으로 어떤 팀원이든 지칭할 수 있다. 팀원들은 차례 사이에는 유휴(idle) 상태로 있다가 메시지를 받으면 깨어난다.
이것들을 TaskList에 연결하면 조율 평면(coordination plane)이 구축된다. 각 작업은 owner 필드를 가진다. owner = analyst3로 설정하는 것이 당신의 원자적 잠금(atomic lock)이다. 해당 필드가 변경될 때까지 그 작업은 해당 에이전트의 소유다.
감사를 위해 나는 프로젝트당 하나씩 총 77개의 작업을 만들었다. 나는 동일한 프롬프트 템플릿을 가진 4명의 analyst 팀원을 생성했다: 프로젝트를 코드 레벨(커밋 메시지가 아닌)에서 읽고, 존재한다면 Claude Code 세션 트랜스크립트(transcripts)를 파싱하며, 프로젝트별 보고서(dossier)를 작성하라. 그런 다음 그들을 풀어 놓았다.
내가 첫날 배포했던 레이스 컨디션(race condition)
첫 번째 시도: 각 팀원(teammate)은 유휴 상태(idle)일 때 TaskList를 실행하여, 소유자(owner)가 없는 가장 낮은 ID의 작업을 찾아 이를 점유하고, 수행한 뒤, 완료로 표시하고, 이 과정을 반복한다.
처음 10분 동안은 잘 작동했다. 그러다 두 명의 분석가(analysts)가 동일한 초(second) 내에 같은 작업을 가로챘다. 소유자를 설정하는 TaskUpdate가 두 번 실행되었다. 두 명 모두 작업을 수행했다. 두 번째 작업자는 동일한 내용의 약간 다른 버전으로 첫 번째 보고서(dossier)를 덮어썼다.
전형적인 낙관적 동시성 제어(optimistic-concurrency) 실수였다. 해결책은 지루하지만 확실하다. 낮은 ID의 작업을 두고 경합(racing)하는 것을 멈추는 것이다. 리드(lead) 에이전트가 군집(swarm)을 다시 가동하기 전에, 남은 전체 범위에 대해 소유자를 미리 할당(pre-assign)한다.
나는 남은 작업을 네 개의 연속된 블록(#33–43, #46–56, #57–67, #68–77)으로 분할하고 모든 소유자를 사전에 설정했다. 이제 어떤 에이전트가 목록을 읽더라도 모든 작업은 이미 누군가에 의해 점유된 상태다. 경합할 대상이 남지 않았다. 각 에이전트는 자신의 블록을 ID 순서대로 처리하고, 블록이 비면 보고한다.
남은 실행 시간 동안 충돌(collision)은 제로였다.
결과물
60개의 보고서(dossier) 파일이 생성되었다. showcase 태그가 붙은 22개, supporting 38개, skip 17개였다. skip된 것들은 정직한 결과였다. 내 커밋(commit) 비중이 10% 미만인 클라이언트 작업, 버려진 스캐폴드(scaffolds), 빈 스텁(stubs) 등이 포함되었다. 개인 브랜딩 감사(personal-brand audit)가 목표일 때, 잘못된 데이터는 데이터가 없는 것보다 더 나쁘다.
흥미로운 발견은 헤드라인 프로젝트에 있지 않았다. 그것들은 교차 분석(cross-cuts)에서 나타났다. 나의 보일러플레이트(boilerplate)가 조용히 네 개의 제품 밑바탕이 되어 있었다. 동일한 코드베이스가 다섯 명의 서로 다른 클라이언트에게서 나타났다. 한 헬스케어 CRM(healthcare CRM)에서는 내가 의존해 왔던 표면적인 메타데이터에는 약 700개라고 되어 있었지만, 실제 내 커밋은 총 2,208개 중 962개(최고 기여자)에 달했다. 수동적인 git 히스토리는 손실이 발생하기 쉽다. 공유된 출력물을 가진 군집(swarm)은 그 편차(drift)를 잡아낸다.
프로덕션 버전
이것은 취미용 패턴이 아니다. 동일한 멀티 에이전트 Claude Code 워크플로우가 본업인 헬스케어 CRM의 2,200개 커밋 규모 코드베이스에서도 실행되고 있다. 그곳의 CLAUDE.md는 네 개의 커스텀 서브 에이전트(subagents)를 정의한다:
django-backend-specialist— 모델(models), 뷰(views), URL을 담당합니다.advertiser-frontend-developer— 광고주 대시보드(advertiser dashboard)를 담당합니다.patient-frontend-specialist— 환자 포털(patient portal)을 담당합니다.linting-specialist— 마지막에 실행되며, 디프(diff)를 담당합니다.
에이전트 간의 명시적인 계약(inter-agent contract)이 존재합니다. 백엔드(backend)가 뷰(view)를 변경하면, 새로운 엔드포인트(endpoint), URL, 응답 형태(response shape)를 보고합니다. 프론트엔드(frontends)는 작업을 시작하기 전에 해당 데이터를 요청해야 함을 알고 있습니다. 린팅(Linting)은 체인의 중간이 아닌 마지막에 실행됩니다. 오케스트레이터(orchestrator, 즉 나)는 아키텍처(architecture)를 보유하고, 에이전트(agents)는 파일 디프(file diffs)를 보유합니다.
이 패턴을 사용하여 출시하는 제품 중 하나인 내부 도구(internal-tools) 재구축 프로젝트에서는, 커밋(commits)의 약 29%가 나의 지시하에 AI가 작성했습니다. 나머지 71%는 내가 작성했습니다. 이 비율은 git shortlog에서 명확히 확인할 수 있습니다. 숨길 것도, 부풀릴 것도 없습니다.
솔직한 부분
이것이 시니어 엔지니어링 판단력(senior engineering judgment)을 대체하지는 않습니다. 오케스트레이터는 여전히 무엇이 '좋은 결과물'인지 알아야 합니다. 분석가(analyst)가 부실한 보고서(dossier)를 가져오면, 이를 인지하고 재작업을 요청해야 합니다. 두 에이전트가 의견 충돌을 일으킨다면? 당신이 해결해야 합니다.
이 방식이 제공하는 것은 레버리지(leverage)입니다. 군집(swarm)을 거느린 한 명의 엔지니어가 스태프 레벨(staff-level)의 업무 범위를 커버합니다. 병목 현상(bottleneck)은 타이핑 속도에서 판단의 질(judgment quality)로 이동하며, 이는 우리가 가져야 할 올바른 병목입니다.
군집이 실제로 무엇을 만들어냈는지 보고 싶다면, 60개의 보고서(dossiers)가 davr.dev/projects에 있습니다. 각 보고서는 마케팅 문구가 아닌 코드 수준의 감사(code-level audit) 결과물입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기