GitHub Pages를 활용한 Spring REST Docs 배포 자동화

임동혁 Ldhbenecia·2025년 7월 29일

SpringBoot

목록 보기
21/28
post-thumbnail

개요

Sent 프로젝트를 개발하면서 클라이언트가 직접 API 명세서를 확인할 수 있도록 문서 제공 환경을 구축할 필요가 생겼다. 초기에는 기존 개발 서버에 명세서를 함께 제공하려 했지만, 최종적으로는 GitHub Pages를 활용한 별도 배포 방식으로 결정하게 되었다.

이 글에서는 왜 GitHub Pages를 선택했는지, CI/CD 구성은 어떻게 했는지를 정리한다.

초기 시도: Spring Boot 내부 또는 정적 파일 복사

1. 정적 리소스로 포함

  • ./gradlew asciidoctor 실행 시 생성되는 HTML 파일을 resources/static/docs 경로에 복사
  • 결과적으로 /docs/index.html로 접근 가능

문제점

  • CI/CD 빌드 시간 증가: 매번 asciidoctor 태스크 실행 필요 (1~2분 소요)
  • 문서 변경이 없어도 항상 실행됨
  • HTML 용량이 늘어나면서 JAR 내부에서 차지하는 비중이 커짐 (41% 이상)
    • Repository의 사용한 Language에서 HTML이 41% 정도를 차지하길래 미감적으로도 마음에 안들어서 포기

2. 수동 복사

  • 문서가 변경될 때마다 수동으로 복사해서 커밋
    • 명세서가 바뀔 때마다 일일히 복사해주는 과정도 그다지 하고 싶지 않았음

문제점

  • 수동 작업 부담
  • 변경 추적이 어렵고 실수 발생 가능성

3. Nginx로 별도 서빙

  • build/docs/asciidoc 결과물을 Nginx 설정을 통해 /docs 경로로 서빙

문제점

  • 서버 환경 설정 필요
  • 도입 비용 대비 복잡도가 높아보여 보류
  • 이 방식을 사용할 수도 있지만 Nginx 설정을 추가적으로 건드리고 싶지는 않았음
    좋은 방법이지만 이번에는 이 방식을 이용하고 싶지 않았음. 순전히 본인 생각

최종 선택: GitHub Pages로 문서 호스팅

개발 서버와는 분리된 환경에서 문서를 배포할 수 있고, 정적 호스팅에 최적화된 GitHub Pages가 가장 적절해 보였다.

장점

  • 독립적인 정적 문서 배포 가능
  • 브랜치/파일 변경 조건을 통해 워크플로우 최적화 가능
  • 서버 설정 없이 간편하게 접근 가능

기껏 구축해놓은 개발서버를 사용하지 않고 개발서버와 별개로 Github Page를 사용하기로 했다.

docs.yml을 통해 파이프라인을 따로 만들어서 개발 서버에 띄우는 방법은 왜 사용하지 않았을까?

“서비스 코드가 배포된 후 최신 코드로 문서를 빌드해야 한다”는 관점에서는 deploy.yml → docs.yml 순서가 필요하다.

왜 이런 순서가 필요할까?

  • deploy.yml: 실제 서비스 코드, API, DB 등 모든 변경사항을 서버에 Docker 기반으로 배포
  • docs.yml: 최신 코드(=배포된 코드) 기준으로 명세서를 빌드/배포

만약 docs.yml이 먼저 실행되면?

  • 아직 배포되지 않은(구버전) 코드 기준으로 명세서가 만들어질 수 있음

만약 deploy.yml이 먼저 실행되면?

  • 실제 서비스가 최신 코드로 배포된 후,
    그 코드 기준으로 명세서가 빌드/배포됨 → API와 문서가 동기화

위와 같이 생각을 하고 지나갔으나, 파이프라인을 짜면서 커밋을 기점으로 코드를 가지고 왔기 때문에 이미 최신화 된 코드를 가지고 ./gradlew asciidoctor 명령어를 사용해서 최신 문서화를 뽑아낸다는 것을 다시금 깨달았다.

하지만 이래도 docs.yml을 통해 개발 서버에 띄우는 방법을 사용하지 않는 이유에 대해 설명한다.

