홈시리즈멘토링

© 2026 정기창. All rights reserved.

본 블로그의 콘텐츠는 CC BY-NC-SA 4.0 라이선스를 따릅니다.

☕후원하기소개JSON Formatter러닝 대기질개인정보처리방침이용약관

© 2026 정기창. All rights reserved.

콘텐츠: CC BY-NC-SA 4.0

☕후원하기
소개|JSON Formatter|러닝 대기질|개인정보처리방침|이용약관

컨텍스트 포화를 구조로 막기 (고급편): 적대적 검증으로 안전하게 병렬화하기

정기창·2026년 6월 1일

기본편에서는 여러 단계짜리 작업의 컨텍스트 포화를 구조로 막았습니다. Phase마다 격리 subagent를 두고, 단계 사이를 컨텍스트가 아니라 파일로 인계하면, 무거운 맥락은 자식과 함께 사라지고 메인 오케스트레이터는 끝까지 가볍게 유지됩니다. 다만 거기까지는 어디까지나 순차 실행이었습니다. 이번엔 한 걸음 더 나아가, 그 오케스트레이션 위에서 서로 독립적인 트랙을 안전하게 병렬로 돌리는 법을 다뤄보려 합니다. 그리고 그 과정에서 가장 인상 깊었던 것 — advocate와 critic을 붙여 '장밋빛 병렬화 계획'을 적대적으로 깨뜨리고, 의심스러우면 순차로 떨어지는 보수적 자동 판정 — 을 정리합니다.

이 글은 기본편의 격리 subagent + 파일 핸드오프 패턴을 이미 알고 있다고 가정합니다. AI 코딩 에이전트로 다단계 작업을 자동화해본 경험이 있고, "이 단계들 중 일부는 서로 독립적인데 동시에 돌리면 빠르지 않을까"라는 생각을 한 번쯤 해본 분에게 닿기를 바랍니다.

왜 '병렬 스킬을 새로 짓는' 접근은 답이 아니었나

병렬화를 떠올렸을 때 가장 먼저 든 충동은 범용 병렬 오케스트레이터를 새로 하나 만들자는 것이었습니다. 여러 트랙을 받아 동시에 뿌리고, 결과를 모아주는 전용 도구 말입니다. 그런데 곰곰이 생각해보니 이 길은 위험했습니다. 순차 오케스트레이터에는 이미 기본편에서 쌓은 안전장치 — 파일 핸드오프, STATUS 기반 자가복구, 루프 가드 — 가 들어 있는데, 병렬 버전을 따로 지으면 그 검증된 기반을 처음부터 다시 만들거나, 두 갈래로 갈라진 코드를 양쪽 다 유지해야 합니다.

그래서 방향을 바꿨습니다. 새 도구를 짓는 대신, 기존 순차 오케스트레이터에 좁은 병렬 모드를 옵트인으로 얹는 것입니다. 기본은 어디까지나 순차이고, 서로 독립인 트랙이 분명할 때만 오케스트레이터가 보수적으로 판정해 병렬로 돌립니다. 사용자가 별도 플래그를 줄 필요도 없습니다. 핵심 원칙은 단 하나였습니다. 의심스러우면 순차로 떨어진다.

이 결정 자체가 사실 글의 결론을 미리 말해버립니다. 병렬화는 야심차게 밀어붙이는 것이 아니라, 기회가 명백할 때만 기회적으로, 그것도 보수적으로 취하는 것이라는 점입니다.

장밋빛 계획을 깨뜨리는 적대적 검증

병렬화에서 가장 어려운 부분은 "무엇을 동시에 돌려도 되는가"를 정하는 일입니다. 처음에는 단순하게 생각했습니다. 계획에 단계가 여럿 있으니, 의존성만 없으면 wave로 묶어 한꺼번에 돌리면 되지 않겠냐는 것이었습니다. 그런데 이 낙관이야말로 가장 위험한 지점이었습니다.

그래서 판정 과정을 한 명의 결정자에게 맡기지 않고 적대적 구조로 짰습니다. 흐름은 이렇습니다.

