docfx-remote-include: DocFX에 원격 마크다운(Markdown)을 인라인으로 포함하고 조립된 페이지를 관리하기
요약
docfx-remote-include는 DocFX 빌드 시 HTTP를 통해 원격 마크다운 파일을 인라인으로 포함할 수 있게 해주는 .NET CLI 도구입니다. 중앙 집중식 거버넌스를 위해 조립된 페이지를 변환 서비스로 전송하는 기능을 제공하며, 기존 DocFX의 업스트림 릴리스를 그대로 유지하며 동작합니다.
핵심 포인트
- HTTP를 통한 원격 마크다운 인라인 포함 기능 제공
- DocFX 포크가 아닌 NuGet 의존성 방식으로 업스트림 추적 가능
- 블록 및 인라인 형태의 두 가지 포함 지시문 지원
- 중첩 지시문, 사이클 탐지 및 빌드 캐시 기능 내장
- 조립된 페이지에 대한 단일 페이지 수준의 변환(transform) 지원
docfx-remote-include는 DocFX를 위한 Markdig 확장 기능이자 dotnet CLI 도구입니다. 이 도구는 두 가지 역할을 수행합니다. 빌드 시점에 HTTP 서비스에서 가져온 마크다운 (Markdown)을 인라인 (inline)으로 포함하며, 완전히 조립된 각 페이지를 중앙 집중식 거버넌스 (governance)를 위해 변환 서비스 (transform service)로 전송할 수 있습니다.
이것은 DocFX의 포크 (fork)가 아닙니다. DocFX의 공개된 BuildOptions.ConfigureMarkdig 접점 (seam)에 연결되므로, 일반적인 NuGet 의존성 (dependency)으로서 업스트림 (upstream) DocFX 릴리스를 추적합니다. 이 도구는 .NET 8, 9, 10을 대상으로 합니다.
이 프로젝트에 대한 저의 이전 게시물을 읽으셨다면, 개별 포함 항목에 대한 AI 재작성 (rewrite) 기능이 있었음을 아실 것입니다. 개별 포함 항목을 태그하면 모델이 해당 파편을 제자리에서 재작성하는 방식이었습니다. 그 기능은 이제 삭제되었습니다. 거버넌스 (governance) 기능은 조립된 페이지에 대해 한 번 실행되는 단일 페이지 수준의 변환 (transform)으로 격상되었으며, 이는 더 단순할 뿐만 아니라 문제 해결에 더 적합합니다. 이에 대한 자세한 내용은 아래에서 다룹니다.
포함 지시문 (The include directive)
DocFX에 의해 처리되는 모든 마크다운 (markdown) 파일 내에서 다음과 같이 사용합니다:
Some local content.
[!remoteinclude[Welcome](snippets/welcome.md)]
...
빌드 시점에 확장 기능은 GET {baseUrl}/{source}를 수행하고, 응답을 마크다운 (markdown)으로 파싱하여 인라인 (inline)으로 포함합니다. 지시문은 하나의 구문을 공유하는 두 가지 형태를 가집니다:
- 블록 (Block) — 지시문이 해당 라인에 유일하게 존재합니다. 가져온 마크다운 (markdown)은 블록 콘텐츠 (headings, lists, paragraphs)로 인라인 (inline) 처리됩니다.
- 인라인 (Inline) — 지시문이 문단 중간에 나타납니다. 가져온 마크다운 (markdown)은 반드시 단일 문단으로 축소되어야 하며,
<p>래퍼 (wrapper) 없이 인라인 (inline) 콘텐츠만 삽입됩니다.
중첩된 지시문 (Nested directives), 사이클 탐지 (cycle detection, AsyncLocal 소스 스택을 통해 최대 깊이 8), 빌드당 프로세스 내 캐시 (in-process per-build cache), 그리고 최대 8개의 동시 요청으로 제한된 동시성 (concurrency) 기능이 모두 내장되어 있습니다. 기본적으로 소스가 누락되면 빌드가 실패하며, --allow-missing 옵션을 사용하면 눈에 보이는 에러 플레이스홀더 (error placeholder)를 렌더링합니다.
콘텐츠 서비스가 복잡한 URL 스킴 (URL scheme)을 사용하는 경우, {source} 플레이스홀더 (placeholder)를 포함한 urlTemplate을 설정하십시오:
{
"baseUrl": "https://api.example.com/",
"urlTemplate": "content/GetFile?path={source}"
...
선택적 페이지 변환 (Optional page transform)
모든 포함 (include) 항목이 해결되고 페이지가 완전히 조립된 후, 빌드 과정에서 해당 페이지를 **페이지 변환 서비스 (page transform service)**로 보낼 수 있습니다. 서비스가 규칙을 소유하며, 페이지는 YAML 프론트매터 (frontmatter)를 통해 의도만을 선언합니다:
---
transform:
audience: engineer
...
확장 프로그램은 이 transform: 블록을 추출하여, 조립된 페이지와 메타데이터를 설정된 엔드포인트 (endpoint)로 전송하고 그 응답을 사용합니다. 거버넌스 (Governance)는 페이지 수준에서 단 한 번 수행됩니다. 라이브러리는 계약 (contract)만을 정의합니다:
{
Task<PageTransformResponse> TransformAsync(
...
transform 설정을 생략하면 이 기능을 비활성화할 수 있습니다. 라이브러리 자체에는 Azure 의존성이 없습니다. LLM (Large Language Model) 또는 결정론적 규칙 (deterministic rules)을 사용하여 IPageTransformService를 구현하십시오.
참조용 콘텐츠 및 변환 서비스 (The reference content-and-transform service)
해당 리포지토리 (repo)에는 하나의 엔드포인트 표면에서 두 가지 역할을 모두 수행하는 참조 서비스(samples/knowledge-service)가 포함되어 있습니다:
| 엔드포인트 (Endpoint) | 메서드 (Method) | 목적 (Purpose) |
|---|---|---|
/content/{path} | GET | [!remoteinclude] 지시문 (directives)을 위한 마크다운 (markdown) 제공 |
| ... |
remoteinclude.json에서 두 항목 모두 동일한 서비스로 지정하십시오:
{
"baseUrl": "http://localhost:8080/content/",
"transform": {
...
/transform 엔드포인트는 구성 방식에 따라 세 가지 동작을 수행합니다:
- 중앙 가이드가 있는 경우 (
content/guidance/아래에 마크다운이 있는 경우): 해당 가이드를 신뢰할 수 있는 단일 출처 (source of truth)로 취급하여, 조립된 각 페이지를 가이드와 비교하고, 내용이 일치하지 않는 부분 위에> [!NOTE] **Team Override**콜아웃 (callout)을 삽입합니다. 또한 페이지의audience/intent에 맞춰 톤 (tone)과 구조를 조화시킵니다. - 가이드 콘텐츠가 없는 경우: LLM으로 전달되는 패스스루 (passthrough) 역할을 수행합니다. 비교나 오버라이드 (override) 콜아웃 없이, 선언된
audience/intent에 맞춰 톤과 구조를 조화시킵니다. AiEndpoint가 구성되지 않은 경우: 페이지를 변경하지 않고 그대로 반환합니다./content는 여전히 작동합니다.
사이드카(sidecar)로 실행하기
이 컨테이너는 0.0.0.0:8080에 바인딩되며(Dockerfile의 ASPNETCORE_URLS를 통해 설정됨), 모든 설정을 환경 변수(environment variables)로부터 읽어오고 필수 종속성이 없으므로, 문서 빌드 옆에서 사이드카(sidecar)로서 깔끔하게 실행됩니다. content/guidance와 AiEndpoint가 없는 경우에도 여전히 /content를 제공하며, /transform으로부터 변경되지 않은 페이지를 반환합니다.
Kubernetes — 동일한 Pod 내에서 실행하고 /health를 프로브(probe)하세요:
containers:
- name: docs-build
image: your-docs-builder:latest
...
사이드카는 빌드 컨테이너와 localhost를 공유하므로, baseUrl과 transform.endpoint 모두 http://localhost:8080을 가리키도록 설정하십시오. Docker Compose 예제는 샘플의 README에 있습니다.
사용 방법
CLI (dotnet tool)로 사용하는 경우, docfx.json 옆에 remoteinclude.json을 배치합니다:
dotnet tool install -g Documentation.DocfxRemoteInclude.Cli
docfx-ri build docs/docfx.json
Docset.Build(...)를 호출하는 호스트를 위한 라이브러리(library)로 사용하는 경우:
using Docfx;
using Docfx.RemoteInclude;
...
HTTP가 아닌 소스, 커스텀 인증 (mTLS, 서명된 URL), 또는 디스크 캐싱(on-disk caching)을 위해 자체적인 IRemoteContentClient를 제공할 수 있습니다.
인증(Auth) 및 자격 증명(credentials)
콘텐츠(content)와 트랜스폼(transform) auth 블록 모두 다음 형식을 허용합니다:
{ "mode": "none" | "default" | "managedIdentity" | "jwt" | "key", "value": "...", "scope": "..." }.
value는 $VAR / ${VAR}를 통해 환경 변수로 간접 참조할 수 있으며, scope는 default/managedIdentity 모드에 대한 OAuth 대상(audience)을 재정의합니다. 자격 증명은 환경 변수 또는 호스트가 제공하는 콜백(callback)에서 읽어오며, 절대 docfx.json에서 읽지 않고 디스크에 기록하지도 않습니다.
엔드 투 엔드(end to end)로 시도해보기
저장소에는 samples/knowledge-service 참조 서비스와 연결된 실행 가능한 samples/basic DocFX 사이트가 포함되어 있습니다. 서비스를 실행하고 샘플을 빌드하면, 공유된 스니펫(snippets)과 페이지 수준의 거버넌스(governance)가 함께 작동하는 것을 확인할 수 있습니다:
# terminal 1
cd samples/knowledge-service
dotnet run
...
MIT 라이선스입니다. 이슈(Issues)와 PR(Pull Requests)을 환영합니다.
GitHub: github.com/saipramod/docfx-remote-include
NuGet: Documentation.DocfxRemoteInclude · Documentation.DocfxRemoteInclude.Cli
Sai Pramod Upadhyayula는 Microsoft의 선임 소프트웨어 엔지니어(Senior Software Engineer)로, AI 기반 기업 지식 플랫폼을 개발하고 있으며 DocFX 오픈 소스 생태계에 기여하고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기