Tech
Unity 커스텀 패키지를 위한 CI/CD 워크플로우의 도입기 - 2부
2023. 11. 28


안녕하세요. 기술지원팀에서 DevOps 엔지니어로 일하고 있는 오평석입니다. 지난 아티클에서는 Unity 커스텀 패키지를 배포하기 위한 CI/CD 도구의 선택과 GitHub Actions에 대한 소개, 그리고 배포 워크플로를 실제로 어떻게 구성하였는지에 대해 알아보았습니다. 이번 아티클에서는 CI/CD 워크플로를 어떻게 개선하고 발전시켰는지에 대한 이야기를 드리도록 하겠습니다.

 


 

Unity 여러 버전에 대한 테스트

기술지원팀에서는 패키지의 신뢰성을 높이고 실제 기능이 올바르게 잘 동작하는지 확인하기 위해 테스트 코드를 작성하고 있습니다. 실제로 Unity 테스트 러너 프레임워크를 사용하여 코드를 작성하고 테스트를 하게 됩니다.

[TestCase(0, true)]
[TestCase(1, true)]
[TestCase(2, true)]
public IEnumerator TestProgress(int step, bool init)
{
    // ...
}
Unity 테스트 러너 프레임워크를 사용한 표본

 

코드의 변경 사항이 테스트 코드를 잘 통과하는지 코드 리뷰 시 확인해야 합니다. 리뷰어는 PR이 올라오면 해당 브랜치를 받고 Unity를 켠 후, 테스트 러너에 들어간 다음 테스트가 잘 통과하는지 체크해야 합니다. 하지만 모든 PR에 대해 테스트 통과 여부를 확인하는 건 비효율적인 작업일 수 있습니다. 따라서 자동화할 필요가 있었습니다. 

 


 

Unity 배치 모드

Unity에서는 커맨드 라인 명령어를 통해 Unity 에디터를 실행할 수 있는 기능을 제공합니다. 특히 -batchmode 인자를 전달하면 배치 모드로 실행되어, 특별한 상호 작용 없이 에디터를 통해 작업을 할 수 있습니다. 보통 Unity 프로젝트에서 빌드 CI를 붙이고 싶다면 빌드 과정을 진행하는 메서드를 Editor 어셈블리에 넣고 이 메서드를 배치 모드에서 호출하는 방법을 사용합니다.

Unity 테스트 러너 역시 비슷한 방법으로 진행할 수 있습니다. 사용법은 매우 간단한데, -runTests 옵션을 주면 끝입니다. 다음과 같이 입력하면 프로젝트 파일을 읽어 테스트를 돌린 후 xml 파일 형식으로 테스트 결과를 저장합니다.

Unity -runTests -batchmode -projectPath <path_to_your_proejct> -testResults <path_to_test_results>
-runTests 옵션을 부여한 Unity 테스트 러너 

 

여러 Unity 버전 세팅

패키지는 여러 Unity 버전을 지원해야 합니다. 이를 위해서는 여러 Unity 버전이 서로 충돌 없이 러너 환경에 세팅돼야 합니다. Unity Hub을 이용하여 Unity 버전이 관리되기 때문에 설치 자체는 기술적으로 어려움이 없지만, CI 머신은 일관된 환경을 유지하는 것이 중요합니다.

워크플로 단계에서 Unity 환경을 세팅하는 스텝을 추가하는 것은 완벽한 해결책은 아닙니다. 특정 워크플로에서 러너 환경을 변경하는 것은 다른 워크플로를 깨뜨리는 원인이 될 수 있기 때문입니다. 그렇다고 새로운 Unity 버전이 필요할 때마다 매번 러너에 접속해 설치를 하는 것도 좋은 해결책은 아닙니다. 이럴 때는 Unity 에디터를 컨테이너로 돌릴 수 있도록 이미지로 패키징하고, 러너에서는 이미지로부터 컨테이너를 생성하여 CI를 처리하면 됩니다.