1. 의존성·충돌 분석 — 어떤 트랙이 어떤 파일·자원을 건드리는가
2. wave 스케줄 종합 — 충돌 없는 것끼리 묶어 동시 실행 후보를 만든다
3. advocate vs critic — 같은 계획을 두고 한쪽은 옹호, 한쪽은 반박
4. 보수적 자동 판정 — 반박이 살아남으면 그 부분은 순차로 강등

여기서 advocate는 "이렇게 묶으면 동시에 돌릴 수 있다"고 가장 공격적인 병렬 계획을 주장합니다. 반대로 critic은 그 계획을 실제 git 상태와 디스크 상태에 대조하며 깨뜨리려 듭니다. 둘을 머리를 맞대게 한 이유는 분명합니다. 한 에이전트에게 "병렬화 계획 세워줘"라고만 하면, 그 에이전트는 자기 계획을 옹호하는 쪽으로 기울기 쉽습니다. 자기 계획을 스스로 깎아내릴 동기가 약한 것입니다.

실제로 인상 깊은 사례가 있었습니다. advocate가 야심차게 여러 wave로 쪼갠 공격적인 병렬 계획을 들고 왔는데, critic이 그 계획을 실제 저장소 상태와 하나하나 대조하자 곳곳에서 무너졌습니다. 계획서 위에서는 독립적으로 보였던 트랙들이, 실제로는 같은 파일과 같은 자원을 건드리고 있었기 때문입니다. 계획의 추상적인 단계 이름만 보면 보이지 않던 충돌이, 실제 git/디스크와 맞대고 나서야 드러났습니다.

병렬로 돌리면 안 되는 것들 — 직렬화 핫스팟

critic이 반복해서 짚어낸 충돌 지점들에는 공통점이 있었습니다. 저는 이것들을 직렬화 핫스팟이라고 부르기로 했습니다. 두 트랙이 아무리 독립적으로 보여도, 아래 자원 중 하나라도 동시에 건드리면 병렬은 곧 사고가 됩니다.

직렬화 핫스팟 동시에 건드리면 생기는 문제
단일 모듈 와이어링 파일 두 트랙이 같은 파일에 등록 코드를 넣어 마지막 쓰기가 앞 쓰기를 덮음
공유 설정 파일 동일 설정을 양쪽에서 수정해 한쪽 변경이 사라짐
ORM 마이그레이션 번호 두 트랙이 같은 다음 번호(예: 동일한 마이그레이션 번호)를 동시에 생성해 충돌
단일 로컬 DB 같은 데이터베이스에 동시 쓰기 — last-writer-wins로 한쪽 작업 유실

이 중에서도 ORM 마이그레이션 번호의 동시 생성이 특히 교묘했습니다. 두 트랙이 각자 자기 작업을 위해 마이그레이션을 새로 만드는데, 둘 다 "다음 번호"를 계산하는 순간 같은 번호를 집어 듭니다. 따로 보면 두 마이그레이션 모두 정당합니다. 다만 같은 번호를 달고 태어나는 순간 충돌이고, 이건 계획서 단계 이름만 봐서는 절대 예측할 수 없습니다. 단일 모듈 와이어링 파일도 마찬가지입니다. 새 기능을 등록하는 한 줄을 양쪽 트랙이 같은 파일에 넣으면, 나중에 머지하거나 저장하는 쪽이 앞쪽을 덮어버립니다.

그래서 병렬 후보를 종합할 때, 이 핫스팟을 건드리는 트랙은 같은 wave에 넣지 않고 순차로 강등했습니다. 독립적으로 보이는 것과 실제로 독립적인 것은 다르다는 것 — 적대적 검증이 가르쳐준 가장 실용적인 교훈이었습니다.

코드만이 아니라 물리 자원도 한계다

의존성과 충돌을 다 정리해도, 마지막에 부딪히는 벽이 하나 더 있었습니다. 개발 머신 그 자체입니다. 저는 단일 노트북에서 이 자동화를 돌립니다. 코드상으로는 트랙 네 개가 완벽하게 독립이어도, 그 넷을 동시에 빌드·테스트까지 돌리면 머신이 먼저 무릎을 꿇습니다.

