모노레포 AGENTS.md 계층 구조 완전 정복 — 루트·서브디렉터리 분리로 AI 에이전트 정밀 제어하기
Hierarchical AGENTS.md in Monorepos: Controlling AI Agents with Precision
요즘 팀 안에 AI 코딩 에이전트를 들이고 나서 가장 먼저 맞닥뜨리는 문제가 있습니다. 에이전트가 "열심히" 하기는 하는데, 우리 팀 컨벤션을 모르거나 엉뚱한 패키지의 스타일을 그대로 따라하는 거죠. 저도 처음엔 루트에 AGENTS.md 하나 만들어 넣고 끝냈는데, 모노레포 규모가 커지면서 "이게 맞나?" 싶은 순간이 왔습니다. 결제 서비스에 프론트엔드 스타일 가이드가 섞여 들어오고, 80K 토큰짜리 아키텍처 문서를 에이전트가 매번 통째로 읽고 있더라고요.
AGENTS.md의 진짜 힘은 단일 파일이 아니라 계층적 구조에 있습니다. 루트 파일과 서브디렉터리 파일을 목적에 맞게 나누면, 에이전트는 자신이 작업하는 디렉터리에 딱 맞는 컨텍스트만 받아볼 수 있습니다. 읽고 나면 루트 AGENTS.md를 얼마나 작게 유지해야 하는지, 어떤 규칙은 서브디렉터리로 내려야 하는지, AGENTS.override.md를 언제 꺼내야 하는지 — 세 가지 판단을 즉시 내릴 수 있게 됩니다.
핵심 개념
계층적 탐색: 에이전트는 어떻게 파일을 읽는가
AI 에이전트가 특정 파일을 편집할 때, 단순히 루트의 AGENTS.md 하나만 읽는 게 아닙니다. 해당 파일이 있는 디렉터리에서 출발해 Git 루트까지 거슬러 올라가면서 경로상의 모든 AGENTS.md를 수집합니다. 그리고 루트에서 현재 디렉터리 방향으로 순서대로 이어 붙여 하나의 컨텍스트를 구성하죠.
핵심은 더 깊은 디렉터리의 파일이 나중에 붙기 때문에 우선순위가 높다는 점입니다. 덮어쓰기(override)가 아니라 추가(append) 방식이라 모든 파일의 내용이 합쳐지되, 에이전트가 규칙 간 충돌을 해석할 때 뒤쪽(더 깊은 디렉터리)의 내용을 더 강하게 인식합니다.
monorepo/
├── AGENTS.md ← 루트 (1번째로 읽힘, 나중에 덮어씌워질 수 있음)
├── apps/
│ └── web/
│ └── AGENTS.md ← 2번째로 읽힘 (마지막 반영, 높은 우선순위)
└── services/
└── payments/
└── AGENTS.override.md ← 루트 AGENTS.md를 무시하고 단독 적용예를 들어 에이전트가 apps/web/src/Button.tsx를 편집한다면, monorepo/AGENTS.md → monorepo/apps/web/AGENTS.md 순으로 읽어서 합칩니다. services/payments/와는 전혀 관계없는 컨텍스트죠.
이 자동 탐색 덕분에 개발자가 "이 파일도 읽어줘"라고 별도로 안내할 필요가 없습니다. 에이전트는 현재 작업 위치에 맞는 규칙을 알아서 로드합니다. 나중에 실전 예시에서 "포인터를 적어두는 게 무슨 의미가 있지?"라는 의문이 들 수 있는데, 그 답이 바로 여기 있습니다 — 포인터는 에이전트가 찾아가도록 지시하는 게 아니라, 계층 탐색으로 자동 로드될 파일에 맥락 안내를 더해주는 역할입니다.
AGENTS.override.md — 이 파일이 존재하는 디렉터리에서는 상위 경로의 AGENTS.md 전체를 상속하지 않고 해당 파일만 단독으로 적용됩니다. 보안 규정이나 완전히 다른 기술 스택을 가진 서브시스템에서 유용합니다.
탐색 우선순위와 32 KiB 제한
OpenAI Codex 기준으로, 에이전트는 각 디렉터리에서 AGENTS.override.md를 먼저 찾고, 없으면 AGENTS.md를 찾습니다. 디렉터리당 하나의 파일만 포함되고, 루트에서부터 읽어 내려오다가 경로상 모든 파일의 합산 크기가 32 KiB를 초과하는 시점에서 탐색을 중단합니다. 그 이후의 서브디렉터리 파일들은 통째로 로드되지 않습니다.
저도 처음엔 "32KB면 꽤 넉넉하지 않나?" 싶었는데, 팀 컨벤션 문서, 아키텍처 개요, 라이브러리 목록을 다 루트에 때려 넣다 보면 생각보다 금방 찹니다. 루트가 비대해지면 정작 중요한 서브디렉터리 규칙이 에이전트에게 전달되지 않는 역설적인 상황이 생깁니다.
컨텍스트 창(Context Window) — LLM이 한 번에 처리할 수 있는 텍스트의 최대 길이입니다. AGENTS.md 내용도 이 창을 차지하므로, 불필요한 내용은 에이전트의 실제 코드 작업 공간을 줄입니다.
실전 적용
예시 1: 루트를 포인터 맵으로만 사용하기
루트 AGENTS.md에 모든 걸 담는 대신, "어떤 작업을 할 때 어느 파일을 참고하면 되는지"만 안내하는 지도 역할을 맡기는 방식입니다. Datadog 프론트엔드 팀이 실제로 채택한 전략이기도 한데, 그들은 루트를 "에이전트가 자기 담당 구역을 파악하는 지도"로만 쓰고 실제 규칙은 각 서브디렉터리에 위임합니다. 이메일 컴포넌트, Go 서비스, 공통 유틸리티 등 관심사가 뚜렷하게 분리된 모노레포 구조에서 이 패턴이 특히 빛납니다.
솔직히 이 패턴을 처음 봤을 때 "이렇게 단순해도 되나?" 싶었는데, 실제로 컨텍스트 효율이 눈에 띄게 좋아집니다.
<!-- monorepo/AGENTS.md -->
# 프로젝트 가이드
## 공통 규칙
- 커밋 메시지: imperative mood (한글 가능)
- PR 전 반드시 `pnpm test` 실행
- 의존성 추가 시 `pnpm add` 사용
## 서브시스템별 상세 가이드
- 웹 앱 작업 시 → `apps/web/AGENTS.md` 참조
- 결제 서비스 작업 시 → `services/payments/AGENTS.md` 참조
- UI 컴포넌트 작업 시 → `packages/ui/AGENTS.md` 참조
- 인증 관련 작업 시 → `packages/auth/AGENTS.md` 참조| 구성 요소 | 역할 |
|---|---|
| 공통 규칙 섹션 | 모든 팀원·에이전트가 공유하는 최소한의 컨벤션 |
| 서브시스템 포인터 | 계층적 탐색으로 자동 로드되는 파일의 경로 안내 |
| 루트 파일 크기 | 가능한 한 작게 유지 (1~2KB 권장) |
예시 2: 서브디렉터리별 전용 규칙 파일
각 패키지나 앱 디렉터리에 해당 팀의 규칙을 집중시키는 방식입니다. Mercari 엔지니어링 팀이 Cursor와 Claude의 개별 룰 파일들을 단일 AGENTS.md로 통합하면서 채택한 구조이기도 합니다. 루트는 아키텍처 개요와 링크 맵 역할만 하고, 실제 내용은 각 서브디렉터리에 위임하죠.
<!-- apps/web/AGENTS.md -->
# 웹 앱 개발 가이드
## 아키텍처
- Server Component 기본, Client Component는 `'use client'` 명시 필요
- 데이터 페칭은 Server Action 또는 Server Component에서 처리
- 상태 관리: Zustand (전역), React Query (서버 상태)
## 컴포넌트 컨벤션
- 파일명: PascalCase (예: UserProfile.tsx)
- Props 타입: 파일 내 상단에 interface로 선언
- 스타일: Tailwind CSS, 커스텀 CSS 금지
## 테스트
- 컴포넌트 테스트: @testing-library/react
- E2E: Playwright (`pnpm test:e2e`)
- 스냅샷 테스트 금지 (유지보수 부담)<!-- services/payments/AGENTS.md -->
# 결제 서비스 가이드
## 보안 규칙
- 카드 정보는 절대 로그에 남기지 않음
- PCI-DSS 규정상 원시 카드 데이터 처리 금지
- 모든 금액 계산은 정수(원 단위)로 처리
## 기술 스택
- Node.js + Express (Next.js 아님)
- 테스트: Jest + supertest (통합 테스트 필수)
- DB: PostgreSQL, ORM 없이 raw SQL 사용이렇게 구성하면 에이전트가 apps/web을 작업할 때는 웹 규칙만, services/payments를 작업할 때는 결제 규칙만 받아봅니다. 결제 서비스의 "카드 정보는 절대 로그에 남기지 않음" 같은 보안 규칙이 프론트엔드 에이전트에게 노출될 이유가 없고, 반대로 Tailwind 컨벤션이 결제 서비스에 끼어들 일도 없습니다.
예시 3: AGENTS.override.md로 규칙 완전 교체하기
레거시 코드베이스나 완전히 다른 기술 스택을 쓰는 서비스가 모노레포 안에 공존할 때 유용합니다. 저도 이런 상황을 실무에서 자주 맞닥뜨리는데 — 팀 전체가 TypeScript로 전환했는데 레거시 Java 서비스 하나가 여전히 살아있는 경우죠. 이럴 때 루트 규칙을 상속받으면 오히려 혼란이 커집니다.
<!-- services/legacy-billing/AGENTS.override.md -->
# 레거시 빌링 서비스 (독립 규칙)
⚠️ 이 서비스는 루트 AGENTS.md 규칙을 따르지 않습니다.
## 기술 환경
- Java 11, Spring Boot 2.x (TypeScript/Node 아님)
- Maven 빌드 (`mvn clean install`)
- 테스트: JUnit 5
## 수정 원칙
- 이 서비스는 동결(freeze) 상태입니다
- 버그 수정 외 기능 추가 금지
- 변경 시 반드시 기존 테스트 통과 확인루트에 TypeScript 규칙이 있어도, 이 디렉터리 안에서 작업하는 에이전트는 위 파일만 읽습니다. 덕분에 "레거시 서비스인데 왜 TypeScript로 마이그레이션하려 하지?"라는 이상한 작업 시도를 사전에 차단할 수 있습니다.
장단점 분석
장점
계층적 구조의 가장 큰 장점 두 가지를 먼저 짚어봅니다.
첫째는 범위 격리입니다. 팀마다 서로 다른 컨벤션을 가져도 하나의 레포에서 충돌 없이 관리할 수 있고, 보안 민감한 규칙이 엉뚱한 에이전트에게 노출되지 않습니다.
둘째는 컨텍스트 효율입니다. 루트를 포인터 맵으로만 쓰면 에이전트가 현재 작업과 무관한 규칙을 읽느라 컨텍스트 창을 낭비하지 않습니다. 이건 단순히 "더 빠르다"의 문제가 아니라 에이전트의 실제 판단 품질과도 직결됩니다.
| 항목 | 내용 |
|---|---|
| 도구 중립성 | Codex, Copilot, Cursor, Windsurf, Devin 등 20개 이상의 AI 도구가 동일한 AGENTS.md를 인식합니다 |
| 범위 격리 | 결제팀 규칙이 프론트엔드 에이전트에게 노출되지 않습니다 |
| 컨텍스트 효율 | 루트를 포인터 맵으로만 쓰면 에이전트가 필요한 내용만 로드합니다 |
| 팀 정렬 | 모든 에이전트와 엔지니어가 동일한 컨벤션 문서를 기준으로 작업합니다 |
| 온보딩 단축 | AI 에이전트 세션 준비 시간이 수십 분에서 2분 미만으로 줄었다는 사례가 있습니다 |
단점 및 주의사항
솔직히 가장 많이 보는 문제는 두 가지입니다. 루트 비대화와 문서 부패죠.
루트 비대화는 처음 AGENTS.md를 도입할 때 "일단 다 넣고 보자"는 심리에서 시작됩니다. 아키텍처 문서, 전체 팀 컨벤션, 온보딩 가이드까지 루트에 때려 넣으면 32 KiB 제한에 걸려서 정작 중요한 서브디렉터리 파일들이 로드되지 않는 상황이 생깁니다.
문서 부패는 시간이 지나면서 코드는 바뀌는데 AGENTS.md가 그 자리에 머물러 있을 때 발생합니다. 에이전트는 "삭제된 모듈을 import하려는" 이상한 작업을 반복하게 됩니다. 저도 처음엔 몰랐는데, 코드 리뷰할 때 관련 AGENTS.md를 함께 보는 습관이 생기고 나서야 많이 줄었습니다.
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 컨텍스트 낭비 | 루트에 방대한 문서를 담으면 에이전트가 무관한 80K 토큰을 매번 소비합니다 | 루트는 포인터 역할만, 상세 내용은 서브디렉터리로 분리 |
| 문서 부패 | 코드와 AGENTS.md가 불일치하면 에이전트가 잘못된 패턴을 반복합니다 | 코드 리뷰 시 관련 AGENTS.md도 함께 검토 |
| 32 KiB 제한 | 루트부터 읽어 내려오다 한도 초과 시 이후 파일이 통째로 로드되지 않습니다 | 각 파일을 최대한 간결하게 유지 |
| 도구별 동작 차이 | Claude Code는 CLAUDE.md를, Gemini CLI는 GEMINI.md를 우선합니다 | 멀티 에이전트 환경이라면 도구별 파일을 병용하는 패턴 고려 |
| LLM 자동 생성의 역효과 | arXiv 연구에 따르면 LLM이 생성한 AGENTS.md는 성공률을 −3% 낮추고 비용을 20% 이상 증가시켰습니다 | 반드시 개발자가 직접 작성 |
LLM이 생성한 AGENTS.md를 그대로 쓰면 안 되는 이유 — LLM은 코드베이스를 "본 적" 없이 일반적인 패턴을 기술하는 경향이 있습니다. 실제 프로젝트 특수성과 맞지 않는 내용이 들어가면, 에이전트는 그걸 사실로 믿고 잘못된 방향으로 반복 작업합니다. 짧더라도 개발자가 직접 쓴 파일이 훨씬 효과적입니다.
실무에서 가장 흔한 실수
- 루트 AGENTS.md에 모든 아키텍처 문서를 때려 넣기 — 모든 작업에서 전체 컨텍스트가 로드되어 32 KiB 제한에 걸리거나, 에이전트의 실제 작업 공간이 줄어듭니다.
- AGENTS.md를 한 번 쓰고 업데이트하지 않기 — 리팩터링 후 컨벤션이 바뀌었는데 파일이 그대로면, 에이전트는 삭제된 패턴을 복원하거나 존재하지 않는 모듈을 import하려 합니다.
- LLM에게 AGENTS.md 초안 생성을 맡기기 — "이 프로젝트 코드 읽고 AGENTS.md 만들어줘"는 편리해 보이지만, 실증 연구 결과 성능이 오히려 나빠졌습니다. 짧더라도 개발자가 직접 쓴 파일이 훨씬 효과적입니다.
마치며
루트는 가볍게, 규칙은 해당 디렉터리에, 예외는 override로 — 이 세 가지 원칙만 지켜도 모노레포의 AI 에이전트 제어 품질이 눈에 띄게 달라집니다.
지금 바로 시작해볼 수 있는 3단계:
- 현재 루트 AGENTS.md의 크기를 확인해봅니다.
wc -c AGENTS.md로 바이트 수를 체크해보고, 2KB가 넘는다면 어떤 내용이 실제로 모든 작업에 필요한지 다시 생각해볼 여지가 있습니다. - 가장 자주 작업하는 서브디렉터리 하나를 골라 전용 AGENTS.md를 만들어봅니다. 그 디렉터리에서만 적용되는 프레임워크, 테스트 도구, 금지 패턴 등을 3~5개 항목으로 직접 작성해보면 됩니다.
- 루트 AGENTS.md에 포인터 섹션을 추가해봅니다.
## 서브시스템 가이드섹션을 만들고 방금 만든 서브디렉터리 파일 경로를 안내하는 한 줄을 추가하면, 계층적 구조의 첫 번째 단계가 완성됩니다.
다음 글: AGENTS.md를 팀 전체가 일관되게 유지하는 법 — "코드는 바뀌었는데 AGENTS.md는 3개월 전 그대로"인 상황을 코드 리뷰 프로세스에서 어떻게 잡아내는지, 문서 동기화를 팀 워크플로에 자연스럽게 녹이는 실전 방법을 풀어봅니다.
참고 자료
- Custom instructions with AGENTS.md – OpenAI Codex 공식 문서
- AGENTS.md 공식 사이트
- GitHub – agentsmd/agents.md
- Steering AI Agents in Monorepos with AGENTS.md – DEV Community (Datadog)
- Taming Agents in the Mercari Web Monorepo – Mercari Engineering
- A good AGENTS.md is a model upgrade – Augment Code Blog
- How to Build Your AGENTS.md (2026) – Augment Code
- Evaluating AGENTS.md: Are Repository-Level Context Files Helpful? – arXiv 2602.11988
- On the Impact of AGENTS.md Files on the Efficiency of AI Coding Agents – arXiv 2601.20404
- AGENTS.md customization files – GitLab Docs
- CLAUDE.md, AGENTS.md & Copilot Instructions: Configure Every AI Coding Assistant – DeployHQ
- Will AI turn 2026 into the year of the monorepo? – Spectro Cloud