소프트웨어 플랫폼 Docker (이미지 출처: docker.com)

 

필요한 환경 세팅은 이미지 안에 이미 다 있으니 추가적인 러너 세팅이 필요 없습니다. 그리고 Docker Hub에서 찾아보면 GameCI라는 커뮤니티에 의해 유지 보수되는 Unity 에디터 이미지가 있으므로, 이를 그대로 사용하였습니다.

 


 

워크플로 반영

GameCI에서 Unity 에디터 이미지를 이용하여 테스트 러너를 돌리는 액션을 제공하고 있었기에, 문서를 잘 읽으면서 워크플로를 작성했습니다. 이때 각 Unity 버전에 따라 Job이 실행되도록 GitHub Actions에서 제공하는 matrix 기능을 사용하고 있습니다.

- name: Checkout the repository
  uses: actions/checkout@v3
- name: Get runner's uid and gid
  id: runner-info
  run: |
    echo "::set-output name=uid::$(id -u)"
    echo "::set-output name=gid::$(id -g)"
- name: Run unity test runner
  uses: game-ci/unity-test-runner@v2.0.3
  id: tests
  env:
    UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
    UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
    UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
  with:
    unityVersion: ${{ matrix.unityVersion }}
    testMode: all
    chownFilesTo: ${{ steps.runner-info.outputs.uid }}:${{ steps.runner-info.outputs.gid }}
    githubToken: ${{ secrets.GITHUB_TOKEN }}
matrix 기능을 사용한 표본

 

여기까지 진행하면 이제 PR이 날아올 때 워크플로가 트리거 되어 자동으로 Unity 테스트 러너를 실행하고, 그 결과를 PR 페이지에서 확인할 수 있습니다. 테스트 코드 자체가 잘 설계되었는지 확인하는 것은 리뷰어의 몫이지만, 더 이상 리뷰어가 테스트의 성공 여부를 일일이 체크하는 건 하지 않아도 되는 것이죠.

PR 페이지에서 확인되는 테스트 코드 통과 여부

 


 

문서 빌드 및 배포

패키지는 각 스튜디오에서 쉽게 사용할 수 있도록 문서화되어야 합니다. 기술지원팀에서는 Unity 커스텀 패키지 구조에 맞춰 Documentation~ 디렉터리에 마크다운 타입으로 작성한 문서를 같이 제공하고 있습니다. 또한 C# 에서는 다음과 같이 클래스, 메서드 등에 XML 태그를 달아두면 IDE에서 제공하는 IntelliSense의 도움을 받아 이를 확인할 수 있습니다.

/// <summary>
/// 글로벌 채팅 생성
/// </summary>
/// <param name="nickname">닉네임</param>
/// <param name="serverID">서버 ID</param>
/// <returns>IChatGlobal instance (null 체크 필요)</returns>
[CanBeNull]
public static IChatGlobal CreateGlobalChat(string nickname, string serverID)
{
    // ...
}
패키지 구조 맞춤형 마크다운 타입 표본

 

하지만 문서를 웹페이지 형식으로 제공하는 것이 가독성 및 접근성에서 더 좋을 거 같다는 의견이 있었습니다. 이에 따라 API 문서를 어떻게 빌드하고 배포할 것인가에 대한 고민을 시작했습니다. 

 


 

문서 빌드 시 사용할 도구 선택

먼저 어떤 도구를 사용하여 문서를 빌드 할 것인가 결정해야 했습니다. 다음은 API 문서를 빌드 하는 도구를 선택할 때 고려한 조건들입니다.

  • C# 코드에 있는 XML 주석으로부터 API 문서를 만들 수 있어야 한다.

  • 마크다운으로 작성한 파일을 웹에서 쉽게 볼 수 있도록 정적 사이트를 생성할 수 있어야 한다.

  • 프로젝트 내 추가적인 종속성 세팅 없이 문서를 빌드할 수 있으면 좋다.

 