실측해보니, 빌드와 테스트를 포함한 동시 작업은 두 개가 사실상의 상한이었습니다. 메모리와 CPU를 가장 많이 먹는 단계가 빌드·테스트인데, 단일 머신에서 이걸 셋 이상 겹치면 전체가 같이 느려지거나 스왑이 터지면서 오히려 순차보다 늦어집니다. 병렬화의 목적이 속도였는데, 자원 경합 때문에 도리어 느려지면 본말이 전도되는 셈입니다.

또 하나 간과하기 쉬운 비용이 워크트리 셋업 세금이었습니다. 트랙을 격리하려고 워크트리를 따로 만들면, 워크트리마다 자기 의존성 설치본을 들고 있어야 합니다. 의존성 설치본 하나가 가볍지 않다 보니(대략 1.7GB 수준이었습니다), 트랙을 늘릴 때마다 디스크와 설치 시간이 선형으로 불어납니다. 병렬 트랙의 수는 코드 독립성만이 아니라 이 물리적 세금까지 계산해서 정해야 했습니다.

그래서 판정 게이트에 라이브 자원 체크를 끼워 넣었습니다. 병렬로 돌려도 되는지 코드만 보고 결정하지 않고, 실행 직전에 메모리 압력·디스크 여유·현재 떠 있는 워크트리 수를 직접 확인합니다. 자원이 빠듯하면, 코드상 병렬이 가능해도 순차로 떨어집니다.

격리는 hard가 아니라 soft다

병렬 트랙을 격리하면서 또 한 가지 분명히 알게 된 것이 있습니다. 작업 디렉토리(cwd) 기반의 격리는 강제(hard)가 아니라 느슨한(soft) 격리라는 점입니다. 트랙마다 자기 cwd를 가진다고 해서, 도구가 그 경계를 물리적으로 지켜주지는 않습니다. 한 트랙의 에이전트가 마음만 먹으면 옆 트랙의 파일을 건드릴 수 있습니다.

그래서 격리가 정말 필요하면, 먼저 시블링 워크트리를 만들고 작업 디렉토리를 명시하는 방식으로 운영했습니다. 여기서 한 가지 함정을 미리 막아야 했습니다. 워크트리를 메인 저장소 하위에 만들면 안 된다는 것입니다. 메인 저장소 안에 워크트리를 두면, 검색·린트·인덱서 같은 도구의 재귀 스캔이 워크트리 내부까지 들어가 같은 파일을 중복으로 잡고, 일부 git 훅의 경로 해석도 꼬입니다.

# ❌ 메인 저장소 하위에 워크트리 — 재귀 스캔/훅 경로 문제
~/project/.worktrees/track-a

# ✅ 시블링(형제) 디렉토리로 분리
~/project           ← 메인
~/project-track-a   ← 워크트리(형제)
~/project-track-b   ← 워크트리(형제)

정리하면, 병렬 트랙의 격리는 "알아서 지켜지겠지"라고 믿을 수 있는 것이 아니라, 시블링 워크트리를 선제적으로 만들고 각 에이전트에게 작업 경로를 명시적으로 못 박아야 비로소 성립하는 운영의 규율이었습니다.

Phase 0.5 판정 게이트 — 의심스러우면 순차

지금까지의 모든 고민 — 적대적 검증, 직렬화 핫스팟, 물리 자원, soft 격리 — 을 한곳에 모은 것이 Phase 0.5 자동 판정 게이트입니다. 본 작업(Phase 1 이후)이 시작되기 전, 0.5라는 사이 단계에서 "이번 실행을 병렬로 돌려도 되는가"를 결정합니다.

게이트는 네 조건을 모두(AND) 만족할 때만 병렬을 승인합니다. 하나라도 어긋나면 순차입니다.

  • 트랙들이 서로 의존하지 않고, 적대적 검증에서 충돌이 살아남지 않았을 것
  • 어느 트랙도 직렬화 핫스팟(단일 와이어링 파일·공유 설정·ORM 마이그레이션 번호·단일 로컬 DB)을 동시에 건드리지 않을 것
  • 라이브 자원 체크(메모리 압력·디스크 여유·워크트리 수)가 동시 작업을 감당할 수 있을 것
  • 격리가 시블링 워크트리로 안전하게 확보 가능할 것

