사양서나 절차서를 Word나 Excel 파일로 공유 폴더에 두고 계시지는 않나요? '최신 버전이 무엇인지?', '누가 어디를 변경했는지?' 알 수 없게 되는 것은 흔히 겪는 고민입니다.
문서도 코드와 마찬가지로 GitHub에서 관리할 수 있습니다. 변경 사항은 PR(Pull Request)을 통해 리뷰하고, 서식 오류는 GitHub Actions를 이용해 자동으로 검사하는 방식입니다. 본 글에서는 그 시작 방법을 소개합니다.
예시로 이 시리즈의 아티클들을 관리하는 리포지토리를 사용하겠습니다. 여기서 소개하는 시스템은 실제로 이 리포지토리에서 작동하고 있습니다. 사양서든 회사 Wiki든, Markdown으로 작성할 수 있는 것이라면 같은 방식으로 적용할 수 있습니다.
문서를 코드와 동일한 메커니즘으로 관리하는 개념을 'Docs as Code'라고 부릅니다. 주요 장점 4가지는 다음과 같습니다.
- 이력이 남는다. 언제, 누가, 무엇을, 왜 변경했는지 모든 기록이 남아 있습니다. '이전 버전으로 되돌리고 싶다'는 것도 즉시 가능합니다.
- 차이점을 볼 수 있다. 변경된 부분만 행 단위로 확인할 수 있습니다. 이는 Word나 Excel에서는 어려운 점입니다.
- 리뷰할 수 있다. 변경 사항을 PR에 올리면, 공개하거나 반영하기 전에 다른 사람이 검토할 수 있습니다.
- 자동 검사가 가능하다. 서식 오류나 필수 항목 누락 등을 사람 대신 기계가 찾아줍니다.
또한 AI와도 궁합이 뛰어납니다. Markdown 파일이 리포지토리에 존재한다면, 에이전트 형태의 AI가 이를 그대로 읽을 수 있습니다. 사양서를 Markdown으로 변환하여 리포지토리에 두는 것은 AI에게 좋은 input을 제공하기 위한 하네스(Harness)를 구축하는 것과 같습니다 (하네스에 대해서는 다른 글에서 작성했습니다).
본 글에서 구현할 흐름은 다음과 같습니다.
- 작업용 브랜치를 만들고 문서를 작성한다.
- PR을 생성한다.
- GitHub Actions가 Markdown의 서식과 front matter를 자동으로 검사한다.
- 사람(또는 AI)이 PR을 리뷰한다.
- 검사와 리뷰가 통과되면 main에 병합(Merge)한다.
main 브랜치에 들어간 내용만이 '공식적인 버전'이 됩니다. Zenn의 경우, main에 병합되는 시점에 아티클이 반영됩니다.
우선 어디에 무엇을 둘지 결정해야 합니다. Zenn 리포지토리라면 다음과 같은 구조가 될 수 있습니다.
zenn-articles/
articles/ ← 아티클 (1개 아티클당 1 파일)
scripts/
...
사양서의 경우, docs/ 아래에 기능별 폴더를 나누는 것이 일반적입니다. 중요한 것은 저장 위치 규칙을 README에 명시하는 것입니다. 사람이나 AI 모두 README만 보면 헤매지 않습니다.
main에 직접 push하지 않고, 작업마다 브랜치를 분리합니다.
git switch -c add-github-docs-article
# 아티클 작성
git add articles/github-docs-pr-review-actions-lint.md
...
push하면 GitHub 화면에 'Compare & pull request' 버튼이 나타납니다. 여기서 PR을 만들 수 있습니다.
.github/pull_request_template.md 파일을 두면, PR을 생성할 때마다 해당 내용이 본문에 포함됩니다. 리뷰 시 매번 확인하고 싶은 내용을 체크리스트로 만들어 두는 것이 좋습니다.
## 변경 내용
<!-- 무엇을 작성했는지・수정했는지 1~2줄로 -->
## 체크리스트
...
리포지토리의 Settings에 있는 Rules(규칙 세트)에서 main 브랜치에 대한 규칙을 설정할 수 있습니다. 추천하는 것은 다음 두 가지입니다.
PR을 통과해야만 병합되도록 하기
GitHub Actions 검사가 통과해야만 병합되도록 하기
혼자 운영하는 리포지토리라면, 리뷰 승인 필수까지는 설정하지 않아도 괜찮습니다. 검사(Check)가 통과하는 것만 필수로 지정해 두면, 실수가 main에 들어가는 것을 막을 수 있습니다. 화면의 항목명은 변경될 수 있으니 자세한 내용은 GitHub 공식 문서를 확인해 주세요.
lint(린트)는 서식 오류나 작성 방식의 불규칙성을 기계적으로 찾아내는 시스템입니다. Markdown에서는 markdownlint라는 도구가 자주 사용됩니다.
바로 GitHub Actions에 넣기 전에, 로컬 환경에서 한번 실행해보는 것이 좋습니다. Node.js가 설치되어 있다면, 별도의 설치 없이 시도할 수 있습니다.
npx markdownlint-cli2 "articles/*.md"
이 시리즈의 아티클에 실제로 적용해 본 결과, 다음과 같은 지적이 나왔습니다.
| 규칙 | 내용 | 대응 |
|---|
| MD036 | 굵은 글씨를 제목처럼 사용함 | 의도적으로 그렇게 작성했으므로, 규칙을 예외 처리함 |
| ... | | |
| 여기서 중요한 것은, 모든 지적 사항에 따를 필요는 없다는 것입니다. 팀의 작성 방식으로서 의도하는 부분은 설정에서 해당 규칙을 제외합니다. | | |
리포지토리 직위에 .markdownlint-cli2.jsonc 파일을 배치합니다.
{
"config": {
"default": true,
...
}
"default": true로 모든 규칙을 활성화한 후, 맞지 않는 것만 false로 설정합니다. 제외한 규칙에는 왜 제외했는지 주석으로 남겨두는 것이 좋습니다. 나중에 보는 사람(그리고 AI)이 헷갈리지 않게 하기 위함입니다.
Zenn 기사 맨 앞에는 프론트 매터(front matter)라는 설정 영역이 있습니다.
---
title: "기사 제목"
emoji: "📚"
...
이 부분을 잘못 작성하면, 기사가 제대로 반영되지 않습니다. Zenn 공식 가이드에서는 주로 다음 규칙들이 정해져 있습니다.
- emoji는 이모지 1개여야 합니다.
- type은
tech (기술 기사) 또는 idea (아이디어 기사)여야 하며, topics는 최대 5개까지입니다.
- published는
true (공개) 또는 false (임시 저장)여야 합니다. 파일명(slug)은 a-z0-9, 하이픈(-), 언더스코어(_)를 포함한 12~50자여야 합니다.
이를 체크하는 스크립트를 scripts/check-frontmatter.mjs에 배치합니다. 추가 패키지 없이 Node.js만으로 작동합니다.
// scripts/check-frontmatter.mjs
// Zenn 기사 (articles/*.md)의 front matter를 체크하는 스크립트
import { readdirSync, readFileSync } from "node:fs";
...
로컬 환경에서 node scripts/check-frontmatter.mjs를 실행하면, 문제가 있을 경우 다음과 같이 알려줍니다.
articles/Bad_Slug.md: 파일명은 a-z0-9, -, _를 포함한 12~50자여야 합니다.
articles/Bad_Slug.md: emoji는 이모지 1개여야 합니다.
articles/Bad_Slug.md: topics는 1~5개여야 합니다.
...
오류 메시지는 한국어로, 어떻게 수정해야 하는지 알 수 있는 방식으로 작성하는 것이 핵심입니다. 오류를 본 사람도, AI도 즉시 수정할 수 있습니다.
이 스크립트는 이 기사처럼 한 줄씩 작성하는 방식을 전제로 한 간이 버전입니다. topics를 여러 줄로 작성하는 방식 등에는 대응하지 않습니다. 더 엄격하게 하고 싶다면 YAML을 읽는 라이브러리를 사용해야 합니다.
마지막으로, PR(Pull Request)가 발생할 때마다 두 가지 체크가 자동으로 실행되도록 설정합니다. .github/workflows/check-articles.yml 파일을 만듭니다.
name: Check articles
on:
pull_request:
...
위에서부터 순서대로 의미는 다음과 같습니다.
- on: 기사나 설정 파일이 변경된 PR일 때만 작동합니다.
checkout: 리포지토리의 파일을 가져옵니다.
setup-node: Node.js 환경을 준비합니다.
markdownlint-cli2-action: articles/ 폴더 내 Markdown 파일을 lint(구문 검사)합니다.
run: 방금 만든 front matter 체크를 실행합니다.
이것을 push하고 PR을 생성하면, PR 화면 하단에 체크 결과가 표시됩니다. 실패했다면, 'Details'에서 어떤 부분이 걸렸는지 확인할 수 있습니다.
참고로, @v7이나 @v24는 액션(Action)의 버전입니다. 각 액션은 주기적으로 업데이트되므로, 사용하기 전에 각각의 GitHub Releases 페이지에서 최신 버전을 확인해야 합니다.
체크가 기계적인 부분이라면 자동화할 수 있고, 그러면 사람의 리뷰는 내용 자체에 집중할 수 있습니다. 나아가 PR 리뷰를 AI의 도움을 받을 수도 있습니다. GitHub Copilot의 코드 리뷰 기능이나 Claude Code의 GitHub 연동 등 다양한 방법으로 PR에 AI 리뷰 코멘트를 달 수 있습니다. 설정 방법은 도구마다 다르므로, 각 공식 문서를 확인해 주세요.
AI에게 문서 리뷰를 요청할 때는 관점을 명확하게 제시하면 정확도가 높아집니다.
이 PR(Pull Request) 문서를 AI에 익숙하지 않은 초보 독자의 시각으로 검토해 주세요. 이해하기 어려운 용어, 설명이 빠진 부분, 사실 확인이 필요한 기술적 서술을 중요도 순서대로 나열해 주십시오.
역할 분담은 다음과 같이 생각하면 이해하기 쉽습니다.
| 점검 담당자 | 강점 분야 |
|---|
| GitHub Actions | 형식(Format), 필수 항목, 명명 규칙 등 명확한 기준이 필요한 부분 |
| ... | |
'Hooks로 시스템화해야 하는 것은 반드시 매번 그렇게 해야 하는 것'이라고 하네스(Harness) 문서에서 작성했습니다. GitHub Actions는 이 사고방식을 팀 전체에 확산시킨 것입니다. 누가 쓰든, AI가 쓰든 동일한 검사를 반드시 통과하게 만듭니다.
처음부터 규칙을 너무 엄격하게 설정하는 것
markdownlint의 모든 규칙을 활성화하면 기존 문서에서 엄청난 양의 에러가 발생합니다. 처음에는 맞지 않는 규칙은 제외하고, 익숙해진 후에 점차 늘려나가야 합니다.
에러가 나올 때마다 규칙을 제거하는 것
반대로 귀찮다는 이유로 모든 것을 제거하면 검사의 의미 자체가 사라집니다. 제거할 것은 '팀의 작성 방식으로서 의도한 부분'으로만 한정해야 합니다. 그리고 제외한 이유는 설정 파일에 주석으로 남겨두어야 합니다.
main 브랜치에 직접 푸시가 가능한 상태로 두는 것
검사를 만들었더라도, main 브랜치에 직접 푸시되면 검사는 무시됩니다. 규칙 세트에서 PR과 검사(Check)를 필수 항목으로 지정해야 합니다.
액션의 버전을 오래된 상태로 방치하는 것
GitHub Actions의 실행 환경은 점진적으로 업데이트되고 있습니다. 구 버전의 액션이 어느 날 갑자기 작동하지 않게 되는 경우도 발생합니다. 주기적으로 Releases를 확인하거나, GitHub의 Dependabot 기능을 사용해 버전 업데이트 PR을 자동으로 생성하게 하면 안심할 수 있습니다.
문서를 GitHub로 관리하면 이력(History), 차이점(Diff), 리뷰, 자동 검사까지 모두 얻을 수 있습니다.
- Markdown으로 리포지토리에 저장
- PR로 리뷰 진행
- 형식과 프론트 매터는 GitHub Actions로 체크
- 내용은 AI와 사람이 리뷰
기계가 할 수 있는 것은 기계에 맡기고, 사람은 '무엇을 쓸 것인가'에 집중합니다. AI가 작성한 글이 늘어나는 지금이야말로 이러한 시스템화된 장치가 효과를 발휘할 때입니다.
본 기사는 Zenn에도 게시되었습니다.