이러한 조건을 만족하는 API 문서 빌드 도구 중 마이크로소프트에서 제공하는 문서 생성기인 DocFX를 선택했습니다. 마이크로소프트에서 제공하는 만큼 당연하게도 C#을 지원하며, 정적 사이트를 생성할 수 있고, .csproj 파일 내에 추가적인 세팅 없이 바이너리 만으로 문서를 빌드 할 수 있기 때문입니다.

 

DocFX를 이용한 빌드 문서 결과

 

결과를 확인하니, Unity 패키지와 동일한 양식이었습니다. 이 문서를 공유하자마자 팀원들도 같은 반응을 보였습니다. 실제로 Unity 공식 패키지 문서에서도 ‘Generated by DocFX’와 같이 명시돼 있어, Unity 에서도 패키지 문서 작성 시 DocFX를 사용하고 있다는 것을 확인했습니다. 이제 어떤 도구를 선택할지 결정했으므로, 문서 빌드 및 배포 작업에 돌입했습니다. 

 


 

DocFX를 활용한 문서 빌드 및 배포

DocFX를 사용해 문서를 빌드하는 방법은 4단계로 나뉩니다.

  • C# 코드 및 마크다운 타입의 문서를 작성합니다.

  • TOC(Table of Contents) 파일을 준비하여 목차를 구성합니다.

  • DocFX 문서를 잘 보고 docfx.json 파일을 준비합니다.

  • 문서 디렉토리 루트에서 DocFX를 실행하면 _site 디렉토리에 빌드 결과물이 나옵니다.

 

웹 사이트 호스팅은 Amazon S3를 이용하여 진행하므로, DocFX를 이용하여 빌드 한 결과물을 S3 버킷에 업로드하면 문서가 배포됩니다. 하지만 매번 로컬에서 직접 빌드를 한 후 업로드를 하는 건 비효율적인 작업이기에 개선할 필요가 있었습니다. 패키지를 개발하는 사람 입장에서는 마크다운 문서 작성 후 코드 내에 주석을 담고, 배포 문서도 CI를 통해 자동으로 빌드 및 배포가 되도록 수정했습니다.

- name: Build docs
  working-directory: Assets/Package/Documentation~
  run: |
    curl -L -o docfx-v2.59.2.zip https://github.com/dotnet/docfx/releases/download/v2.59.2/docfx.zip
    unzip -d bin docfx-v2.59.2.zip
    chmod +x bin/docfx.exe
    bin/docfx.exe docfx.json
- name: Deploy docs
  working-directory: Assets/Package/Documentation~
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  run: |
    aws s3 sync _site s3://<path_to_s3_bucket>
개선된 문서 빌드 및 배포 워크플로우 표본

 


 

문서 미리 보기 자동화

문서 역시 코드 리뷰의 대상이므로, 문서가 바뀌었을 때는 이를 리뷰어가 체크해야 합니다. 하지만 로컬에서 빌드 해 체크하지 않아도 됩니다. 문서 배포는 S3 버킷을 이용하므로, SHA 해시 값과 같이 고유한 문자열을 붙여 업로드를 해두면 쉽게 웹 페이지에서 문서 미리 보기를 확인할 수 있습니다.

다만 리뷰어의 입장에서 문서 미리 보기 링크를 얻기 위해 워크플로 전체 로그를 살펴보거나 SHA 해시 값을 조합하고 싶지는 않을 것입니다. 문서 미리 보기 링크를 바로 확인할 수 있다면 좋을 것 같았고, 이를 위해 GitHub Actions에서 제공하는 워크플로 런타임 때 요약 문구를 생성할 수 있는 기능을 사용했습니다. GITHUB_STEP_SUMMARY 변수에 문서 미리 보기 링크를 추가해두면, Job Summary 탭에서 쉽게 링크를 확인할 수 있습니다.