그리고 중요한 한 가지를 더 박았습니다. 매 실행마다 게이트가 자기 판단의 근거를 겉으로 드러내도록 한 것입니다. 실행할 때마다 "이번엔 병렬인가 순차인가, 그 근거가 무엇인가"를 담은 결정 블록을 출력하게 했습니다.

PARALLELIZATION DECISION
- 트랙: A(백엔드) / B(데스크탑 클라이언트)
- 의존성: 없음 (적대적 검증 통과)
- 직렬화 핫스팟: 없음
- 자원: 메모리 여유 충분 / 워크트리 2개 가능
→ 결정: 병렬 (2-wave)  ※ 한 조건이라도 불충족이었으면 순차

이 블록을 남긴 이유는, 자동화가 조용히 병렬로 돌다가 나중에 문제가 생겼을 때 왜 그렇게 판단했는지를 사후에 추적할 수 있어야 했기 때문입니다. 판단을 블랙박스로 두면, 잘못된 병렬 결정이 어디서 비롯됐는지 알 수 없습니다. 근거를 매번 노출하면, 게이트의 룰 자체를 데이터로 고쳐 나갈 수 있습니다.

정리하며

고급편을 쓰며 돌이켜 생각해보면, 병렬화에서 제가 배운 가장 큰 교훈은 기본편의 교훈과 닮아 있습니다. 기본편이 "컨텍스트는 비우는 것이 아니라 차지 않게 설계하는 것"이었다면, 고급편은 "병렬화는 밀어붙이는 것이 아니라 기회적으로, 보수적으로 취하는 것"입니다. 둘 다 야심보다 절제가 안정성을 만든다는 같은 이야기였습니다.

그 절제를 떠받친 것이 적대적 검증이었습니다. 자기 계획을 스스로 깎아내릴 동기가 약한 단일 결정자 대신, advocate의 장밋빛 계획에 critic을 붙여 실제 git·디스크 상태와 대조하게 하자, 계획서 위에서는 보이지 않던 직렬화 핫스팟이 드러났습니다. 코드 독립성만으로는 부족했고, 단일 머신의 물리 자원과 soft 격리의 한계까지 함께 계산해야 비로소 안전한 병렬이 됐습니다.

그래서 최종 형태는 화려한 병렬 엔진이 아니라, 기존 순차 오케스트레이터에 얹은 좁은 옵트인 모드와 네 조건 AND 게이트, 그리고 매번 근거를 노출하는 결정 블록이었습니다. 의심스러우면 순차로 떨어진다는 단순한 원칙 하나가, 결국 병렬화를 안전하게 만드는 가장 든든한 안전장치였다는 생각이 들었습니다.

Claude CodeAI 에이전트오케스트레이터병렬화멀티에이전트적대적 검증자동화

관련 글

컨텍스트 포화를 구조로 막기: Phase 격리 subagent와 파일 핸드오프

컨텍스트는 비울 수 없습니다. auto-compaction이 한 번 터지면 토큰의 97%가 사라집니다. 그렇다면 애초에 메인 세션이 차지 않게 만들면 됩니다. Phase별 격리 subagent와 파일 핸드오프로 컨텍스트 포화를 구조적으로 막은 회고입니다.

관련도 90%

사람 손 없이 PR→머지→배포: AI 에이전트 자동 연쇄를 설계하며 배운 것

코드 리뷰·머지 게이트·배포는 다 자동화했는데, 사슬의 진입점만 수동 호출로 남아 있었습니다. PR 생성 직후 자동 트리거, 보고가 아닌 GitHub를 직접 조회하는 머지 게이트, 배포 보류 게이팅까지 — 자동 연쇄를 직접 설계하며 겪은 시행착오를 정리했습니다.

관련도 90%

Claude Code 하네스: 리서치부터 글쓰기까지, 하나의 구조로 확장하기

Claude Code 하네스를 리서치, 디자인, 글쓰기, 분석, 데이터 파이프라인에 확장한 사례. 팬아웃, 파이프라인, 생성-검증 세 가지 아키텍처 패턴과 5개 실전 하네스를 공유합니다.

관련도 89%