GitHub Actions로 CI CD 파이프라인 구축하기
개요
개발 프로젝트를 진행하다 보면 CI/CD 파이프라인을 구축하는 일이 필수적이다. 최근 모노레포 구조의 프로젝트에서 GitHub Actions를 활용해 자동 배포 워크플로우를 설정하는 과정에서 몇 가지 트러블슈팅 경험이 있었다. npm에 패키지를 배포할 의도는 없었는데 GitHub Actions 워크플로우에서 자꾸 npm 배포 관련 오류가 발생했던 상황을 해결한 경험을 공유하고자 한다.
1. 문제 상황 파악
처음 맞닥뜨린 오류는 이랬다:
🦋 error an error occurred while publishing @common-ui/ui: ENEEDAUTH This command requires you to be logged in to https://registry.npmjs.org/
🦋 error You need to authorize this machine using `npm adduser`
Turborepo와 Changesets를 활용한 모노레포 구조에서 pnpm run release 명령이 자동으로 npm에 패키지를 배포하려고 시도하는 상황이었다. 근데 난 npm에 배포할 생각이 전혀 없었다. 그냥 코드 빌드하고 GitHub에 릴리스 태그만 만들고 싶었을 뿐인데...
2. NPM_TOKEN 설정 시도
처음에는 공식 문서대로 NPM_TOKEN을 발급받아 GitHub 시크릿에 등록했다. 그런데 이번엔 또 다른 오류가 튀어나왔다:
error an error occurred while publishing @common-ui/ui: E404 Not Found - PUT https://registry.npmjs.org/@common-ui%2fui - Not found
npm 스코프 접근 권한 문제였다. @common-ui라는 스코프를 사용하려면 해당 이름의 npm 사용자나 조직이 필요한데, 이런 걸 만들 생각도 없었다. 솔직히 나는 그냥 코드 빌드만 자동화하고 싶었을 뿐이었다.
3. 불필요한 npm 배포 단계 제거하기
문제의 근본적인 원인은 Turborepo 템플릿에 기본으로 포함된 changesets 배포 설정이 내 상황에는 필요 없었다는 점이다. 그래서 워크플로우에서 npm 배포 관련 설정을 모두 제거하고 필요한 단계만 남기기로 했다.
이전에는 다음과 같은 changesets 배포 설정이 있었다:
- name: Create Release PR or Publish
id: changesets
uses: changesets/action@v1
with:
publish: pnpm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}이를 단순히 빌드만 실행하도록 변경했다:
- name: Build project
run: pnpm run build
shell: bash4. GitHub 릴리스 태그 생성 및 권한 문제 해결
npm 배포 대신 GitHub 릴리스 태그를 자동으로 생성하는 단계를 추가했지만, 새로운 문제가 발생했다:
Error: Resource not accessible by integration
이는 GitHub Actions 워크플로우가 릴리스를 생성할 권한이 없다는 의미였다. 이를 해결하기 위해 워크플로우 파일에 권한 설정을 추가했다:
permissions:
contents: write5. 최종 워크플로우 구성
불필요한 부분을 제거하고 필요한 기능만 추가한 최종 워크플로우는 다음과 같다:
name: Release
on:
push:
branches:
- main # 운영환경
- develop # 개발환경
- 'release/**' # 릴리스 환경
workflow_dispatch: # 수동 실행
inputs:
branch:
description: '릴리스 대상 브랜치'
required: false
default: 'main'
# 동시 실행 방지 및 이전 실행 취소
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# 저장소 내용 쓰기 권한 부여
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
# 1. 저장소 체크아웃
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # 전체 커밋 이력 가져오기
# 2. pnpm 설정
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.15.6
# 3. Node.js 설정 (캐싱 포함)
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
# 4. 의존성 설치
- name: Install dependencies
run: pnpm install --frozen-lockfile
shell: bash
# 5. 프로젝트 빌드
- name: Build project
run: pnpm run build
shell: bash
# 6. feature 브랜치에서 develop으로 자동 PR 생성
- name: Create PR to develop from feature branch
if: startsWith(github.ref, 'refs/heads/feature/')
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: Auto PR from ${{ github.ref_name }}'
branch: 'auto/pr/${{ github.ref_name }}'
title: 'Auto PR: ${{ github.ref_name }} → develop'
body: '이 PR은 GitHub Actions에 의해 자동 생성되었습니다.'
base: 'develop'
# 7. main 브랜치일 경우 릴리스 브랜치 생성
- name: Create release branch from main
if: github.ref == 'refs/heads/main'
uses: peter-evans/create-branch@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: release/${{ github.run_number }}
source: main
# 8. GitHub 릴리스 생성
- name: Create GitHub Release
if: |
github.event_name == 'workflow_dispatch' ||
github.ref == 'refs/heads/main' ||
startsWith(github.ref, 'refs/heads/release/')
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.run_number }}
name: ${{ github.ref == 'refs/heads/main' && 'Release' || 'Pre-release' }} ${{ github.run_number }}
draft: false
prerelease: ${{ github.ref != 'refs/heads/main' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}이 워크플로우는 다음 기능을 수행한다:
- 코드 체크아웃과 빌드
- feature 브랜치에서 develop으로 자동 PR 생성
- main 브랜치에서 릴리스 브랜치 자동 생성
- GitHub 릴리스 및 태그 자동 생성
결론 및 교훈
이 경험을 통해 몇 가지 중요한 점을 깨달았다:
-
실제 필요한 것만 설정하자. npm에 배포할 계획이 없다면 해당 기능을 제거하고 필요한 부분만 구성하는 것이 좋다.
-
GitHub Actions의 권한 시스템을 이해하는 것이 중요하다. 워크플로우에서 릴리스나 브랜치를 생성하려면 명시적으로
contents: write권한이 필요하다. -
템플릿을 그대로 사용하지 말고 프로젝트에 맞게 조정하자. 보통 템플릿이나 공식 예제는 모든 상황을 포괄하기 위해 불필요하게 복잡한 경우가 많다.
-
빌드 파이프라인은 점진적으로 개선하자. 처음부터 완벽한 워크플로우를 만들기보다 기본 기능부터 시작해 필요에 따라 기능을 추가하는 것이 효율적이다.
결국, CI/CD 파이프라인은 프로젝트에 맞게 최적화되어야 한다. 내 프로젝트에 필요한 기능만 포함하고 불필요한 복잡성은 제거함으로써 유지보수하기 쉽고 효율적인 워크플로우를 구축할 수 있다.
이 트러블슈팅 경험이 비슷한 상황에 처한 다른 개발자들에게 도움이 되었으면 한다.
이 CI/CD 파이프라인이 적용된 모노레포 구조의 도입기는 [[KnowledgeBase/Blog/모노레포를 실무에 적용한 뒤 한달. 후기]]에서 확인할 수 있다.
댓글
첫 번째 댓글을 남겨보세요.