AI Agent 환경 구성 (모노레포 기준)
모노레포 환경에서의 claude code 최적화
category
type
Post
slug
monorepo-ai-agent-architecture
summary
모노레포 환경에서의 claude code 최적화
date
May 1, 2026
updatedAt
May 1, 2026
tags
cluade code
monorepo
thumbnail
Claude Code 같은 AI 에이전트를 모노레포에 붙여서 쓰기 시작하면서, 초반엔 매번 프롬프트에 맥락을 길게 설명해야 했다. "이 프로젝트는 이런 구조고, 이 컨벤션을 따르고, 이 부분은 건드리면 안 되고..." 같은 설명을 세션마다 반복하다 보니 두 가지 문제가 눈에 띄었다.
하나는 일관성이었다. 같은 질문을 해도 그날그날 프롬프트에 뭘 얼마나 자세히 적었느냐에 따라 에이전트가 다른 방식으로 작업했다. 다른 하나는 규모였다. 앱과 패키지가 여러 개인 모노레포에서는 "지금 작업 중인 이 패키지"에만 해당하는 규칙과 "저장소 전체"에 해당하는 규칙이 섞여 있는데, 이걸 매번 사람이 골라서 설명해주는 건 지속 가능한 방식이 아니었다.
그래서 생각한 방향은 하나였다. 사람이 매번 설명하는 대신, "이 코드베이스를 어떻게 다뤄야 하는지"에 대한 지식을 코드 옆에 파일로 고정해두고, 에이전트가 작업을 시작할 때 스스로 그걸 읽게 만드는 것. 그렇게 나온 게
brain 폴더 구조다.brain 폴더가 하는 일
brain은 "이 코드베이스(또는 앱/패키지)를 어떻게 다룰지"를 사람과 에이전트가 공유하는 지식 저장소다. 규칙·워크플로·브랜드·목표 같은 맥락을 코드 옆에 묶어두면, 에이전트가 매번 전체 코드를 추측하지 않고 의도된 방식으로 작업하게 만들 수 있다.위치는 스코프별로 나눠서 배치한다. 저장소 전체에 적용되는 건 루트에, 특정 앱에만 해당하는 건
apps/<앱명>/brain에, 특정 패키지에만 해당하는 건 packages/<패키지명>/brain에 둔다. 그 안에서 다시 레이어로 나누는데, 이 레이어 설계가 이 구조에서 가장 신경 쓴 부분이다.레이어를 왜 P0~P5로 나눴나
처음엔 그냥 "규칙 폴더" 하나에 다 몰아넣을까 고민했다. 근데 그렇게 하면 에이전트 입장에서 "반드시 지켜야 하는 것"과 "참고하면 좋은 것"을 구분할 방법이 없었다. 보안 규칙이랑 디자인 토큰이랑 같은 무게로 취급되면, 에이전트가 둘 중 뭘 더 강하게 지켜야 하는지 판단할 근거가 없어진다.
그래서 "얼마나 자주 바뀌는가"와 "얼마나 강제적인가"를 기준으로 숫자를 매기기로 했다. 숫자가 낮을수록 불변에 가깝고, 높을수록 자주 바뀐다. 에이전트는 낮은 숫자(P0, P1)를 먼저, 강하게 준수하도록 설계했다.
레이어 | 기능 | 폴더명 | 설명 |
P0 | 생존·보안·불변 | p0-trust-first | 변하지 않는 절대 규칙·제약·보안 |
P1 | 자동화·반복 | p1-automation-rhythm | CI/CD, 포맷, 반복 빌드·배포 |
P2 | 기억·상태 | p2-state-memory | 전역 상태·캐시·세션 등 데이터 흐름 명세 |
P3 | 위험 감지·방어 | p3-risk-detect | 에러 처리, 테스트 가드레일, 런타임 예외 |
P4 | 실행·작업 | p4-workflow | UI·비즈니스 로직 실행 순서, 실무 SKILL |
P5 | 스타일·페르소나 | p5-identity | 브랜드 가치, 톤앤매너, 디자인 토큰 |
P0(보안·불변 규칙)와 P5(브랜드·톤)를 양 끝에 둔 것도 의도한 배치다. 하나는 "절대 어기면 안 되는 것", 하나는 "가장 자주 바뀌지만 그만큼 덜 치명적인 것"이라서, 숫자만 보고도 에이전트가 우선순위를 판단할 수 있게 하고 싶었다.
디렉토리 구조 — 필요한 레이어만 만든다
루트에 전체 명세(
CLAUDE.md)를 두고, 상세는 brain 하위 레이어로 나누는 구조로 정리했다. 처음에는 여섯 레이어를 전부 미리 만들어두는 것도 고려했는데, 실제로 써보니 모든 앱·패키지가 여섯 레이어를 다 필요로 하지는 않았다. 그래서 원칙을 하나 세웠다. 모든 레이어를 전부 만들지 않는다, 필요한 레이어만 둔다./ ├── CLAUDE.md # 전체 규약 (P0~P6 안내) ├── brain/ │ ├── p0-trust-first/ # [고정] 불변 규칙 │ ├── p1-automation-rhythm/ # [반복] 자동화·CI │ ├── p4-risk-detect/ # [보안] 보안·테스트 관련 명세 │ └── p5-identity/ # [개성] AI 톤 │ ├── apps/ │ └── <앱명>/ │ └── brain/ │ ├── p2-state-memory/ # 이 앱의 상태 트리·데이터 흐름 │ └── p4-workflow/ # 이 앱의 SKILL (컴포넌트·로직 작성법) │ └── packages/ └── <패키지명>/ └── brain/ └── p4-workflow/ # 패키지 전용 규약·SKILL
이렇게 하면 새 패키지를 만들 때 "이 패키지는 상태 관리가 단순하니 p2는 생략하고 p4만 두자"는 식의 판단이 가능해진다. 구조를 억지로 채우는 대신, 실제로 그 스코프에서 의미 있는 레이어만 만드는 쪽을 택했다.
에이전트가 읽는 순서도 정해뒀다
레이어를 나눠놔도 에이전트가 어떤 순서로 읽는지가 정해져 있지 않으면 결국 또 매번 다르게 동작한다. 그래서 읽는 순서 자체도 규약으로 못박았다.
- 루트
CLAUDE.md로 전체 명세를 파악한다.
- 루트 P0, P5를 먼저 준수한다. (불변 규칙·브랜드)
- 실제 작업 중인
apps/...또는packages/...의brain에서 P4, P5(필요 시 P2)를 읽고 그 스코프에 집중한다.
정리하면, 에이전트는 작업을 시작할 때 루트의 P0, P5를 준수하고, 현재 작업 폴더의 P4, P6(및 필요한 brain)에 집중하게 된다. "전역 불변 규칙 먼저, 그다음 지금 손대는 범위의 실무 규칙"이라는 순서를 강제한 셈이다.
SKILL.md는 왜 100줄 제한을 뒀나
레이어 구조를 만들고 나서 다음 문제는 "그 안에 뭘 얼마나 자세히 적을 것인가"였다. 처음엔 한 SKILL.md에 관련 내용을 다 몰아 적었는데, 파일이 길어질수록 에이전트가 그걸 규약이 아니라 그냥 참고 자료 취급하면서 준수율이 떨어지는 게 체감됐다. 파일이 너무 길면 핵심 규칙이 부가 설명 사이에 묻혀버리는 것 같았다.
그래서 다음 원칙을 세웠다.
- 한 파일은 최대 약 100줄까지 권장한다. 초과 시 에이전트가 규약이 아닌 참고 자료로만 취급해 준수율이 낮아진다.
- 100줄을 넘기는 상세 내용은 같은 주제 폴더에
<설명한내용>.md등으로 분리한다.
- 단일
SKILL.md가 100줄을 넘을 것 같으면 기능 단위로 폴더를 나눠 SKILL을 분리한다.
이렇게 나누면
SKILL.md는 짧은 문서 맵(퀵 레퍼런스) 역할만 하고, 세부 규칙은 필요할 때만 펼쳐보는 참고 문서로 빠진다./ ├── CLAUDE.md └── brain/ └── p0-trust-first/ └── fsd/ ├── SKILL.md ← 문서 맵 (퀵 레퍼런스) ├── project-structure.md ├── layer-rules.md ├── cross-import-x.md ├── segments.md ├── tanstack-query.md └── api-requests.md packages/uikit/brain/p4-workflow/ ├── button/ │ └── SKILL.md └── input/ └── SKILL.md
실제 서비스에 적용한 형태
Project A 기준으로는
app/<서비스명>/.claude > rules > brain 경로를 루트와 동일한 구조로 맞췄고, CLAUDE.md에는 이 스킬이 해당 서비스 전용이라는 것과, 공용 규칙(@CLAUDE.md)을 함께 준수한다는 점, 그리고 상태·작업 관련 brain 경로를 명시했다.# Project A 워크스페이스 이 스킬은 `instiz` 전용 스킬이다. 공용 규칙인 @CLAUDE.md 파일을 준수하며 응답한다. ## 상태 @rules/brain/p2-state-memory ## 작업 @rules/brain/p4-workflow 프롬프트 실행 결과에 대해서는 @rules/brain/p4-workflow/ 하위에 결과 파일을 md 파일 형식으로 작성한다. @rules/brain/p4-workflow/SKILL.md만 읽고, @rules/brain/p4-workflow/*.md 파일의 경우 별도의 지시가 있지 않는한 참조하지 않는다.
여기서 실제로 도움이 됐던 규칙 하나는 "
SKILL.md만 기본으로 읽고, 나머지 .md는 별도 지시가 있을 때만 참조한다"는 부분이다. 세부 문서를 아무리 잘 분리해둬도 에이전트가 매번 전부 펼쳐 읽으면 결국 다시 파일 하나에 다 적어둔 것과 비슷한 상태가 된다. 진입점을 하나로 고정해두는 게 레이어를 나눈 것 못지않게 중요했다.결과 기록도 같은 구조 안에 남긴다
또 하나 정한 규칙은
.gitignore 처리였다. SKILL.md는 공유해야 하지만, 세부 참고 문서나 에이전트가 작업 중 남기는 결과 파일까지 전부 버전 관리에 올리고 싶지는 않았다.# .claude/rules/brain 아래 SKILL.md 외 .md 파일 무시 .claude/rules/brain/**/*.md !.claude/rules/brain/**/SKILL.md
메인이 되는
SKILL.md는 공유하고, Claude 실행 응답에 대한 결과는 동일 경로에 저장해두는 방식으로 정리했다. 추후 같은 작업이 다시 필요할 때 해당 결과 파일을 참조하게 하려는 목적이다. 규칙과 실행 로그를 같은 위치에 두면, 다음에 비슷한 작업을 시킬 때 "이 패턴은 전에 어떻게 풀었는지"를 에이전트 스스로 찾아볼 수 있다.이 구조를 만들면서 확인한 건, 에이전트에게 맥락을 매번 프롬프트로 설명하는 방식은 애초에 확장이 안 된다는 것이었다. 사람이 매번 설명하는 대신 코드 옆에 지식을 고정해두고, 그 지식을 "얼마나 강제적인가"에 따라 레이어로 나누고, 읽는 순서까지 규약으로 못박아야 비로소 에이전트가 세션마다 일관되게 움직였다.
숫자로 강제성을 표현한 것(P0~P5), 파일 길이를 강제로 제한한 것(100줄), 진입점을 하나로 고정한 것(
SKILL.md만 기본으로 읽기) — 세 가지 다 같은 문제의식에서 나왔다. 규칙이 아무리 잘 정리돼 있어도, 에이전트가 그중 뭘 우선하고 뭘 건너뛸지 판단할 수 없으면 결국 사람이 매번 다시 설명해야 한다는 것. 이 구조는 아직도 새 앱·패키지가 생길 때마다 조금씩 다듬고 있다.