PlantUML 다이어그램을 CI/CD 파이프라인에 물리기

PlantUML 렌더링을 GitHub Actions/GitLab CI와 MkDocs 빌드에 통합해, 다이어그램과 문서를 코드 변경과 함께 자동 배포하는 파이프라인 구성법을 정리한다.

2026-08-12 · 최초 발행 2025-10-31

코드와 함께 움직이지 않는 문서는 결국 낡는다

개발 산출물과 문서가 따로 놀면 어느 순간 둘 중 하나는 거짓말을 하게 된다. PlantUML로 UML/아키텍처 다이어그램을 텍스트 DSL로 관리하면 형상관리와 코드 리뷰에 그대로 올라탈 수 있고, 여기에 Docs-as-Code 접근 — 문서를 코드 리포지토리에 두고 빌드·테스트·배포를 자동화하는 방식 — 을 얹으면 변경 이력 추적과 PR 게이트까지 확보된다. 소스 변경이 다이어그램 렌더링 → 문서 빌드 → 정적 사이트/아티팩트 배포로 이어지는 흐름을 CI/CD 파이프라인으로 굳히는 게 이 글의 목표다.

파이프라인을 구성하는 요소

  • 리포지토리 구조 표준화: 소스, 다이어그램, 문서를 분리한다. 예를 들어 src/, uml/, docs/, mkdocs.yml처럼 나누고, 이미지 산출물 경로(docs/images/*.png)도 일관되게 관리한다.
  • 렌더링 엔진 선택: 로컬 CLI/JAR나 Docker 기반 렌더링이 기본이고, 원격 렌더링(PlantUML Server/Kroki)을 쓸 거라면 보안·가용성을 먼저 따진다.
  • CI 오케스트레이션: GitHub Actions, GitLab CI, Jenkins 어디서든 파이프라인을 정의할 수 있다. 캐시·병렬 처리·조건부 배포로 실행 시간을 줄인다.
  • 문서 빌드 시스템: MkDocs, Sphinx, Docusaurus 중 하나를 정적 사이트 생성기로 쓴다. 공통 테마·템플릿·버전 태깅이 재현성을 만든다.
  • 품질 게이트: 링크 체크, 스펠 체크, UML Lint로 실패 지점을 조기에 잡는다. main 브랜치 병합 시 자동 배포하고, PR에는 프리뷰를 띄운다.

파이프라인 흐름

fail fasterrorwarningspassfailDeveloper Commit/PRCI TriggerDocs/UML Lint & Link checkRender PlantUML(CLI/Docker/Server)Build Docs (MkDocs)Publish(Pages/S3/Artifacts)Status/Preview URLBlock PRKroki/PlantUML ServerFallbackQuality Gate?Fix & Retry

Lint 단계에서 실패하면 곧바로 PR을 막고, 렌더링이 실패하면 Kroki/PlantUML Server로 폴백한 뒤 빌드를 이어간다. 품질 게이트를 통과하지 못하면 배포 대신 수정·재시도로 돌아간다.

리포지토리 구조부터 잡기

uml/.puml 원본을, docs/에 Markdown 문서와 이미지 출력을, mkdocs.yml에 사이트 설정을, requirements.txt에 빌드 의존성을 둔다.

repo-root/
├─ uml/
│  ├─ context.puml
│  └─ components/
│     └─ service-A.puml
├─ docs/
│  ├─ index.md
│  ├─ architecture.md
│  └─ images/        # 렌더 출력
├─ mkdocs.yml
└─ requirements.txt

전제조건은 Python 3.11 이상과 MkDocs 1.5+(mkdocs-material 권장), 그리고 Docker 24+ 또는 Java 17+ + Graphviz(로컬 렌더링 시)다. 네트워크가 격리된 환경이라면 사내 PlantUML Server나 Kroki를 따로 설치하는 편이 낫다.

GitHub Actions로 구성하기

docs/, uml/, mkdocs.yml이 바뀔 때 트리거되도록 하고, Docker 기반 로컬 렌더링 → MkDocs 빌드 → GitHub Pages 배포 순으로 진행한다. 렌더링이 실패하면 빌드를 중단하고, 서버 렌더링으로 대체할 수 있는 옵션도 열어둔다.

name: docs-pipeline

on:
  push:
    paths:
      - "docs/**"
      - "uml/**"
      - "mkdocs.yml"
      - "requirements.txt"
  pull_request:
    paths:
      - "docs/**"
      - "uml/**"
      - "mkdocs.yml"

jobs:
  build-docs:
    runs-on: ubuntu-latest
    env:
      RENDER_MODE: local # local | server
      KROKI_URL: ${{ vars.KROKI_URL }} # 예: https://kroki.example.com
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Prepare output dir
        run: mkdir -p docs/images

      - name: Render PlantUML (local via Docker)
        if: env.RENDER_MODE == 'local'
        run: |
          set -e
          files=$(git ls-files 'uml/**/*.puml' || true)
          if [ -n "$files" ]; then
            docker run --rm -v "$PWD":/data plantuml/plantuml:1.2024.7 \
              -tpng -o docs/images $files
          fi

      - name: Render PlantUML via server (fallback)
        if: env.RENDER_MODE == 'server'
        run: |
          set -e
          : "${KROKI_URL:?KROKI_URL not set}"
          for f in $(git ls-files 'uml/**/*.puml'); do
            out="docs/images/$(basename "${f%.*}").png"
            curl -fsS -H 'Content-Type: text/plain' \
              --data-binary @"$f" "$KROKI_URL/plantuml/png" -o "$out"
          done

      - name: Lint links
        run: |
          pip install linkchecker
          linkchecker --config /dev/null docs || true  # 경고 허용, 실패 전환은 품질 정책에 맞춤

      - name: Build MkDocs
        run: mkdocs build --clean

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: site

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: build-docs
    permissions:
      pages: write
      id-token: write
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4

GitLab CI로 구성한다면

동일한 3단계(render → build → deploy)를 GitLab CI로 옮기면 이렇게 짧아진다.

stages: [render, build, deploy]

render:
  image: plantuml/plantuml:1.2024.7
  script:
    - mkdir -p docs/images
    - plantuml -tpng -o docs/images $(git ls-files 'uml/**/*.puml')
  artifacts:
    paths: [docs/images]
  rules:
    - changes:
        - docs/**/*
        - uml/**/*

build:
  image: python:3.11-slim
  needs: [render]
  script:
    - pip install -r requirements.txt
    - mkdocs build --clean
  artifacts:
    paths: [site]

deploy:
  stage: deploy
  environment: production
  script:
    - rsync -avz site/ user@server:/var/www/docs/
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

MkDocs 설정과 의존성 고정

mkdocs==1.6.0
mkdocs-material==9.5.27
pymdown-extensions==10.8.1
site_name: Team Docs
theme:
  name: material
nav:
  - Home: index.md
  - Architecture: architecture.md
plugins:
  - search
markdown_extensions:
  - admonition
  - pymdownx.superfences
extra:
  social: []

문서 안에서는 렌더링된 이미지를 그냥 참조하면 된다.

# 시스템 아키텍처

![Context](images/context.png)

- 컴포넌트 다이어그램 참조: images/service-A.png

입력 → 처리 → 출력, 그리고 실패 처리

입력은 .puml 원본, Markdown, mkdocs.yml, requirements.txt다. 처리는 UML 렌더링(Docker/Server) → 링크/문서 Lint → MkDocs 빌드 순으로 이어진다. 출력은 정적 사이트(site/)이고, Pages·S3 등으로 배포한다.

에러 처리는 세 갈래로 나뉜다. 렌더링이 실패하면 빌드를 실패 처리하되 필요하면 서버 렌더링으로 폴백한다. 외부 렌더 서버 장애 시에는 재시도 백오프와 사내 캐시/미러를 구성해둔다. 링크 체크 경고는 PR 상태 체크로 가시화하고, 임계치를 넘으면 실패로 전환한다.

운영·보안에서 흔히 놓치는 것들

네트워크·보안 관점에서는 외부 SaaS 렌더링을 막아야 하는 환경이라면 사내 PlantUML Server나 Kroki 컨테이너를 운영하는 편이 낫고, 비밀정보가 담긴 도면은 외부 전송을 금지하고 로컬 렌더링 정책을 강제한다.

성능·확장성은 캐시(pip, Docker layer)를 쓰고 변경된 파일만 렌더링하는 전략으로 확보한다. 대형 도면을 병렬 렌더링할 때는 러너 병렬도와 CPU 제한도 함께 봐야 한다.

운영 편의·신뢰성은 다이어그램 규칙(UML Lint)과 템플릿으로 표준화하고, 실패 시 PR 코멘트나 Slack 알림, Preview URL 제공으로 대응한다.

트레이드오프도 분명하다. 로컬 렌더링은 보안·일관성이 좋은 대신 초기 실행 비용이 들고, 서버 렌더링은 속도·확장에 유리한 대신 가용성과 네트워크 의존성이 늘어난다.

실제로 쓰이는 곳

마이크로서비스 아키텍처 문서화에서는 서비스/의존성 다이어그램을 PR마다 갱신해 운영 포털에 자동 배포한다. 규제·감사 대응에서는 버전별 문서·도면 스냅샷을 보관해 변경 이력 추적성을 확보한다. 온보딩 가속화 측면에서는 시스템 구성도와 운영 절차 문서를 최신 상태로 유지해 신규 인력 학습 시간을 줄인다.

렌더링 전략 비교

전략 성능 확장성 일관성 안정성 운영 편의
로컬 Docker/CLI 초기 약간 느림, 이후 캐시로 안정적 러너 스케일 아웃으로 수평 확장 빌드마다 동일 환경 보장 외부 의존성 최소, 안정성 높음 Docker 필요, 세팅 단순
사내 PlantUML Server 빠른 처리, 중앙 캐시 효과 서버 수평 확장 가능 서버 버전 고정 시 높음 서버 가용성에 종속 운영·모니터링 필요
외부 Kroki/PlantUML SaaS 대체로 빠름 서비스 SLA 의존 서비스 버전 종속 가능 네트워크/서비스 장애 리스크 운영 부담 최소, 보안 검토 필요

효과는 리드타임에서 가장 먼저 드러난다. 문서 갱신 리드타임이 60% 이상 단축되고 수동 렌더링·배포 작업이 사라진다. 링크·구조 오류를 조기에 잡아내면서 배포 실패율도 30% 줄어든다. 여기에 더해 문서-코드 일관성이 올라가고 리뷰 품질이 좋아지며, 표준화된 다이어그램 스타일 덕분에 커뮤니케이션 비용도 줄어든다.

PlantUMLCI/CDGitHub ActionsMkDocs문서 자동화