Chapter 12. Subagent와 병렬 워크플로

생산성의 병목은 생성 속도가 아니라 조정 비용이다
Codex를 처음 쓸 때는 보통 한 에이전트에게 모든 일을 몰아준다. 버그 원인도 찾고, 관련 파일도 읽고, 테스트도 돌리고, 문서도 확인하고, 마지막엔 수정까지 하라고 시킨다. 작은 작업에서는 이 방식이 꽤 잘 먹힌다. 문제는 작업이 커질수록 한 스레드 안에 계획, 로그, 스택 트레이스, 추측, 실패한 시도, 테스트 출력이 한데 뒤엉킨다는 점이다. 그러면 에이전트가 똑똑하지 않아서가 아니라, 회의실이 너무 시끄러워서 판단이 흐려진다.
Codex의 서브에이전트는 이 문제를 정면으로 푼다. 부모 에이전트가 전문 역할을 가진 자식 에이전트들을 병렬로 띄우고, 각자 결과를 가져오면 마지막에 하나로 모아 답하는 구조다.1 중요한 점은 이것이 자동 마법이 아니라는 것이다. Codex는 사용자가 명시적으로 나눠 달라고 했을 때만 서브에이전트를 스폰한다. 기본 동시 스레드 상한은 6, 깊이는 1이어서 실전에서는 "자식까지만 두고, 손자부터는 만들지 않는다"는 운영이 가장 안전하다.2
이 장의 핵심은 단순하다. 진짜 생산성 차이는 한 에이전트를 잘 다루는 데서 끝나지 않는다. 여러 에이전트를 서로 안 부딪히게, 같은 기준으로, 같은 산출물 형식으로 굴릴 때 비로소 속도가 붙는다.
Subagent가 필요한 순간
서브에이전트는 모든 작업에 쓰는 만능 버튼이 아니다. 특히 읽기 중심 작업에서 가장 먼저 빛난다. 반대로 여러 에이전트가 동시에 같은 파일을 고치게 만들면, 속도보다 정리 비용이 더 빨리 커진다.
병렬화 가치가 큰 대표 장면은 다음 세 가지다.
탐색: 낯선 레포에서 인증 흐름, API 진입점, 테스트 위치를 동시에 찾을 때
테스트: 단위 테스트, 통합 테스트, E2E 시나리오를 모듈별로 나눠 늘릴 때
트리아지: 장애 재현, 로그 확인, 영향 범위 분석을 동시에 진행할 때
쉽게 말하면 이렇다. "무엇이 문제인지 찾는 일"은 병렬화하기 쉽고, "같은 파일을 고치는 일"은 병렬화하기 어렵다. 그래서 실전에서는 읽기 병렬화 먼저, 쓰기 병렬화는 격리 후에가 기본 원칙이 된다.
메인 스레드를 깨끗하게 유지하는 위임 패턴
잘 굴러가는 멀티 에이전트 워크플로는 부모 에이전트가 모든 걸 직접 하지 않는다. 부모는 팀장처럼 목표를 정의하고, 자식은 담당자처럼 좁은 임무만 수행한다. 보통은 다음 흐름이 가장 안정적이다.
부모 에이전트가 목표와 완료 조건을 적는다.
explorer 계열 에이전트가 읽기 전용으로 코드 구조와 영향 범위를 조사한다.
worker 계열 에이전트가 좁은 파일 범위에서만 구현한다.
reviewer 또는 tester 에이전트가 회귀, 보안, 테스트 누락을 점검한다.
이 구조의 장점은 메인 스레드가 계속 깨끗하다는 데 있다. 부모는 핵심 요구사항과 최종 의사결정만 붙들고 있고, 세부 탐색 로그는 각 스레드 안에 가둬둘 수 있다. 실행 중인 스레드는 /agent 같은 제어 흐름으로 오가며 확인하고, 필요한 경우 특정 스레드만 더 파보게 하거나 멈출 수 있다.2
예를 들어 Next.js + Supabase 프로젝트에서 "소셜 로그인 후 간헐적으로 대시보드가 아닌 루트로 튕기는 버그"를 고친다고 해보자. 이때 부모에게 이렇게 요청할 수 있다.
현재 브랜치에서 로그인 리다이렉트 버그를 해결해줘.
먼저 서브에이전트로 분업해.
- explorer 1: auth callback, middleware, redirect 관련 실제 실행 경로를 추적
- explorer 2: Supabase 세션 복구와 쿠키 처리 지점을 정리
- reviewer 1: 회귀 위험과 누락된 테스트 포인트를 찾기
모든 결과를 기다린 뒤,
부모가 수정 계획을 5줄 이내로 요약하고,
그 다음 worker 1개만 사용해 최소 수정으로 구현해.
마지막으로 테스트 결과와 diff 검토 포인트를 정리해.포인트는 두 가지다. 첫째, 처음부터 쓰기 에이전트를 여러 개 띄우지 않는다. 둘째, 탐색 결과를 받은 뒤에야 단일 worker에게 구현을 맡긴다. 이 순서만 지켜도 헛손질이 크게 줄어든다.
Worktree가 없는 병렬 쓰기는 거의 금지에 가깝다
여러 에이전트가 동시에 코드를 수정하려면, 각자 자기 책상에서 일해야 한다. 같은 책상 위에서 세 사람이 한 파일을 열고 저장하기 시작하면 그건 병렬 작업이 아니라 난장판이다.
Codex 앱의 worktree는 바로 이 문제를 해결한다. 같은 프로젝트 안에서 여러 독립 작업을 서로 간섭 없이 돌릴 수 있게 해주며, 자동화 작업도 전용 백그라운드 worktree에서 돌아가도록 설계돼 있다.3 Git worktree를 쓰면 하나의 저장소 이력을 공유하면서도, 각 작업은 별도 디렉터리와 별도 작업 트리에서 움직인다. 쉽게 말해 "클론은 아니지만, 각자 자기 체크아웃을 가진다"에 가깝다.4
예를 들어 프론트와 백엔드, 테스트를 동시에 진행하려면 이렇게 나눌 수 있다.
git worktree add -b fix/auth-redirect ../wt-auth main
git worktree add -b feat/orders-validation ../wt-orders main
git worktree add -b test/e2e-auth ../wt-e2e main이제 ../wt-auth에서는 Next.js 로그인 리다이렉트만, ../wt-orders에서는 Express + MongoDB 주문 검증만, ../wt-e2e에서는 Playwright 테스트만 다루게 만들 수 있다. 각 에이전트는 서로의 미커밋 변경을 밟지 않는다.
하지만 worktree를 만들었다고 끝은 아니다. 실무에서는 아래 문제가 반드시 따라온다.
| 문제 | 왜 생기나 | 권장 대응 |
|---|---|---|
| 의존성 누락 | node_modules, .venv, .env 같은 gitignored 파일은 새 worktree에 없다 | setup 스크립트로 설치/복사 자동화 |
| 포트 충돌 | 모든 에이전트가 3000번 포트를 쓰려 든다 | 3000, 3010, 3020처럼 인덱싱 |
| DB 충돌 | 같은 로컬 DB를 공유하면 테스트 데이터가 섞인다 | 테스트 DB를 worktree별로 분리 |
| 머지 충돌 | 다른 worktree가 같은 파일을 고친다 | 파일 소유권을 먼저 나눈다 |
| 비용 폭주 | 스레드 수만큼 토큰과 검토 비용이 늘어난다 | 동시 에이전트 수를 3~5개로 제한 |
특히 첫 번째가 많이 당황스럽다. worktree는 tracked 파일만 가져오므로, "왜 실행이 안 되지?"의 절반은 사실 의존성과 환경 파일 때문이다.4 그러니 병렬 작업 전에 .codex/setup.sh 같은 준비 스크립트를 두는 습관이 좋다.
멀티 에이전트 v2 식으로 운영하기
멀티 에이전트 워크플로가 성숙해질수록 중요한 건 단순한 "병렬 실행"이 아니라 "어떻게 서로를 가리키고, 어떻게 상태를 넘기느냐"가 된다. 최근 멀티 에이전트 v2 흐름에서 강조되는 변화도 여기에 있다. 예전처럼 불투명한 세션 ID와 평평한 네임스페이스에 의존하기보다, 더 명시적인 경로 기반 주소 지정과 구조화된 메시징으로 옮겨가고 있다는 점이다.5
말이 조금 어려워 보이지만, 운영 원칙으로 번역하면 쉽다.
첫째, 에이전트 이름을 역할과 범위가 보이게 짓는다.
app/auth/explorerapp/auth/workerapp/auth/testerapi/orders/workerops/incident/triage
이렇게 해두면 부모 입장에서 "누가 무엇을 맡고 있는지"가 한눈에 보인다. worker-3, agent-b 같은 이름은 당장은 편해 보여도, 세 개만 넘어가면 머릿속이 엉킨다.
둘째, 자유문 채팅보다 핸드오프 포맷을 고정한다. 예를 들어 부모가 자식에게 넘기는 작업은 다음 항목을 항상 포함하게 만든다.
task: 주문 생성 API의 입력 검증 추가
scope:
read:
- src/routes/orders.ts
- src/services/orderService.ts
- tests/
write:
- src/routes/orders.ts
- tests/orders.validation.test.ts
forbidden:
- package.json
- db/migrations/
success_criteria:
- 잘못된 payload에서 400 반환
- 기존 정상 주문 테스트 유지
- 신규 테스트 3개 이상 추가
output:
- 변경 파일 목록
- 실행한 테스트 명령
- 남은 리스크 3줄 요약이 포맷의 장점은 자식이 똑똑해져서가 아니라, 엉뚱하게 똑똑해지는 일을 막는 데 있다. 범위를 보이고, 금지 구역을 적고, 성공 조건을 숫자로 쓰면 에이전트가 멋대로 리팩터링 욕심을 낼 여지가 줄어든다.
셋째, 부모는 대화 로그를 수집하지 말고 산출물을 수집해야 한다. 좋은 멀티 에이전트 운영은 "뭐 했어?"라고 매번 캐묻는 방식이 아니다. 대신 각 자식이 짧은 보고서, diff, 체크리스트, 실패 분류표처럼 검토 가능한 결과물을 남기게 한다. 실제 Codex 기반 QA 템플릿들도 stage ownership을 분명히 두고, 토큰 예산과 산출물 형식을 관리하는 방향으로 설계된다.7
실전 패턴 1: 탐색을 병렬화하고 구현은 하나로 모으기
가장 추천하는 시작점이다. 쓰기 충돌이 거의 없고, 체감 속도는 매우 크다.
예를 들어 Next.js + Supabase SaaS에서 결제 완료 후 프로필이 갱신되지 않는 문제를 다룬다면 다음처럼 나눈다.
explorer A: 결제 완료 후 호출되는 서버 액션과 웹훅 경로 추적
explorer B: 프로필 캐시 무효화와 revalidation 지점 정리
explorer C: 관련 테스트와 누락된 시나리오 찾기
부모는 세 결과를 받은 뒤 "원인은 webhook 처리 지연인지, 캐시 문제인지, 클라이언트 상태 동기화 문제인지"를 정리한다. 그 다음에만 worker 하나를 띄워 수정한다. 이 방식은 빠를 뿐 아니라, 사람 리뷰어가 읽을 때도 왜 그 수정이 나왔는지 맥락이 선명하다.
실전 패턴 2: 테스트를 모듈 단위로 병렬화하기
Express + MongoDB 프로젝트에서 주문 API를 확장했다고 해보자. 이때 구현이 끝난 뒤 테스트를 한 스레드에 다 몰아주면 오래 걸리고, 실패 원인도 뒤섞인다. 대신 다음처럼 분리한다.
worker 1: DTO/validation 단위 테스트 추가
worker 2: 라우트 통합 테스트 추가
reviewer 1: flaky 가능성이 있는 시간 의존 테스트 점검
여기서는 worker 둘 다 파일을 쓰므로 worktree를 분리하는 편이 낫다. tests/unit/orders.*와 tests/integration/orders.*처럼 소유 파일이 명확하면 충돌 확률이 낮다. 반대로 둘 다 tests/orders.test.ts를 건드린다면 병렬화하지 말고 순차로 가야 한다.
실전 패턴 3: 트리아지를 세 갈래로 찢기
트리아지란 쉽게 말해 "무슨 일이 났고, 어디까지 번졌고, 당장 뭘 해야 하는지 판단하는 일"이다. 장애 대응에서는 이 작업이 특히 잘 쪼개진다.
슈퍼앱 클론 서비스에서 "푸시 알림 발송 후 앱이 느려지고 일부 메시지가 중복 저장된다"는 장애가 났다고 해보자. 이때는 보통 이렇게 나눈다.
ops/incident/reproducer: 문제 재현 단계와 조건 수집ops/incident/log-reader: 서버 로그, 큐 적체, 에러 패턴 확인ops/incident/impact-mapper: 어떤 사용자 군과 어떤 기능이 영향받는지 정리
부모는 이 세 결과를 합쳐서 판단한다. 지금 필요한 게 핫픽스인지, 롤백인지, 큐 비우기인지, 알림 기능만 임시 차단할지 말이다. 장애 상황에서 가장 위험한 건 한 에이전트가 재현도 하기 전에 코드를 고치기 시작하는 것이다. 트리아지는 수정이 아니라 판단의 문제다. 그래서 읽기 전용 병렬화가 특히 강하다.
충돌을 줄이는 7가지 규칙
병렬 에이전트가 많아질수록 중요한 건 모델 성능보다 운영 규율이다. 아래 규칙은 팀 단위로 거의 그대로 가져다 써도 된다.
한 파일 한 오너: 병렬 에이전트마다 수정 가능한 파일을 먼저 나눈다.
읽기 에이전트 우선: explorer, reviewer, triager를 늘리고 writer는 최소화한다.
max_depth는 1 유지: 자식의 자식까지 열기 시작하면 관리보다 구경거리가 먼저 생긴다.2
동시 에이전트는 3~5개가 현실적: 그 이상은 코드 생성보다 인간 검토가 병목이 되기 쉽다.6
리뷰어는 상시 읽기 전용으로 둔다: 구현 에이전트와 분리된 품질 게이트가 필요하다.6
상태 값을 표준화한다:
blocked,needs-review,done처럼 짧고 같은 언어로 보고하게 만든다.토큰 예산을 작업 단위로 자른다: 오래 헤매는 에이전트는 똑똑한 게 아니라 비싼 경우가 많다.67
이 규칙만 지켜도 멀티 에이전트가 "우와, 동시에 많이 돌았다"에서 끝나지 않고, "아침에 확인할 만한 결과가 쌓여 있다"로 바뀐다.
무엇을 병렬로 하고, 무엇을 순차로 할까
병렬화가 항상 답은 아니다. 아래 표처럼 고르는 것이 좋다.
| 작업 유형 | 병렬화 추천 | 이유 |
|---|---|---|
| 코드베이스 탐색 | 예 | 서로 다른 경로를 읽기만 하면 됨 |
| PR 리뷰를 보안/버그/테스트로 분리 | 예 | 평가 관점이 독립적임 |
| 테스트 생성(모듈별 분리) | 조건부 예 | 파일 소유권만 분리되면 효과 큼 |
| 장애 트리아지 | 예 | 재현, 로그, 영향 범위가 잘 쪼개짐 |
| 같은 파일 대규모 리팩터링 | 아니오 | 충돌과 병합 비용이 큼 |
| 스키마 변경 + 마이그레이션 + 데이터 보정 | 대체로 아니오 | 순서 의존성이 강함 |
| 인증/권한 핵심 로직 전면 수정 | 신중 | 리뷰와 검증 비용이 매우 큼 |
한 문장으로 정리하면 이렇다. 여러 요리사를 한 주방에 넣는 건 괜찮지만, 한 도마 위에 동시에 세우면 안 된다.
마무리
시니어스럽게 Codex를 쓴다는 건 프롬프트를 화려하게 쓰는 일이 아니다. 어떤 일은 탐색자에게, 어떤 일은 구현자에게, 어떤 일은 리뷰어에게 넘겨야 하는지 구분하는 일이다. 부모 스레드는 전략과 판단을 맡고, 서브에이전트는 증거와 결과를 모으고, worktree는 충돌을 물리적으로 끊고, 사람은 마지막 diff와 리스크를 검토한다.
한 에이전트를 잘 쓰면 작업이 빨라진다. 여러 에이전트를 질서 있게 굴리면 개발 방식 자체가 바뀐다. Codex의 진짜 맛은 그때부터다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.