1. 개요

각 책을 독립적인 원고 저장소(Repo)로 유지하면서, 빌드 엔진(SSG)은 하나의 공통 저장소에서 관리하는 전략을 구현 관점에서 정리한다. 핵심은 원고 저장소는 콘텐츠와 리소스만 보관하고, 엔진 저장소는 렌더링과 빌드, 배포를 전담하는 구조다.

구현의 공통 패턴은 다음과 같이 고정된다.

  • 엔진 Repo는 템플릿, 스타일, 빌드 스크립트, 배포 파이프라인을 가진다.
  • 책 Repo는 content/static/ 중심의 콘텐츠 패키지로 유지한다.
  • 로컬과 CI에서 책 Repo를 일정한 경로에 배치한 뒤, 엔진이 그 경로를 읽어 빌드한다.

즉, 저장소는 분리하고, 빌드 시점에만 조립한다.

2. Hugo 구현

Hugo에서는 Hugo Modules의 mounts로 외부 콘텐츠 디렉터리를 content/, static/으로 연결하는 방식이 가장 단단하다. 엔진 Repo에서 책별 설정 파일을 두고, 빌드 스크립트가 책 식별자만 받아 항상 같은 명령으로 빌드하도록 만든다.

2.1 권장 작업공간 구조

plain text
/my-workspace
├── hugo-engine/
└── books/
	├── book-1/
	│	├── content/
	│	└── static/
	└── book-2/
		├── content/
		└── static/

2.2 엔진 Repo 최소 골격

plain text
hugo-engine/
├── config/
│	├── _default/
│	│	└── hugo.toml
│	└── books/
│		├── book-1.toml
│		└── book-2.toml
├── layouts/
├── assets/
├── static/
└── scripts/
	└── build-book.sh

2.3 공통 설정

config/_default/hugo.toml에는 공통 설정만 둔다.

toml
baseURL = "/"
title = "Common Publishing Engine"
defaultContentLanguage = "ko"

[module]
	[module.hugoVersion]
		extended = true
		min = "0.140.0"

2.4 책별 설정과 mounts

config/books/book-1.toml에 책별 baseURL, title과 mounts를 둔다.

toml
baseURL = "https://book-1.example.com/"
title = "Book 1"

[module]
	[[module.mounts]]
		source = "../books/book-1/content"
		target = "content"

	[[module.mounts]]
		source = "../books/book-1/static"
		target = "static"

mounts는 절대 경로가 아니라 작업공간 상대 경로로 고정한다. 따라서 로컬과 CI에서 my-workspace 구조를 동일하게 재현하는 것이 중요하다.

2.5 빌드 스크립트

엔진 Repo에서 책 식별자를 받아 책별 설정 파일을 합쳐 빌드한다.

bash
#!/usr/bin/env bash
set -euo pipefail

BOOK="${1:?book id required}"
WORKSPACE_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
ENGINE_DIR="$WORKSPACE_ROOT/hugo-engine"
BOOK_DIR="$WORKSPACE_ROOT/books/$BOOK"
DEST_DIR="$BOOK_DIR/public"

cd "$ENGINE_DIR"

hugo \
	--config "config/_default/hugo.toml,config/books/$BOOK.toml" \
	--destination "$DEST_DIR"

이 방식은 엔진 변경과 책 변경을 분리하면서도, 빌드 절차를 단일화한다.

3. Astro 구현

Astro의 핵심은 엔진 Repo가 렌더링과 라우팅을 담당하고, 책 Repo의 마크다운을 외부 경로에서 로딩하는 구조를 고정하는 것이다. 최신 Astro에서는 Content Layer 기반으로 컬렉션을 정의하고, 로더에서 외부 경로를 대상으로 파일을 읽도록 만든다.

3.1 권장 작업공간 구조

plain text
/my-workspace
├── astro-engine/
└── books/
	└── book-1/
		├── content/
		└── static/

3.2 외부 콘텐츠 로딩 패턴

