문서 빌드를 위해 전체 Repos 복제를 중단하세요
Source: Dev.to
번역을 진행하려면 전체 텍스트(코드 블록 및 URL 제외)를 제공해 주시겠어요? 텍스트를 받는 대로 원본 형식과 마크다운을 유지하면서 한국어로 번역해 드리겠습니다.
아무도 이야기하지 않는 문제
우리 팀은 수십 개의 대형 저장소에서 콘텐츠를 끌어오는 문서 포털을 운영합니다. 각 문서 빌드에는 수십만 개의 파일을 포함한 저장소에서 몇 개의 markdown 파일과 이미지가 필요합니다. 순진한 접근법인 git clone은 매우 느리고 비효율적입니다.
| 접근 방식 | 문제 |
|---|---|
| 전체 복제 | 50개의 파일만 필요한 빌드에도 다운로드에 수분이 걸림 |
| API 파일 다운로드 | 몇 백 개 파일을 다운로드한 뒤 속도 제한에 걸림 |
| Sparse checkout | 여전히 git‑히스토리 협상이 필요하고 API‑기반 파이프라인에 도움이 되지 않음 |
아이러니: 매니페스트가 이미 정확히 필요한 파일을 선언하고 있습니다. docfx.json(또는 정적 사이트 생성기가 사용하는 설정) 은 모든 콘텐츠 glob 및 리소스 패턴을 나열합니다. 우리는 그 정보를 충분히 일찍 사용하지 않았을 뿐입니다.
왜 이것이 지금 더 중요한가: AI 에이전트
제품에 대한 질문에 답하고, 개발자를 온보딩하거나 내부 프로세스를 지원하는 AI 에이전트를 만들고 있다면 — 코드나 테스트가 아니라 문서에 대한 접근이 필요합니다.
문제는 빠르게 규모가 커집니다:
- RAG 파이프라인은 수십 개의 레포에서 문서를 수집해야 합니다 — 모든 레포를 복제하는 것은 터무니없습니다.
- 증분 인덱싱은 파일이 문서인지 코드인지 알아야 합니다 — 매니페스트가 이미 알려줍니다.
- 멀티‑repo 지식 베이스는 여러 레포에서 콘텐츠 파일만 빠르고 선택적으로 가져오는 방법이 필요합니다.
문서를 추출하는 속도와 정확도가 높을수록 에이전트의 지식은 더 최신하고 정확해집니다. 선택적 fetch 문제를 해결하면 빠른 빌드 와 신뢰할 수 있는 AI‑기반 문서 경험을 모두 얻을 수 있습니다.
아이디어: 가져오기 전에 해결하기
순서를 바꾼다면 어떨까요?
clone everything → build → throw away 99 % of the files
다음과 같이 바뀝니다
get the file listing → match against manifest → fetch only what matches
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Git Provider │────▶│ selective‑repo‑fetch │────▶│ Doc Pipeline │
│ (file listing) │ │ (manifest matching │ │ (build only │
│ │ │ + reference filter)│ │ matched files) │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
GitHub, Azure DevOps, 또는 GitLab에서 제공하는 파일‑트리 목록은 단일 저비용 API 호출이며, 파일 내용이 아니라 메타데이터를 반환합니다. 해당 목록을 매니페스트 패턴과 매칭하면 정확히 어떤 파일을 가져와야 하는지 알 수 있습니다.
selective‑repo‑fetch 소개
우리는 이 로직을 TypeScript 라이브러리로 오픈소스했습니다: selective‑repo‑fetch. MIT 라이선스를 사용하며 공급자에 구애받지 않습니다.
npm install github:microsoft/selective-repo-fetch
핵심 워크플로우
import {
resolveFileMatches,
filterReferencedResources,
} from 'selective-repo-fetch';
// Your manifest declares what your doc site needs
const manifest = {
build: {
content: [{ files: ['**/*.md'], src: 'docs' }],
resource: [{ files: ['**/*.{png,jpg,svg}'], src: 'docs/images' }],
},
};
// 1️⃣ Get file listing from any git API (one cheap metadata call)
const repoFiles = await getTreeListing(); // → [{ path: '/docs/intro.md' }, …]
// 2️⃣ Resolve manifest → content + resource matches
const matched = resolveFileMatches(repoFiles, manifest, '/', '/docfx.json');
// matched.contentMatches → 빌드에 필요한 마크다운 파일만
// matched.resourceMatches → 리소스 glob에 일치하는 이미지/비디오만
200 k개의 파일 중에서 중요한 50개만 남기기 — 함수 호출 하나로.
더 나아가기: 참조되지 않은 리소스 필터링
Glob 매칭은 너무 관대할 수 있습니다. **/*.png 패턴은 해당 폴더 아래의 모든 이미지를 매칭하는데, 이는 어떤 마크다운 파일에서도 참조되지 않은 경우도 포함됩니다. 규모가 큰 레포에서는 이러한 참조되지 않은 이미지가 수 메가바이트에 달하는 불필요한 다운로드를 초래할 수 있습니다.
두 단계 접근법
// 3️⃣ 콘텐츠 파일을 가져옵니다 (작은 텍스트 — 빠르고 비용이 저렴함)
const contentFileTexts: Record<string, string> = {};
for (const filePath of matched.contentMatches) {
contentFileTexts[filePath] = await fetchFileContent(filePath);
}
// 4️⃣ 실제로 참조된 리소스만 필터링합니다
const referencedResources = filterReferencedResources(
matched.resourceMatches,
contentFileTexts,
);
// 마크다운/HTML에서 <img src="…">, [text](…) 를 스캔하고,
// 어떤 콘텐츠 파일에서도 참조되지 않은 리소스는 모두 제외합니다.
전체 파이프라인 (GitHub 예시)
import { Octokit } from '@octokit/rest';
import {
resolveFileMatches,
filterReferencedResources,
} from 'selective-repo-fetch';
const octokit = new Octokit({ auth: token });
// 1️⃣ One API call to get the full file tree (metadata only, no content)
const { data } = await octokit.git.getTree({
owner,
repo,
tree_sha: 'HEAD',
recursive: 'true',
});
const files = data.tree
.filter(item => item.type === 'blob')
.map(item => ({ path: '/' + item.path }));
// 2️⃣ Resolve manifest patterns
const manifest = JSON.parse(/* your docfx.json */);
const matched = resolveFileMatches(files, manifest, '/', '/docfx.json');
// 3️⃣ Fetch only the needed content files
const contentFileTexts: Record<string, string> = {};
for (const path of matched.contentMatches) {
const { data: blob } = await octokit.git.getBlob({
owner,
repo,
file_sha: /* SHA for `path` */,
});
contentFileTexts[path] = Buffer.from(blob.content, 'base64').toString('utf8');
}
// 4️⃣ Filter resources to those actually referenced
const referencedResources = filterReferencedResources(
matched.resourceMatches,
contentFileTexts,
);
// 5️⃣ Download the final set of files (content + referenced resources)
// … feed them to your documentation build pipeline …
TL;DR
- 파일 트리를 한 번만 가져오기 (메타데이터만).
- 매니페스트와 매칭하여 정확히 필요한 파일을 파악합니다.
- 선택적으로 참조되지 않은 리소스 정리를 가져온 콘텐츠를 스캔하여 수행합니다.
- 최소한의 파일만 다운로드하고 이를 문서 빌드 또는 AI‑인제션 파이프라인에 전달합니다.
이렇게 하면 네트워크 트래픽이 감소하고 CI 속도가 빨라지며, AI 에이전트가 가장 최신이고 관련성 높은 문서를 지속적으로 제공받을 수 있습니다. 🚀
매니페스트 기반 선택적 리포지터리 가져오기
// 1. Get a tree of all files in the repo
const files = await getTreeListing(owner, repo);
// 2. Resolve matches against the manifest
const matched = resolveFileMatches(
files,
manifest,
'/', // repo root
'/docfx.json' // manifest location
);
// 3. Fetch content files (small text)
const contentTexts: Record<string, string> = {};
for (const path of matched.contentMatches) {
const { data } = await octokit.repos.getContent({
owner,
repo,
path: path.slice(1) // strip leading slash
});
contentTexts[path] = Buffer.from(data.content, 'base64').toString();
}
// 4. Filter resources to only referenced ones
const resources = filterReferencedResources(
matched.resourceMatches,
contentTexts
);
// 5. Fetch only referenced resources
// You now have the exact list — nothing wasted
처리 내용
- Glob 패턴 (중괄호 확장 포함) (
*.{md,yml}) src경로 해석은 매니페스트 위치를 기준으로- 섹션별 제외 (
exclude: ["**/draft/**"]) - 템플릿, 메타데이터 파일,
.order파일 — 자동 포함 - 외부 참조
src: "../other‑folder"를 통한 — 가져오기 전에 발견
참조 필터는 다음을 처리합니다:
- Markdown 이미지 및 링크:
,[text](path) - HTML 속성:
<img src="…">,<a href="…">,<source src="…">
추가 세부 사항:
- 경로 정규화는
~/, 앞선/, 쿼리 문자열 및 앵커를 제거합니다 - 외부 URL, data URI,
mailto:,javascript:스킴을 건너뜁니다 - 대소문자를 구분하지 않는 파일명 매칭
언제 사용할까
- 문서 포털이 여러 저장소에서 가져올 때 — 복제하기 전에 해결
- 모노레포 문서 빌드 — 매니페스트가 중요한 것을 알고 있으니 사용하세요
- CI/CD 파이프라인 — 변경된 부분만 가져와 빌드 시간을 단축
- 모든 정적 사이트 생성기 (DocFX, MkDocs, Sphinx, Docusaurus) — 매니페스트를 사용하는 경우
Source: …
왜 이것이 AI 에이전트에게 중요한가
우리가 처음 이 시스템을 만들 때 예상하지 못한 하위 효과가 있습니다: 문서를 효율적으로 AI 에이전트에게 제공하는 것.
제품에 대한 질문에 답하거나, 개발자를 온보딩하거나, 내부 프로세스를 지원하는 에이전트를 구축하고 있다면 — 에이전트는 전체 코드베이스가 아니라 문서에 접근할 수 있어야 합니다. 매니페스트 기반 접근 방식은 바로 그 분리를 제공합니다:
- 선택적 수집 — 코드, 테스트, CI 설정이 아니라 문서만 RAG 파이프라인에 끌어옵니다
- 점진적 업데이트 — 문서가 변경되면 매니페스트가 이를 명시하므로 코드 파일이 아니라 문서임을 알 수 있습니다
- 다중 레포지토리 지식 베이스 — 레포를 클론하지 않고도 50개 이상의 레포에서 문서를 끌어올 수 있습니다
// Feed docs from multiple repos into your agent's knowledge base
for (const repo of repositories) {
const files = await getTreeListing(repo);
const matched = resolveFileMatches(
files,
repo.manifest,
'/',
'/docfx.json'
);
// Only index documentation — not code, not tests, not configs
for (const docPath of matched.contentMatches) {
const content = await fetchFile(repo, docPath);
await knowledgeBase.ingest({
path: docPath,
repo: repo.name,
content
});
}
}
레포에서 문서를 더 빠르고 정확하게 추출할수록, 에이전트의 지식은 더 최신이고 정확해집니다. 효율적인 콘텐츠 가져오기는 신뢰할 수 있는 AI 기반 문서 경험의 기반입니다.
직접 사용해 보기
이 라이브러리는 MIT 라이선스를 따르며, Git 제공자에 대해 어떠한 의견도 가지고 있지 않습니다 — 파일 목록을 제공할 수 있는 모든 API와 함께 작동합니다.
npm install github:microsoft/selective-repo-fetch
리포가 커서 문서 빌드가 느리다면 한 번 시도해 보세요. PR은 언제든 환영합니다.
가장 최악의 모노레포 문서 빌드 경험은 무엇이었나요? 댓글로 알려 주세요.