Docusaurus + GitHub Pages 출판 가이드
출판 스택 결정 (현행)
| 영역 | 도구 |
|---|---|
| 정적 사이트 생성기 | Docusaurus 3.10.x (classic preset, docs only) |
| 배포 | GitHub Pages (workflow: .github/workflows/deploy-docusaurus.yml) |
| 배포 트리거 | 태그 push (v* 패턴) — main push 로는 배포되지 않음 |
| 단일 출처 | manuscript/articles/*.md |
| 빌드 변환 | docs-docusaurus/scripts/copy-from-manuscript.mjs |
영입 원본: book-happtalk 의 multi-tool hub 패턴. 이 프로젝트는 단일 도구만 사용해 hub 패턴을 생략했다 — Docusaurus 산출물이 곧 사이트.
URL 구조
https://blumnai-studio.github.io/tech-writing-harness/
└─ baseUrl
url:https://blumnai-studio.github.io— GitHub organization Pages 도메인baseUrl:/tech-writing-harness/— 저장소명 그대로organizationName:BlumnAI-StudioprojectName:tech-writing-harness
저장소 이름이 변경되면 docusaurus.config.ts 의 세 곳을 동시에 갱신해야 한다.
글 링크 체계 — M코드 공식 링크 + 한글 링크 호환 (v1.6.1)
한글 slug URL은 공유 시 percent-encoding 으로 길고 깨지기 쉬워, 글마다 짧은 M코드를 공식 링크로 쓴다.
| 층 | 값 | 역할 |
|---|---|---|
| 공식 링크 | /articles/M011 | frontmatter code 에서 생성 — 공유·클릭 이동의 정본 |
| 한글 링크 | /articles/110-자비스-라운드테이블 | 기존 공유분 호환 — 정적 리다이렉트 스텁이 M코드로 보냄 |
동작 구조:
- 원고 frontmatter 의
code: MNNN([[manuscript-conventions]] §frontmatter) 이 단일 출처 copy-from-manuscript.mjs가code를 Docusaurusslug/id로 주입하고, 한글 파일명 slug → M코드의 링크 호환 맵을src/data/article-redirects.json으로 생성 (gitignored)@docusaurus/plugin-client-redirects가 맵을 읽어 구 한글 경로마다 정적 HTML 스텁(meta refresh + canonical + JS, query/hash 보존)을 빌드에 심는다 — GitHub Pages 는 서버 리다이렉트가 없으므로 이것이 정적 호스팅의 권장 방식- 본문 상호 링크는 파일 상대 형식(
./NNN-슬러그.md)으로 쓰면 빌드가 M코드 URL 로 자동 재작성한다 — URL 상대 형식(../NNN-슬러그)은 금지 (030/040/050 에서 일괄 정정된 전력)
새 글 추가 시 code 누락이면 copy 스크립트가 경고를 내고 한글 slug 로 발행된다 — lint 대상.
빌드 흐름
manuscript/articles/*.md ← 사람이 작성
│
│ npm run copy:manuscript
│ (MANUSCRIPT_STATUS_FILTER=published 가 CI 기본)
▼
docs-docusaurus/docs/articles/ ← 자동 생성 (gitignored)
│
│ npm run build (docusaurus build)
▼
docs-docusaurus/build/ ← 정적 사이트 산출물 (gitignored)
│
│ actions/upload-pages-artifact → actions/deploy-pages
│ (트리거: git tag v* push)
▼
GitHub Pages
출판 트리거 — 태그 기반
main 브랜치 push 만으로는 사이트가 배포되지 않는다.
v* 패턴의 git tag 가 push 되어야 배포가 일어난다 — 사이트 게시를 명시적 결정으로 끌어올린 의도.
태그 컨벤션 (semver)
| 자리 | 언제 올리는가 |
|---|---|
| MAJOR (v1.0.0 → v2.0.0) | 사이트 구조 전면 개편, 도구 교체 (Docusaurus → 다른 SSG) |
| MINOR (v1.0.0 → v1.1.0) | 새 글 published, 사이드바 구조 변경 |
| PATCH (v1.0.0 → v1.0.1) | 오탈자 수정, 이미지 교체, 표기 통일 같은 작은 보정 |
본 프로젝트 내부 하네스 버전(
harness/docs/vX.Y.Z.md)과 사이트 태그(vX.Y.Z)는 별도 축으로 흘러간다. 하네스는 정원이 자라는 속도, 사이트 태그는 발행물의 누적 속도.
출판 절차
# 1) 원고 작업 + 검수 + manuscript-architect 가 status: published 로 승급
# (publication-pipeline 의 Stage 1~4)
# 2) main 으로 commit + push (배포 미발동 — 작업 백업만)
git push origin main
# 3) 사이트 발행 결정 — 새 태그 생성
git tag v1.0.0 -m "첫 글 게시 — 010-문서시스템-하네스도입"
git push origin v1.0.0
# → GitHub Actions deploy-docusaurus.yml 가 발동
# 4) 워크플로우 상태 확인
gh run list --workflow=deploy-docusaurus.yml --limit 3
# 5) 성공 후 사이트 URL 확인 (run summary 또는 repo Settings → Pages)
긴급 수동 배포
태그 없이 즉시 배포해야 할 때 (예: 핫픽스 검증):
gh workflow run deploy-docusaurus.yml --ref main
workflow_dispatch 트리거가 발동된다. 단, 정상 출판 흐름은 태그를 통하는 것이 원칙.
copy-from-manuscript.mjs 동작
manuscript/walk →.md파일 수집- frontmatter 파싱 →
status가 필터값에 포함되어야 통과 - 파일명에서 slug 추출 → frontmatter 에
id/slug명시 주입 (Docusaurus 가 prefix010-을 자동 제거하는 동작 차단) order→sidebar_position매핑- 상대경로 이미지: manuscript/ 바깥이면
docs-docusaurus/docs/_assets/<parent>/로 복사 + 경로 재작성 {{변수}}→ 백틱 inline code (MDX JS 평가 회피)
환경변수
| 변수 | 기본 | 효과 |
|---|---|---|
MANUSCRIPT_STATUS_FILTER | published | 콤마 구분. 예: draft,review,published |
CI는 기본값 그대로. 로컬 미리보기 시 작성자가 토글.
GitHub Pages 활성화 (저장소 처음 셋업 시)
- GitHub repo → Settings → Pages
- Source:
GitHub Actions선택 (Deploy from a branch 가 아님!) - Pages visibility: 저장소가
internal/private인 경우 별도로Public선택 (GitHub Enterprise Cloud 기능 — 코드는 비공개 유지하면서 Pages만 공개 게시) - 첫 워크플로우 실행: Actions 탭 →
Deploy · Docusaurus → GitHub Pages→ Run workflow - 성공 후 환경 URL 확인 (workflow summary 의 deployment URL)
본 프로젝트(
BlumnAI-Studio/tech-writing-harness)는internal가시성으로 운영하면서 Pages 만Public으로 게시한다. 소스는 조직 내부에서만 보이고, 발행물은 누구나 읽을 수 있다.
권한
워크플로우의 permissions 블록이 필수:
permissions:
contents: read
pages: write
id-token: write
조직 정책에서 Actions Pages 배포가 차단되어 있으면 organization → Settings → Actions → General 에서 허용.
트러블슈팅
빌드 단계
| 증상 | 진단 | 조치 |
|---|---|---|
npm install 의존성 해석 실패 | Node 버전 확인 | setup-node 의 node-version: '22' 과 로컬 일치 |
copy-from-manuscript.mjs 가 0개 복사 | status 필터 mismatch | MANUSCRIPT_STATUS_FILTER 환경변수 확인. manuscript 에 status: published 글이 실제로 있는지 |
Broken link warn | 내부 링크의 상대경로 오류 | 같은 articles/ 의 다른 글로 가는 링크는 ./<slug> 형태. onBrokenLinks: 'warn' 이라 빌드는 통과하나 사이트에서 404 |
Broken markdown image warn | manuscript의 이미지 src 가 존재하지 않음 | copy 스크립트의 [copy] WARN image not found 로그에서 누락 파일 식별 |
MDX 파서 오류 (Unexpected token) | 본문에 <...> 또는 {...} 형태 텍스트 | .md 만 사용 + {{변수}} 는 자동 escape 됨. 그래도 깨지면 백틱으로 명시 escape |
워크플로우 등록/파싱 단계 (v1.0.0 사이클에서 학습)
| 증상 | 진단 | 조치 |
|---|---|---|
워크플로우가 gh run list 에 name: .github/workflows/<>.yml (파일 경로) 으로 표시 | YAML 파싱 실패 — GitHub Actions 가 name: 필드를 읽지 못함 | 로컬 python -c "import yaml; yaml.safe_load(open(...))" 로 정확한 줄·열 식별 |
step name: 에 (status filter: published) 같이 콜론+공백이 들어가면 즉시 실패 | YAML 매핑 키:값으로 잘못 파싱 | step name 을 큰따옴표로 감싼다: name: "Copy manuscript to docs (status filter: published)" |
워크플로우는 등록됐는데 job 이 0초만에 실패, annotation 에 is not allowed to deploy to <environment> due to environment protection rules | environment 의 deployment branch policy 가 태그를 허용하지 않음 | gh api -X POST repos/<owner>/<repo>/environments/<env>/deployment-branch-policies -f name='v*' -f type='tag' |
gh api .../pages 가 "public": false 인데 발행물을 공개하고 싶다 | internal/private repo 의 Pages 기본 가시성 | gh api -X PUT repos/<owner>/<repo>/pages -f public=true — Enterprise Cloud 기능 |
배포 단계
| 증상 | 진단 | 조치 |
|---|---|---|
| 401/403 deploy 실패 | Pages 권한 누락 | repo Settings → Pages → Source: GitHub Actions |
| 페이지 404 (URL은 맞는데) | baseUrl mismatch | baseUrl: '/tech-writing-harness/' 와 실제 저장소명 일치 확인 |
| asset 404 (이미지 깨짐) | _assets/ 복사 실패 또는 경로 깨짐 | 빌드 산출물의 build/articles/<slug>/_assets/ 존재 확인 |
| 사이트가 옛 버전 그대로 | 캐시 또는 워크플로우 실패 | Actions 탭에서 최근 실행 상태 확인. concurrency 그룹 pages 가 cancel 되었는지 |
Pages 환경 (cosmetic)
| 증상 | 조치 |
|---|---|
| 다크모드가 시스템 설정 무시 | themeConfig.colorMode.respectPrefersColorScheme: true 확인 |
| 한국어 검색 불가 | 기본 검색만 제공. Algolia DocSearch 도입 검토 |
| 사이드바가 어색 | sidebars.ts 의 카테고리 라벨 및 dirName 수정 |
채택 의사결정 기록
| 결정 | 이유 |
|---|---|
| Docusaurus만 사용 (multi-tool hub 안 함) | 단일 사이트로 충분. hub 패턴은 도구가 2개 이상일 때만 가치 |
버전 디렉터리 없음 (docs-docusaurus/v1/ 가 아님) | 아직 다중 버전 필요 없음. 필요해지면 그때 docs-docusaurus/v2/ 추가하고 hub 패턴 검토 |
| docs preset (블로그 X) | manuscript-architect 의 frontmatter/status/order 흐름이 docs 와 잘 맞음. 글이 시간순 정렬이 본질이면 blog preset 으로 전환 검토 |
routeBasePath: '/' | 사이트의 root 가 곧 문서. /docs/ 접두사 없음 |
전환을 검토할 신호:
- 글의 발견이 시간순이 더 자연스러워질 때 → blog preset
- 다국어/다중 버전 도입 → hub 패턴 영입
- 검색 만족도가 낮을 때 → Algolia DocSearch
노션 북 채널 — 제2 출판 채널 (2026-08-09 개설)
GitHub Pages 와 별개로, 사내 노션 워크스페이스(happytalk)에 시리즈 전편을 북 형태로 출판한다.
| 항목 | 값 |
|---|---|
| 루트 페이지 | "블룸 정원의 하네스" (3b7c0b11-04dd-80ea-9aed-ffd8f7426138) |
| 파이프라인 | docs-notion/publish-notion-book.mjs (Node 18+, 의존성 0) |
| 상태 파일 | docs-notion/notion-book.state.json — 편별 pageId·해시·내비 블록 id (커밋 대상) |
| 시크릿 | .secret/notion.json — internal-doc-tools 와 공유 (integration "쌤전용") |
| API 버전 | 2022-06-28 (file_upload·cover·inline DB 프로브 검증: 2026-08-09) |
구조
블룸 정원의 하네스 (루트)
├── 📚 시리즈 목차 — 인라인 DB "하네스 테크라이팅 시리즈"
│ 속성: 제목("NN · 제목") / 순번 / 게시일 / 카테고리 / 코드 / 요약 / 상태
│ 각 행 = 편 페이지: 커버(히어로) + 아이콘 🌿 + TOC + 본문(이미지 인라인 업로드)
│ + 하단 내비(← 이전편 | 📖 목차 | 다음편 →)
└── 🕘 업데이트 이력 — 인라인 DB (최초출판/추가/수정, 일시·상세)
실행 (증분 sync)
node docs-notion/publish-notion-book.mjs sync [--dry-run] [--limit N] [--status published,review]
- 기본은
status: published원고만 대상 — review 편은 승급 후 다음 sync 에서 자동 등재 --status published,review로 검수 전 편도 선출판 가능 — 이 경우 시리즈 DB 상태가 검수중으로 표기되고, 이후 원고가 published 로 승급되면 frontmatter 변경이 해시를 바꿔 다음 기본 sync 에서 자동 재출판 + 상태 '게시' 갱신 (2026-08-09 19편 선출판이 첫 사례)- 해시 동일 → skip / 신규 → 행 생성 / 변경 → 본문 블록 전량 교체 + 속성·커버 갱신
- 내비는 매 sync 재계산 — 새 편이 붙으면 직전 편의 "다음편" 링크가 자동 갱신됨
- 이력 DB 에 최초출판/추가/수정 행 자동 기록
변환 규칙 (Docusaurus 채널과의 차이)
| 원고 요소 | 노션 처리 |
|---|---|
| H1 (제목) | 제거 — 페이지 제목이 대신 |
히어로 이미지 (hero.png) | 본문에서 빼고 페이지 커버로 승격 (갤러리 카드 미리보기) |
| "3줄 요약" 인용 | 💡 파란 콜아웃으로 승격 |
| 절 이미지 | Notion File Upload API 로 직접 업로드 + alt → 캡션 |
내부 상호 링크 (./NNN-*.md) | 노션 편 페이지 URL 로 재작성 (미등재 편이면 링크 제거, 텍스트 유지) |
| GFM 표 / 코드펜스 / 인용 | Notion table / code / quote 블록 |
수동 1회 설정 (API 미지원 영역)
- 시리즈 DB 의 뷰를 갤러리로 전환 + 카드 미리보기 = "페이지 커버", 정렬 = 순번 오름차순 — Notion API 는 뷰 생성/변경을 지원하지 않아 최초 1회 사람이 전환
- 제목의
NN ·접두어 덕에 기본 정렬로도 출판순이 유지된다
주의
- 통합 토큰은 워크스페이스 단위 + 페이지별 공유 — 루트 페이지에 integration 이 연결되어 있어야 하위 전체 접근 가능
- 파일 업로드 id 는 발급 후 1시간 내 attach 필요 — 파이프라인이 업로드 직후 attach 하므로 정상 흐름에선 문제 없음
- 재출판(변경) 은 기존 블록을 전량 삭제 후 재적재 — 블록 수 × ~300ms 소요, 대량 개정 시 시간 감안
- GitHub Pages 채널과 이 채널은 독립 — 사이트 태그 배포와 노션 sync 는 서로를 트리거하지 않는다