- name: Build docs
  working-directory: Assets/Package/Documentation~
  run: |
    curl -L -o docfx-v2.59.2.zip https://github.com/dotnet/docfx/releases/download/v2.59.2/docfx.zip
    unzip -d bin docfx-v2.59.2.zip
    chmod +x bin/docfx.exe
    bin/docfx.exe docfx.json
- name: Deploy docs
  working-directory: Assets/Package/Documentation~
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  run: |
    aws s3 sync _site s3://<path_to_s3_bucket>
GITHUB_STEP_SUMMARY에 미리보기 링크를 추가한 표본

 

Job Summary에서 링크 확인 가능한 결과

 


 

Changelog 생성 자동화

Changelog는 프로젝트의 변경 사항을 기록한 것을 의미합니다. 기술지원팀에서 제공하는 패키지에서도 최상단에 CHANGELOG.md 파일로 제공을 하고 있습니다. Changelog 작성은 중요한 일이지만, 작성을  미루다 보면 커밋 히스토리를 헤매게 되는 상황이 생기기도 합니다.

 

커밋 히스토리를 헤매게 되는 상황

 

Changelog 생성을 자동화하는 방법 중 일반적인 방식에는 2가지가 있습니다.

  • 커밋 메시지에 컨벤션을 도입하고 메시지로부터 Changelog를 작성

  • PR 제목 및 메시지로부터 Changelog를 작성.

 

기술지원팀에서는 커밋 메시지를 잘 작성하는 것이 중요하다고 생각해, 첫번째 방법을 선택했습니다. 그리고 커밋으로부터 Changelog를 생성하는 도구로는 git-chglog를 채택했습니다. git-chglog 는 Docker 이미지를 제공하므로, Docker 명령어를 이용하여 손쉽게 Changelog 를 생성할 수 있습니다.

- name: Update changelog
  working-directory: ${{ github.workspace }}
  run: |
    docker run --rm -v "$PWD":/workdir quay.io/git-chglog/git-chglog \
      --next-tag ${{ inputs.tag }} \
      --tag-filter-pattern "^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$" \
      -o ./Assets/Package/CHANGELOG.md
Docker 명령어를 이용한 Changelog 생성 자동화 표본

 

자동으로 생성된 Changelog는 커밋 메시지로부터 생성하고 있으므로, 메시지를 명확하게 작성해야 양질의 Changelog를 얻을 수 있습니다. 여기에 DocFX 설정을 살짝 수정하면 다음과 같이 문서 페이지에서도 Changelog를 확인할 수 있습니다.

## [0.5.1] - 2022-08-18
### Bug Fixes
- 패키지 배포 시 사용하는 메타데이터의 .meta 파일 충돌 해결
- preview 버전은 changelog 에 나오지 않도록 수정
### CI Configurations
- Docs 프리뷰에서도 Changelog 를 확인할 수 있도록 수정
## [0.5.0] - 2022-07-21
### CI Configurations
- Changelog 자동화 반영
### Features
- `git-chglog` 도입

자동으로 생성된 Changelog(위)와 문서화 한 Changelog(아래)

 


 

마치며

지금까지 패키지 배포를 위한 CI/CD 워크플로우의 도입과 구성 요소, 고민과 개선 과정을 소개했습니다. 휴먼 에러를 줄이기 위해 도입한 CI/CD 워크플로우는 50여 줄의 YAML 파일에서 시작해, 지금은 업무의 많은 부분을 자동화하기에 이르렀습니다. 테스트와 문서 빌드, Changelog 작성 등 많은 부분이 CI/CD 워크플로를 통해 더 효율적으로 바뀌었습니다.

저희가 채택한 GitHub Actions의 가장 큰 장점은 원하는 GitHub 이벤트에 맞춰 자유롭게 워크플로를 구성할 수 있다는 점입니다. GitHub Actions를 사용하여 각자 원하는 CI/CD 워크플로를 리포지토리에 추가해보길 추천합니다!