이번 글에서는 GitHub Actions와 Vercel을 사용하여 CI/CD 자동 배포를 설정했으나, CI 과정에서 실패했다는 오류를 해결하는 과정에 대해 작성했습니다.
에러를 해결하기 전에, CI/CD가 무엇이며 이를 왜 프로젝트(Receito, 모임 정산 서비스)에 도입하게 되었는지 설명하겠습니다.
CI/CD는 지속적 통합(Continuous Integration, CI)과 지속적 전달/배포(Continuous Delivery/Deployment, CD)를 말합니다. 이 방법론은 소프트웨어 개발과 배포를 자동화하여 효율성과 품질을 높입니다.
여러 개발자가 작업한 코드를 중앙 저장소에 자주 통합합니다. 코드가 추가될 때마다 자동으로 빌드와 테스트가 실행되어, 변경 사항이 기존 코드에 문제를 일으키지는 않는지 신속히 확인합니다. 이를 통해 버그를 조기에 발견하고, 개발자 간의 충돌 문제를 줄입니다.
지속적 전달은 CI 후 테스트를 통과한 코드를 운영 환경에 배포할 준비를 하는 단계입니다. 배포는 수동으로 결정할 수 있습니다.
지속적 배포는 테스트를 통과한 코드를 자동으로 운영 환경에 배포합니다. 개발자가 코드를 올리면 즉시 사용자에게 전달됩니다.
혼자 진행하는 프로젝트였고 테스트 코드는 없었지만, 빌드 과정의 오류를 빠르게 확인하고 싶었습니다.
과거에는 로컬 환경에서만 테스트하면서 모든 기능을 구현한 후 운영 환경에 배포하려고 했습니다. 하지만 이 과정에서 디자인이 깨지거나 예상치 못한 에러가 발생해, 마무리를 급하게 처리해야 하는 상황이 자주 있었습니다.
그래서 이번에는 초기부터 운영 환경에 배포하면서 문제를 조기에 발견하고, 빠르게 수정하기 위해 CI/CD를 도입했습니다.
이를 통해 운영 환경에서도 안정적으로 작동하는 애플리케이션을 만들고자 했습니다.
Receipto 프로젝트에 CI/CD를 설정하기 위해 GitHub Actions와 Vercel을 사용했습니다.
GitHub Actions? GitHub 저장소와 연동하여 빌드, 테스트 및 배포 파이프라인을 자동화할 수 있는 CI/CD(연속 통합 및 지속적인 업데이트) 플랫폼입니다. 리포지토리에 변경 내용을 푸시할 때마다 테스트를 실행하거나 병합된 pull request를 프로덕션에 배포하는 워크플로를 만들 수 있습니다.
Vercel? 웹사이트나 웹 애플리케이션을 빠르고 쉽게 배포할 수 있도록 도와주는 플랫폼입니다. 코드를 깃허브(GitHub) 등 버전 관리 저장소에 올리면, Vercel이 자동으로 코드를 가져와 빌드하고, 전 세계 어디서든 접속 가능한 URL로 배포해줍니다. CI/CD(지속적 통합/배포) 기능이 내장되어 있어, 코드에 변경이 생길 때마다 자동으로 테스트와 배포가 이루어지기 때문에 초보자도 복잡한 설정 없이 손쉽게 최신 버전을 공개할 수 있습니다.
특정 브랜치에서 CI를 수행하도록 워크플로 파일을 설정했습니다. 하지만 프로젝트의 요구사항을 구현하며 반복적으로 커밋과 푸시를 하는 동안, CI가 빌드 단계에서 실패했다는 알림이 뒤늦게 온 것을 발견했습니다. CI 설정 후에 방치해 둔 결과였습니다…
npm ci
에러GitHub에서 로그를 다운로드 받아 분석했습니다.
다운로드 방법:
GitHub Repository > Actions > 에러가 표시된 워크플로우 클릭 > 에러가 발생한 Job 클릭 > 설정 아이콘(톱니바퀴 모양) 클릭 > Download log archive 클릭하여 다운로드)
로그를 확인해 보니 다음과 같은 에러가 있었습니다:
2025-06-18T03:41:47.5441363Z ##[group]Run npm ci
2025-06-18T03:41:47.5441734Z [36;1mnpm ci[0m
2025-06-18T03:41:47.5543650Z shell: /usr/bin/bash -e {0}
2025-06-18T03:41:47.5543983Z ##[endgroup]
2025-06-18T03:41:48.0225178Z npm error code EUSAGE
2025-06-18T03:41:48.0225906Z npm error
2025-06-18T03:41:48.0226961Z npm error The `npm ci` command can only install with an existing package-lock.json or
2025-06-18T03:41:48.0228346Z npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
2025-06-18T03:41:48.0229883Z npm error later to generate a package-lock.json file, then try again.
...
이 에러는 package-lock.json
파일이 없기 때문에 발생했습니다.
생각해보니, 제 리포지토리는 Bun 환경을 사용하고 있어서 package-lock.json
대신 bun.lock
파일만 존재합니다. 그래서 CI 워크플로 파일에 문제가 있을 것이라고 직감했습니다.
워크플로 파일? GitHub Actions에서 자동화된 작업(워크플로우)을 정의하는 설정 파일입니다. 코드가 깃허브에 올라갈 때마다 자동으로 빌드, 테스트, 배포 같은 작업을 순서대로 실행할 수 있습니다.
쉽게 말해, 워크플로 파일에 "어떤 상황에서(예: push), 어떤 작업을(예: 배포) 어떻게 할지"를 적어두면, GitHub가 알아서 그 과정을 자동으로 처리해주는 역할을 합니다
GitHub Actions는 프로젝트 루트의 .github/workflows
폴더에 있는 워크플로 파일을 자동으로 감지해 CI/CD를 수행합니다. 저도 CI 과정을 자동화하기 위해 이 폴더 안에 ci.yml
이라는 이름의 워크플로 파일을 만들어두었습니다.
해당 파일을 살펴보니, Node.js
환경으로 설정되어 있었습니다. 워크플로 파일을 작성할 때, 내용을 자세히 검토하지 않은 채 일반적인 예제를 그대로 따라 적용한 것이 분명했습니다😥.
name: CI
on:
push:
branches: [develop/v0.0.1]
pull_request:
branches: [develop/v0.0.1]
jobs:
build:
runs-on: ubuntu-latest
steps:
# 1. 소스 체크아웃
- uses: actions/checkout@v3
# 2. Node.js 설치
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
# 3. 패키지 의존성 설치
- name: Install dependencies
run: npm ci
# 4. 테스트(있다면)
# - name: Run tests
# run: npm test
# 5. 빌드
- name: Build
run: npm run build
문제를 해결할 방법으로 두 가지가 떠올랐습니다:
2번 방법은 변경 작업이 크기 때문에, 시간과 노력이 덜 드는 1번 방법으로 진행하기로 결정했습니다.
Bun 공식 문서를 참고하여 워크플로 파일을 수정했습니다. 이를 통해 프로젝트에 적합한 설정을 적용할 수 있었습니다.
name: Receipto Bun CI
on:
push:
branches: [develop/v0.0.1]
pull_request:
branches: [develop/v0.0.1]
jobs:
build:
runs-on: ubuntu-latest
steps:
# 1. 소스 체크아웃
- uses: actions/checkout@v4
# 2. bun 설치 (공식 curl 스크립트 사용)
- name: Install bun
run: |
curl -fsSL https://bun.sh/install | bash
echo "$HOME/.bun/bin" >> $GITHUB_PATH
# 3. bun 환경 확인 (버전 체크)
- name: Check bun version
run: bun --version
# 4. 패키지 의존성 설치 (bun)
- name: Install dependencies
run: bun install
# 5. 코드 품질 체크
- name: Run linter
run: bun run lint
- name: Check code formatting
run: bun run format:check
# 6. 빌드
- name: Build
run: bun run build
위에 제가 작성한 워크플로 파일에 대해 설명하겠습니다. 저는 테스트 코드를 작성하지 않았기에 테스트 단계는 작성하지 않았습니다.
name: Receipto Bun CI
name
: 워크플로의 이름을 지정합니다. 여기서는 Receipto Bun CI
라고 지었습니다.on:
push:
branches: [develop/v0.0.1]
pull_request:
branches: [develop/v0.0.1]
on
: 워크플로를 트리거하는 이벤트를 정의합니다.push
와 pull_request
는 설정해 놓은 브랜치에 푸시나 풀 리퀘스트가 있을 때 워크플로가 실행되도록 핳ㅂ니다.develop/v0.0.1
브랜치만 트리거 되도록 설정했습니다.jobs:
build:
runs-on: ubuntu-latest
jobs
: 워크플로 내에서 실행될 작업을 정의합니다.build
: 실행할 작업의 이름입니다.runs-on
: 작업이 실행될 환경을 지정합니다. 여기서는 ubuntu-latest
를 사용합니다.steps:
- uses: actions/checkout@v4
- name: Install bun
run: |
curl -fsSL https://bun.sh/install | bash
echo "$HOME/.bun/bin" >> $GITHUB_PATH
Bun 공식 문서에 나와있는걸 참고해서 작성했습니다.
- name: Check bun version
run: bun --version
Bun이 제대로 설치되었는지 확인합니다.
- name: Install dependencies
run: bun install
- name: Run linter
run: bun run lint
- name: Check code formatting
run: bun run format:check
- name: Build
run: bun run build
이러한 설정을 통해 자동으로 코드 변경이 반영되고, 필요한 의존성을 설치하고, 빌드가 이루어집니다.
수정한 워크플로 파일을 푸시하여 결과를 확인해 보겠습니다.
이미지에서 보시다시피, 에러 없이 통합과 빌드가 성공적으로 완료되었습니다.
처음에는 깊이 생각하지 않고 CI 설정 파일을 작성해 에러가 발생했습니다. 이 경험을 통해, CI를 설정할 때 현재 사용 중인 런타임 환경과 패키지 매니저를 고려해야 한다는 점을 배웠습니다.
그동안 CI/CD라는 용어를 자주 들어왔지만, 명확하게 설명하지 못했는데 이번 기회를 통해 그 개념과 사용 이유를 제대로 이해할 수 있었습니다.
사실 GitHub Actions와 Vercel을 사용한다고 했지만, 현재 워크플로 파일에는 Vercel 배포 내용이 포함되어 있지 않습니다. 그래서 GitHub Actions가 빌드를 수행하고, Vercel은 자체적으로 다시 빌드하여 에러를 확인한 후 배포하는 식으로 진행됩니다. 이 과정을 통해, 다음에는 Vercel CLI를 사용하여 중복 빌드를 피하고 효율적인 배포 과정을 갖춰보고 싶습니다.
일단 이 글은 CI 에러를 해결해서 기쁜 상태로 마무리를 하도록 하겠습니다.