이 글에 대해
Claude Code에는 여러 에이전트를 한꺼번에 굴리는 기능이 둘 있다. dynamic workflow와 /batch다. 둘 다 "병렬로 에이전트를 띄운다"는 설명이 붙어서, 기능 소개만 봐서는 무엇이 다른지 손에 잡히지 않았다.
그래서 안 써본 걸 써본 척 쓰는 대신, 실제 프로젝트 하나에 두 기능을 직접 돌려보고 차이를 기록했다. 대상은 본인이 만드는 macOS 회의록 앱이다. workflow로는 모듈을 읽어 아키텍처 지도를 만들었고, batch로는 공개 API에 문서 주석을 달았다.
이 글은 그 두 번의 실행에서 실제로 관찰한 것, 그리고 둘의 차이가 어디에 있는지가 처음 생각과 달랐다는 이야기다.
§1둘 다 "병렬"이라는 말이 함정이다
문서 기준으로 정리하면 이렇다. /batch는 동일한 패턴의 대규모 변경을 여러 워커가 병렬로 처리하고, dynamic workflow는 복잡한 목표 하나를 조사부터 실행, 검증, 종합까지 단계로 나눠 설계한 뒤 그 과정을 병렬로 돌린다.
둘 다 "여러 에이전트가 동시에 돈다"는 한 줄로 요약되니 같은 기능처럼 들린다. 이 차이는 설명만으로는 안 잡히고, 직접 돌려봐야 보였다.
§2실험 1: dynamic workflow로 코드베이스를 읽었다
먼저 workflow다. 작업은 "이 앱의 모듈을 나눠 읽고 아키텍처 지도를 만들어라"였다. dynamic workflow는 복잡한 목표 하나를 받으면 그 목표에 맞춰 조사·실행·종합 단계를 가진 병렬 파이프라인을 구성해 돌리는데, 이번에는 모듈마다 리더 에이전트를 하나씩 병렬로 7개 띄워 소스를 읽게 하고(fan-out), 그 요약을 모아 종합 에이전트 하나가 지도로 합치는(barrier) 흐름으로 짜였다.
실측은 이렇다. 에이전트 8개 · 페이즈 2개 · 약 32.6만 토큰 · 툴 호출 75회 · 6분 44초. 도는 동안 /workflows를 치면 어느 스텝이 돌고 있는지 라이브 트리로 보였고, 결과는 코드 변경 없이 아키텍처 지도 하나로 돌아왔다. 모듈 간 순환 의존 한 곳과 양방향 결합 한 곳까지 짚어 줬다.
여기서 핵심은 목표 하나를 향해 단계가 설계되고, 결과가 한 덩어리로 종합돼 돌아온다는 점이다. 몇 갈래로 나눠 읽고 언제 합칠지를 미리 정해 둔 흐름이었고, 코드는 건드리지 않았다.
dynamic workflow 실행 구조. 하나의 목표가 7개 병렬 리더 에이전트로 fan-out 되어 모듈을 읽고, 종합 에이전트 한 곳으로 barrier 되어 아키텍처 지도 한 건으로 수렴한다. 코드 변경은 없다
§3실험 2: /batch로 공개 API에 문서를 달았다
batch는 시작부터 정반대였다. 작업은 "공개 API 약 40개에 문서 주석을 달아라"였는데, /batch를 치자 바로 워커가 뜨지 않고 먼저 계획을 세우고 승인을 받았다.
계획 단계로 들어가 파일 수, 선언 수, 기존 주석 컨벤션, 빌드 의존성을 직접 grep으로 조사하고, 분할 단위와 검증 방식 두 가지를 본인에게 물은 뒤, 계획서를 쓰고 승인을 받고 나서야 워커를 띄웠다. 승인 전에는 워커가 단 하나도 뜨지 않았다.
실측은 이렇다. 에이전트 17개(모듈 워커 10 + 중단 복구 7) · 워커마다 격리 git worktree · PR 10개 · 코드 변경 0줄 · 전체 약 20분. 분할 기준은 파일 수 균등이 아니라 모듈(디렉터리) 경계였다.
/batch 실행 구조. 목표가 조사·계획을 거쳐 승인 게이트를 통과한 뒤에야 worktree 워커들로 fan-out 되고, 각 워커가 모듈별로 별도 PR을 열어 결과가 PR 여러 개로 발산한다
batch 분할을 파일 수로 나눌지 모듈로 나눌지
batch는 계획 단계에서 분할 단위를 본인에게 물었다. 파일 수로 균등하게 나누면 워커 부하는 고르지만 한 모듈이 여러 PR로 쪼개져 리뷰가 흩어지기 때문에, 모듈 경계로 잘라 모듈과 PR을 1:1로 맞췄다. UI 모듈 하나가 약 4,300줄로 유독 무거웠지만, 분할 일관성을 위해 워커 하나를 유지했다.
품질도 단순히 "주석이 달렸다"가 아니었다. 모든 PR이 풀 빌드를 통과했고 코드는 한 줄도 바뀌지 않았다. 주석에는 코드를 그대로 옮기는 대신 동작의 근거가 한국어로 적혀 있었고, 한 워커는 추론으로 잘못 단 주석 두 개를 스스로 grep으로 검증해 고친 뒤 올렸다.
§4둘의 진짜 차이점
처음에는 단순하게 봤다. workflow는 읽기만 했고 batch는 코드를 고쳤으니, 둘의 차이는 작업이 읽기냐 쓰기냐에 있다고 생각했다.
그런데 이건 틀렸다. dynamic workflow도 코드를 고친다. 여러 곳을 찾아 각각 worktree에서 변환하고 검증하는 마이그레이션이 workflow의 대표 용도 중 하나이고, 이번에 본인이 읽기 작업을 골랐을 뿐 workflow가 못 고치는 게 아니다.
진짜 차이점은 읽기냐 쓰기냐가 아니라 흐름을 누가 쥐고, 결과가 어떻게 묶이는지였다.
둘 다 같은 병렬인데 무엇이 다른가
두 번의 실행을 같은 표에 놓으면 차이가 분명해진다.
| 기준 | dynamic workflow | /batch |
|---|---|---|
| 이번 작업 | 아키텍처 매핑 (읽기) | 공개 API 문서 주석 |
| 에이전트 수 | 8개 | 17개 (워커 10 + 중단 복구 7) |
| 소요 시간 | 6분 44초 | 약 20분 |
| 토큰 | 약 32.6만 | 측정 안 함 |
| 산출물 | 아키텍처 지도 1건, 코드 무변경 | PR 10건, 코드 0줄 변경 |
| 동작 방식 | 목표에 맞춰 조사·실행·종합 단계 구성 | 반복 변경을 자동 계획 후 워커에 분산 |
| 실행 전 게이트 | 없음 | 계획 승인 게이트 |
| 격리 | 필요할 때 worktree | 워커마다 worktree |
| 진행 가시성 | /workflows 라이브 트리 | 트리 밖, 완료 알림으로 추적 |
| 맞는 일 | 복잡한 목표 하나 풀기 | 동일 패턴 대규모 반복 |
§5직접 돌려보고 나서야 알게 된 것들
다음 세 가지는 직접 돌려보지 않았다면 알기 어려운 것들이다.
첫째, 진행 가시성이 다르다.
workflow는 /workflows에 라이브 트리로 떴지만, batch 워커는 같은 트리에 잡히지 않았다. 완료가 알림으로 하나씩 들어왔고, 어디까지 됐는지는 본인이 표로 직접 추적했다. workflow가 진행 트리를 보여주는 도구라면, batch는 완료를 보고받아 본인이 표를 짜는 도구에 가까웠다.
둘째, batch는 중간에 한 번 멈췄다.
1차 워커 10개 중 7개가 편집까지 끝내고도 커밋, 푸시, PR을 빠뜨린 채 턴을 종료했고, 자력으로 끝낸 건 3개뿐이었다. 나머지는 이어받기 실행으로 복구해 결국 PR 10개를 다 만들었지만, 이런 중단과 복구는 직접 돌려보지 않았다면 알 수 없었을 내용이다.
셋째, 돌리기 전 git 상태가 중요했다.
그 프로젝트는 리팩터링이 진행 중이라, 작업 트리에 커밋하지 않은 변경이 남아 있었다.
커밋 안 한 변경이 남은 채로 batch를 돌릴지, 정리 후 돌릴지
batch 워커는 현재 커밋(HEAD) 기준으로 격리 worktree를 만든다. 진행 중인 작업이 커밋되지 않은 채로 돌리면 워커가 옛 상태 기준으로 PR을 열어 진행 중 작업과 엉키기 때문에, 진행 중 리팩터링을 먼저 커밋해 트리를 비우고 문서 작업용 별도 브랜치에서 batch를 돌렸다.
§6정리
병렬이라는 단어에 속지 말자. 복잡한 목표 하나를 여러 단계로 풀어 병렬로 돌리고 결과를 한 덩어리로 받고 싶으면 dynamic workflow, 같은 패턴을 여러 곳에 대규모로 적용하고 변경마다 PR로 받고 싶으면 /batch다. 읽기냐 쓰기냐의 문제가 아니라, 단계를 설계해 푸는 복잡한 목표냐 정해진 반복 변경이냐, 결과를 종합해 받느냐 PR로 흩어 받느냐에서 갈린다.
한 가지 비용은 미리 염두에 둘 만하다. batch의 PR은 만들 때만 병렬이 아니라 되돌릴 때도 N개다. 결과가 마음에 안 들면 PR 열 개를 각각 닫고 워크트리 브랜치를 정리해야 한다. 결과가 한 덩어리로 모이는 workflow와 달리, 발산형은 산출도 원복도 워커 수에 비례한다.
이 결론은 두 기능을 실제로 한 번씩 돌려보고 나서야 정리됐다. 써본 척만 했다면 읽기와 쓰기라는 틀린 기준에서 멈췄을 것이다.