Develop
GitHub Actions로 Node.js CI/CD 파이프라인을 구성하는 방법 — 빌드부터 자동 배포까지
Jenkins 없이 GitHub 저장소 안에서 CI/CD를 완결하고 싶다면 GitHub Actions가 가장 빠른 선택이다. workflow 파일 작성부터 Secrets 관리, SSH 배포까지 순서대로 정리했다.

- ·GitHub Actions: 2019년 GA, .github/workflows/*.yml 파일로 파이프라인 정의
- ·runner: GitHub 호스팅(ubuntu-latest 등)과 self-hosted 두 가지 방식
- ·Secrets: 저장소 Settings에서 등록, ${{ secrets.이름 }} 으로 참조
- ·on: push, pull_request, schedule 등 다양한 이벤트로 워크플로 트리거 가능
매번 직접 젠킨스 인프라 서버를 패치하고 디스크 청소하는 작업이 너무 지긋지긋해서 GitHub Actions로 프로젝트 배포망을 갈아탔는데, 결과적으로 인프라 관리 부담이 거짓말처럼 사라졌습니다. YAML 파일 하나만 프로젝트에 던져두니 코드가 머지되는 즉시 자동으로 빌드 테스트가 돌아서 정말 편리했습니다. 다만 초기에 알려지지 않은 호스트 키(Host Key Checking) 문제 때문에 빌드가 계속 뻗어 애를 먹었지만, `ssh-keyscan` 등록 한 줄로 접속 락을 뚫어낸 후에는 어떤 개발 PC에서든 메인 브랜치 머지만으로 즉시 서버가 롤링 배포되는 기적 같은 쾌적함을 즐기고 있습니다.
1. 서버 관리 없는 모던 CI/CD 자동화
SaaS형 인프라 GitHub Actions 플랫폼을 채택하는 이점
CI/CD 자동화를 위해 무거운 Jenkins 전용 인프라 서버를 개설하고 포트 열고, 플러그인 업데이트 주기적으로 체크하며 유지 보수하는 것은 리소스가 부족한 소규모 팀이나 1인 개발자에게 매우 큰 인프라 비용 낭비입니다. GitHub Actions는 깃허브 저장소에 코드만 올려두면 추가 인프라 구축 없이 저장소 내에서 워크플로를 설계해 파이프라인을 가동할 수 있는 SaaS형 자동화 플랫폼입니다. 개발자는 번거로운 서버 호스팅 고민 없이, .github/workflows/ 디렉토리 아래에 YAML 설정 파일 하나만 커밋해서 올리는 것만으로 즉시 빌드와 배포 시스템을 완성할 수 있죠. 깃허브가 자체적으로 컨테이너 빌드용 호스팅 러너(Runner)를 대여해 주어 월 2,000분 한도 내에서 완전 무료로 돌릴 수 있으며, 깃허브 에코시스템과 완벽히 네이티브로 맞물려 있어 코드가 머지되거나 PR이 꽂히는 순간의 이벤트를 바로 낚아채어 피드백 속도가 정말 눈부시게 빨라집니다.
자체 서버 운영 젠킨스(Jenkins)와 클라우드 깃허브 액션의 세부 비교
젠킨스와 깃허브 액션은 둘 다 널리 쓰이는 CI/CD 명가이지만 결이 다릅니다. 젠킨스는 온프레미스(On-Premise) 성향이 강해 회사 보안망 내부의 로컬 서버에 물리적으로 셋업할 수 있고 수천 개의 커스텀 플러그인을 가져와 입맛대로 커스터마이징할 수 있는 무한의 자유도를 줍니다. 다만 서버 운영 체제 패치, 자바 버전 트러블슈팅, 디스크 용량 관리 등을 전담 엔지니어가 도맡아 관리해야 하는 치명적인 운영 비용이 발생하죠. 반면 GitHub Actions는 클라우드 기반 관리 서비스라 셋업이 1분 컷이며 관리 비용이 제로에 수렴하는 극단적인 편의성을 자랑합니다. 복잡한 스테이지 파이프라인 조립이나 철저한 사내 망 격리 배포 등 특수 요구사항이 얽혀 있는 엔터프라이즈 환경이 아니라면, 대부분의 웹 프로젝트 배포 파이프라인은 가볍고 러닝 커브가 낮은 GitHub Actions가 효율성 면에서 앞섭니다.
2. workflow 워크플로 명세서 설계
Node.js 빌드 과정을 자동화하는 YAML 파일의 구문 분석
워크플로의 심장부인 YAML 파일은 on 지시어로 시작되는데, 주로 push: branches: [main] 이벤트를 감지해 배포 사이클을 켭니다. jobs 아래에는 독립적으로 병렬 기동할 수 있는 태스크(예: lint, build, deploy)를 엮는데, runs-on: ubuntu-latest를 명시하여 깃허브 클라우드가 준비한 깨끗한 우분투 컨테이너 가상 머신을 할당받죠. 이후 steps 배열 안에 실행할 명령어 시퀀스를 순차적으로 적어주면 됩니다. 코드를 복사해 오는 actions/checkout을 첫 스텝에 박고, Node.js 가상 엔진 버전을 매핑해 주는 actions/setup-node를 그 아래에 올립니다. 마지막으로 빌드에 필요한 의존성 파일들을 깔끔하게 인스톨해 주는 npm ci 명령어와 실 서버 빌드 산출물을 내뿜는 npm run build를 작성해 주면, 서버 구문 오류 체크를 컴파일 단계에서 바로 감지해 내는 튼튼한 CI 체계가 완성됩니다.
# .github/workflows/ci.yml
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run buildGitHub Actions Secrets를 활용해 배포용 개인키 암호화 격리하기
원격 배포를 자동으로 돌리려면 운영 서버의 IP 주소와 SSH 접속용 개인키(Private Key)를 파이프라인 엔진에 넘겨주어야 합니다. 이 중요한 비밀번호를 코드 파일에 그대로 적었다가 공개 레포지토리에 커밋하여 해커들에 의해 전체 서버 자원이 털려 채굴기로 개조되는 보안 참사가 빈번하죠. 깃허브는 이를 철저히 보호하기 위해 저장소 단위의 암호화 저장소인 'Secrets' 저장소를 제공합니다. 관리자 화면의 Settings 탭에서 비밀 변수명과 원시 값을 등록해 두고, YAML 파이프라인 내부에서는 ${{ secrets.SSH_KEY }} 형식으로만 참조 매핑하는 방식입니다. 이렇게 숨겨진 Secrets 값들은 액션 작동 로그창에서 자동으로 별표 마스킹(***) 처리가 수행되어 콘솔 출력으로도 유출을 막아주므로, 개발자가 안심하고 키 교체를 단행하고 빌드 흐름을 돌려 보안 무결성을 지킬 수 있습니다.
3. SSH 배포 파이프라인 실전 통합
CI/CD 배포 러너에서 운영 서버로 SSH 접속 후 원격 쉘 실행하기
빌드가 완료된 파일을 실제 서버에 배포하려면 배포 job 또는 deploy step을 추가합니다. SSH를 통해 서버에 접속해 배포 스크립트를 실행하거나 rsync로 파일을 전송하는 방식이 일반적이죠. appleboy/ssh-action 액션을 쓰면 SSH 연결과 명령 실행을 한 step으로 처리할 수 있습니다. host, username, key(SSH 개인키)를 Secrets에서 가져와 전달하고, script에 서버에서 실행할 명령을 작성하면 됩니다. 서버에서 git pull을 하고 npm ci, npm run build, pm2 restart를 순서대로 실행하는 방식이 흔히 쓰입니다. rsync로 빌드 산출물만 전송하는 방식을 쓴다면 appleboy/scp-action 액션이 적합하죠. CI job에서 빌드한 결과물을 artifacts로 저장하고 deploy job에서 내려받아 서버에 전송하는 패턴도 있습니다. 처음 SSH 배포를 설정할 때 StrictHostKeyChecking 때문에 연결이 실패하는 경우가 있습니다. ssh-keyscan으로 서버 호스트 키를 known_hosts에 미리 등록하거나, ssh-action의 설정으로 호스트 키 검증을 처리해야 합니다.
# .github/workflows/deploy.yml (deploy job 부분)
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/my-app
git pull origin main
npm ci --production
npm run build
pm2 restart my-appGitHub Actions 캐싱 설정을 적용해 빌드 시간 단축하기
GitHub Actions의 러너는 매번 완전 무결하게 초기화된 독립된 VM 컨테이너 위에서 실행됩니다. 이로 인해 빌드가 돌 때마다 매번 node_modules 폴더의 수백 가지 패키지들을 인터넷 망을 통해 처음부터 다운로드받고 설치하느라 막대한 시간 손실이 발생하고 파이프라인 속도가 질질 끌리게 되죠. 이 리소스를 줄이기 위해 actions/setup-node에 내장된 cache: 'npm' 플래그를 활성화하면, package-lock.json 파일의 해시값을 캐시 고유 키로 활용해 패키지 잠금 파일이 변경되지 않았다면 깃허브 가상 디스크 캐시망에서 모듈 소스를 그대로 가져와 설치 단계를 수초 만에 건너뜁니다. 나아가 무거운 Next.js 빌드 파일들이 임시 적재되는 .next/cache 폴더까지 actions/cache 태스크를 엮어서 전용 디스크 캐싱 루틴을 잡아주면, 평균 5분이 넘게 소요되던 고통스러운 빌드 대기 시간이 단 1분 미만으로 압축되어 개발 피드백 생산성을 획기적으로 개선할 수 있는 강력한 캐싱 전술입니다.
자주 묻는 질문
GitHub Actions에서 배포 스텝 도중 Host key verification failed 에러가 나며 뻗어 버립니다.+
깃허브 가상 서버가 실서버에 처음 접속할 때 대상 서버의 보안 신원을 알 수 없어 접속을 차단한 것입니다. SSH 액션 실행 전 스텝에서 ssh-keyscan 명령어를 사용하여 대상 IP의 공개키 정보를 깃허브 임시 디스크의 ~/.ssh/known_hosts 파일에 미리 강제 기입해 두시면 해결됩니다.
빌드 속도가 너무 느려서 Github Actions 무료 제공 시간이 너무 빨리 닳습니다.+
매번 새로 설치하는 node_modules와 Next.js 컴파일 결과물 폴더인 .next/cache가 원인입니다. actions/setup-node 아래에 cache: 'npm' 설정을 활성화하고, 별도로 actions/cache 모듈을 연동해 빌드 임시 디렉토리를 보존하면 빌드 시간이 절반 이하로 대폭 축소됩니다.
관련 글
Jenkins GitHub Webhook 트리거 완벽 가이드 — push 후 즉시 빌드를 시작하는 방법
매번 손수 빌드를 누르거나 pollSCM으로 리소스를 낭비하지 않고, GitHub Webhook을 연계해 머지 즉시 실시간 Jenkins 빌드가 시작되도록 설계하는 방법을 상세히 다룹니다.
Next.js 정적 사이트를 Jenkins로 자동 배포하는 방법 — Jenkinsfile로 빌드부터 배포까지
Next.js 프로젝트를 output: 'export' 설정으로 정적 빌드한 뒤, Jenkins 파이프라인을 구축해 원격 운영 서버로 자동 배포하는 정석 프로세스를 알아봅니다. Credentials를 통한 SSH 안전 연동 팁도 다룹니다.
Jenkins 빌드 결과를 Slack으로 알림 보내는 방법 — Slack Notification 플러그인 완벽 가이드
Jenkins 빌드가 끝날 때마다 성공/실패 알림을 Slack으로 받으면 배포 상태를 실시간으로 파악할 수 있습니다. Slack Notification 플러그인 설치부터 Webhook 설정, Jenkinsfile post 블록 작성까지 정리했습니다.