현재 개발 서버는 Github Actions를 통하여 Docker 기반으로 배포를 진행하고 있다.
개발 서버에서 docs.yml을 통해서 명세서를 제공하는 방법은 위에서 말한 두 가지를 다시금 충족한다.

  1. Nginx를 사용하여 서빙을 한다.
    1. docs.yml을 한 다음에 결국 Nginx 서빙까지 처리해야한다.
  2. docs.yml을 통해 ./gradlew asciidoctor 명령어만 deploy.yml에서 빼서 돌리더라도 Nginx를 사용하지 않을 거라면 이 명령어로 만들어진 build/docs에 있는 파일을 static/resources로 다시 옮긴 다음에 이미지를 말아서 다시 스프링부트를 띄우면 :8080/docs/index.html에서 볼 수 있을 것이다.

GitHub Actions 기반 문서 배포 파이프라인 구성

name: Spring Rest Docs

on:
  push:
    branches:
      - develop
  workflow_dispatch:

concurrency:
  group: github-pages
  cancel-in-progress: true

jobs:
  deploy:
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Pages
        uses: actions/configure-pages@v5

      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          distribution: "temurin"
          java-version: "21"
          cache: gradle

      - name: Build Asciidoc
        run: ./gradlew asciidoctor

      - name: Upload pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: "./build/docs/asciidoc"

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

concurrency:
  group: github-pages
  cancel-in-progress: true

동시 실행 제어
같은 그룹의 워크플로우가 여러 번 실행될 때 이전 실행을 취소하고 최신만 유지


jobs:
  deploy:
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
    runs-on: ubuntu-latest

배포 작업 정의
permissions: GitHub Pages 배포에 필요한 권한 부여
environment: github-pages 환경 사용
runs-on: Ubuntu 최신 러너에서 실행


steps:
  - name: Checkout
    uses: actions/checkout@v4

코드 체크아웃: 현재 커밋의 코드를 빌드 서버로 가져옴


  - name: Set up Pages
    uses: actions/configure-pages@v5

GitHub Pages 환경 설정: Pages 배포에 필요한 환경 준비


  - name: Set up JDK 21
    uses: actions/setup-java@v4
    with:
      distribution: "temurin"
      java-version: "21"
      cache: gradle

JDK 21 설치: Gradle 빌드에 필요한 Java 21 환경 준비
cache: gradle: Gradle 의존성 캐시 활용


  - name: Build Asciidoc
    run: ./gradlew asciidoctor

명세서 빌드: asciidoctor로 API 문서 생성


  - name: Upload pages artifact
    uses: actions/upload-pages-artifact@v3
    with:
      path: "./build/docs/asciidoc"

빌드된 HTML을 Pages 아티팩트로 업로드 이후 step에서 GitHub Pages로 배포할 수 있도록 준비


  - name: Deploy to GitHub Pages
    id: deployment
    uses: actions/deploy-pages@v4

GitHub Pages로 실제 배포
업로드된 HTML 파일을 Pages 사이트로 공개

결론적으로 이러한 yml 파일을 작성하여, 배포할 때 분리해서 실행한다.

명세서가 바뀌지 않아도 매번 빌드하는 것은 마음에 들지 않다.

  • paths 옵션으로 워크플로우 최적화
paths:
  - 'api/src/docs/asciidoc/**'
  - '.github/workflows/docs.yml'

나는 추가로 이러한 설정을 해주었다.

paths 옵션을 추가하면 develop 브랜치에서 명세서 소스 (api/src/docs/asciidoc/**)나 docs.yml이 변경될 때만 워크플로우가 실행된다.

명세서가 바뀌지 않으면 워크플로우가 실행되지 않아 효율적이다.


결론적으로 개발서버에 API 명세서를 빌드하지 않고, Github Pages를 통해서 처음으로 API 명세서를 배포하였다.
스웨거를 사용할까도 하였지만 스웨거를 여러 번 사용해본 경험자로서 이번 프로젝트에서는 코드 품질에 대해 신경을 좀 더 쓰면서 개발을 진행하고 있기에 컨트롤러단을 더럽히고 싶지 않아서 Spring Rest Docs를 사용하고 있다.

테스트 코드를 작성하는 습관이 들어있지않고 아직도 너무나 어렵지만 AI의 도움덕에 테스트코드 작성 난이도도 굉장히 내려갔고, AI의 도움을 받더라도 작성을 아예 하지 않는 것보다는 Spring Rest Docs는 테스트코드 강제성을 부여한다는 점에서 도움이 많이 되고 있다.

개발단에서의 인프라 구축이 모두 끝나서 홀가분하다.


참고: https://han-joon-hyeok.github.io/posts/java-spring-rest-docs-with-deploying-github-pages/#github-actions-%EC%84%A4%EC%A0%95

0개의 댓글