astro-engine는 다음을 고정한다.

  • src/content.config.ts에서 컬렉션 스키마를 정의한다.
  • 로더가 ../books/${BOOK}/content/**/*.{md,mdx} 같은 외부 경로를 대상으로 문서를 수집한다.
  • 빌드 대상 책은 환경 변수나 빌드 인자로 주입한다.

구현은 프로젝트 버전에 따라 차이가 있으므로, 형태를 다음처럼 단순 패턴으로 유지하는 편이 좋다.

  • BOOK=book-1 환경 변수를 기준으로 외부 콘텐츠 루트를 결정한다.
  • 로더 또는 glob 수집 경로에 그 값을 반영한다.

3.3 정적 리소스 연결

책 Repo의 static/은 엔진의 public/로 복사하거나, 빌드 단계에서 심볼릭 링크로 연결한다.

  • CI에서는 rsync -a books/book-1/static/ astro-engine/public/ 같은 방식이 가장 단순하다.
  • 로컬에서는 개발 편의상 링크를 사용할 수 있다.

결과적으로 Astro에서도 저장소 분리는 유지되고, 엔진이 책별 빌드 프로파일을 통해 외부 원고를 조립한다.

4. MkDocs 구현

MkDocs는 기본적으로 프로젝트 내부의 docs/를 읽지만, mkdocs-multirepo-plugin 또는 mkdocs-monorepo-plugin을 사용하면 외부 문서 트리를 가져올 수 있다. 엔진 Repo에 MkDocs 설정과 테마를 두고, 책 Repo의 문서 트리를 플러그인으로 합성한다.

4.1 권장 작업공간 구조

plain text
/my-workspace
├── mkdocs-engine/
└── books/
	└── book-1/
		└── docs/

4.2 설정 패턴

엔진 Repo의 mkdocs.yml이 테마, 플러그인, 네비게이션을 관리한다.

  • 멀티레포 계열 플러그인을 사용해 ../books/book-1/docs를 문서 소스로 포함한다.
  • 네비게이션은 엔진에서 단일 규칙으로 관리하거나, 책 Repo의 mkdocs.nav.yml 같은 파일을 불러오는 방식으로 위임한다.
  • mkdocs.yml 설정 파일에서 다른 Repo의 경로를 !import 구문으로 가져옵니다.
yaml
nav:
  - Book 1: '!import ../books/book-1/mkdocs.yml'

개념적으로는 다음 구조로 고정한다.

  • 엔진: mkdocs.yml, theme/, plugins/, 빌드 스크립트
  • 책: docs/ 트리(마크다운과 이미지)

4.3 빌드 스크립트

엔진 Repo에서 BOOK 인자를 받아 문서 루트와 설정 변수를 고정한다.

  • BOOK=book-1 mkdocs build 형태로 환경 변수를 주입한다.
  • 플러그인 설정에서 해당 값을 사용해 포함할 경로를 결정한다.

MkDocs는 문서형 콘텐츠에 최적화되어 있으므로, 책이 기술 문서 스타일일수록 운영 난도가 낮다.

5. 결론

이 전략의 구현 핵심은 한 문장으로 정리된다. 저장소는 분리하고, 빌드 시점에만 조립한다.

Hugo에서는 mounts가 가장 직접적인 조립 수단이고, Astro는 외부 콘텐츠 로딩과 정적 리소스 합성이 조립 수단이며, MkDocs는 플러그인 기반 문서 트리 합성이 조립 수단이다.

따라서 장기 운영 관점에서 지켜야 할 기준은 다음으로 수렴한다.

  • 책 Repo는 콘텐츠 패키지로 유지해 이식성을 확보한다.
  • 엔진 Repo는 빌드와 표현의 단일 원천으로 유지한다.
  • 빌드 명령은 엔진 스크립트 하나로 단일화한다.
  • 로컬과 CI가 동일한 작업공간 구조를 재현하도록 고정한다.