GitHub Actions로 자동 배포 구성하기 — CI/CD 기초
코드를 수정할 때마다 서버에 SSH로 접속해서 git pull을 치고, 빌드하고, 서비스를 재시작하는 과정을 반복하다 보면 점점 귀찮아집니다. 수정 사항이 간단해도 매번 같은 과정을 거쳐야 하고, 바쁠 때는 실수로 빌드를 빼먹거나 잘못된 브랜치를 배포하는 일도 생깁니다. 이런 반복 작업을 자동화하는 게 CI/CD이고, GitHub Actions는 GitHub에서 바로 사용할 수 있는 CI/CD 도구입니다. 이번 글에서는 CI/CD가 무엇인지, GitHub Actions를 어떻게 설정하는지, 실제로 코드를 푸시하면 서버에 자동 배포되는 흐름을 정리해보겠습니다.
CI/CD란 무엇인가
CI는 Continuous Integration, 지속적 통합이라는 뜻입니다. 개발자가 코드를 변경해서 저장소에 푸시하면 자동으로 빌드와 테스트가 실행되는 것을 의미합니다. 여러 명이 같은 프로젝트에서 작업할 때 각자의 코드를 합쳤더니 에러가 나는 상황을 미리 잡아내는 게 목적입니다. 코드를 푸시할 때마다 테스트가 자동으로 돌아가니까 문제가 생기면 바로 알 수 있습니다.
CD는 두 가지 의미로 쓰입니다. Continuous Delivery는 빌드와 테스트를 통과한 코드를 배포 가능한 상태까지 자동으로 만들어두는 것이고, Continuous Deployment는 거기서 한 발 더 나아가 실제 운영 서버에 자동으로 배포하는 것입니다. Delivery는 배포 버튼을 사람이 누르고, Deployment는 그 버튼마저 자동화합니다.
정리하면 CI/CD는 코드 변경부터 실제 서비스 반영까지의 과정을 자동화하는 것입니다. 수동으로 하던 빌드, 테스트, 배포를 파이프라인으로 구성해서 코드를 푸시하는 것만으로 모든 과정이 알아서 돌아가게 만드는 겁니다.
GitHub Actions란 무엇인가
GitHub Actions는 GitHub에서 제공하는 CI/CD 서비스입니다. GitHub 저장소에 코드를 푸시하거나 풀 리퀘스트를 만들거나 특정 이벤트가 발생하면 미리 정의해둔 작업이 자동으로 실행됩니다.
별도의 CI/CD 서버를 구축할 필요가 없다는 게 가장 큰 장점입니다. Jenkins 같은 도구는 서버를 직접 설치하고 관리해야 하지만, GitHub Actions는 GitHub에 저장소만 있으면 바로 사용할 수 있습니다. 공개 저장소에서는 무료이고, 비공개 저장소도 월 2,000분까지 무료로 사용할 수 있습니다.
설정은 저장소 안에 YAML 파일을 만드는 것으로 끝납니다. .github/workflows 디렉토리에 워크플로우 파일을 넣으면 GitHub가 자동으로 인식하고 실행합니다.
기본 개념 정리
GitHub Actions에서 사용하는 용어가 몇 가지 있습니다.
워크플로우(Workflow)는 자동화할 전체 과정을 정의한 YAML 파일입니다. 하나의 워크플로우 안에 여러 작업이 들어갈 수 있습니다.
잡(Job)은 워크플로우 안에서 실행되는 작업 단위입니다. 빌드 잡, 테스트 잡, 배포 잡처럼 나눌 수 있습니다. 기본적으로 각 잡은 독립된 가상 환경에서 실행되고, 병렬로 돌아갑니다. 순서를 지정하고 싶으면 의존성을 설정할 수 있습니다.
스텝(Step)은 잡 안에서 순서대로 실행되는 개별 명령입니다. 코드를 체크아웃하는 스텝, 의존성을 설치하는 스텝, 테스트를 실행하는 스텝 같은 식으로 나눕니다.
러너(Runner)는 워크플로우가 실행되는 서버입니다. GitHub에서 제공하는 가상 머신을 사용할 수도 있고, 내 서버를 러너로 등록해서 사용할 수도 있습니다.
액션(Action)은 자주 사용되는 작업을 재사용 가능하게 패키징한 것입니다. 코드를 체크아웃하는 actions/checkout, Node.js를 설정하는 actions/setup-node 같은 공식 액션이 있고, 커뮤니티에서 만든 액션도 수천 개가 있습니다.
첫 번째 워크플로우 만들어보기
간단한 예시부터 시작하겠습니다. main 브랜치에 코드를 푸시하면 자동으로 테스트를 실행하는 워크플로우입니다.
저장소 안에 .github/workflows 디렉토리를 만들고, 그 안에 ci.yml 파일을 생성합니다.
yaml
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 의존성 설치
run: npm install
- name: 테스트 실행
run: npm test
하나씩 살펴보겠습니다.
name은 워크플로우의 이름입니다. GitHub Actions 탭에서 이 이름으로 표시됩니다.
on은 워크플로우가 실행되는 조건입니다. main 브랜치에 push가 발생하거나 main을 대상으로 하는 pull_request가 만들어지면 실행됩니다.
jobs 아래에 test라는 잡을 정의했습니다. runs-on: ubuntu-latest는 GitHub에서 제공하는 최신 Ubuntu 가상 머신에서 실행한다는 뜻입니다.
steps에 실행할 단계를 순서대로 적습니다. uses는 미리 만들어진 액션을 사용하는 것이고, run은 셸 명령어를 직접 실행하는 것입니다. 코드를 체크아웃하고, Node.js 환경을 설정하고, 의존성을 설치하고, 테스트를 실행하는 흐름입니다.
이 파일을 저장소에 푸시하면 GitHub Actions 탭에서 워크플로우가 실행되는 걸 확인할 수 있습니다.
서버 자동 배포 워크플로우
테스트까지 자동화했으니 이제 배포도 자동화해보겠습니다. main 브랜치에 푸시하면 테스트를 실행하고, 테스트가 통과하면 운영 서버에 자동으로 배포하는 워크플로우입니다.
서버 배포는 SSH로 접속해서 명령어를 실행하는 방식이 가장 기본적입니다. 이걸 자동화하려면 GitHub Actions에서 서버로 SSH 접속이 가능해야 합니다. SSH 키를 GitHub 저장소의 시크릿에 등록하면 워크플로우에서 안전하게 사용할 수 있습니다.
먼저 시크릿을 등록합니다. GitHub 저장소 페이지에서 Settings, Secrets and variables, Actions 순서로 들어가서 New repository secret을 클릭합니다. 서버 접속에 필요한 정보를 등록합니다. SSH_HOST에 서버 IP를, SSH_USERNAME에 접속 사용자명을, SSH_KEY에 SSH 개인키 내용을 넣습니다.
시크릿으로 등록한 값은 워크플로우 로그에서 자동으로 마스킹 처리되기 때문에 외부에 노출되지 않습니다.
배포 워크플로우 파일은 이렇게 작성합니다.
yaml
name: Deploy
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v4
- name: Node.js 설정
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 의존성 설치
run: npm install
- name: 테스트 실행
run: npm test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- name: SSH로 서버에 배포
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/today-play
git pull origin main
npm install
pm2 restart all
여기서 핵심은 deploy 잡의 needs: test 부분입니다. 이건 test 잡이 성공적으로 완료된 후에만 deploy 잡을 실행하라는 뜻입니다. 테스트가 실패하면 배포가 실행되지 않기 때문에 버그가 있는 코드가 운영 서버에 올라가는 걸 방지할 수 있습니다.
appleboy/ssh-action은 커뮤니티에서 만든 SSH 액션입니다. 지정한 서버에 SSH로 접속해서 script에 적힌 명령어를 실행합니다. 서버에서 git pull로 최신 코드를 받고, 의존성을 설치하고, pm2로 앱을 재시작하는 흐름입니다.
${{ secrets.SSH_HOST }} 형식은 아까 등록한 시크릿 값을 참조하는 문법입니다. 워크플로우 파일에 직접 서버 IP나 키를 적으면 안 됩니다.
환경 변수와 시크릿 관리
배포할 때 환경 변수가 필요한 경우가 많습니다. 데이터베이스 접속 정보, API 키 같은 값들은 코드에 직접 넣으면 안 되고 환경 변수로 관리해야 합니다.
GitHub Actions에서 환경 변수를 사용하는 방법은 두 가지입니다.
워크플로우 파일 안에서 직접 정의하는 방법이 있습니다.
yaml
env:
NODE_ENV: production
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 환경 변수 확인
run: echo $NODE_ENV
민감하지 않은 값은 이렇게 워크플로우 파일에 직접 적어도 됩니다.
민감한 값은 시크릿으로 등록하고 참조합니다. 시크릿은 저장소 설정에서 등록하고, 워크플로우에서 ${{ secrets.시크릿이름 }} 형식으로 사용합니다. 시크릿은 한 번 등록하면 다시 확인할 수 없고, 수정하거나 삭제만 가능합니다.
배포 워크플로우 실전 팁
배포 실패에 대비하는 게 중요합니다. 배포 도중에 에러가 나면 서비스가 중단될 수 있습니다. script 안에서 각 명령어 사이에 확인 로직을 넣거나, 배포 전에 현재 상태를 백업하는 스텝을 추가하면 안전합니다.
yaml
script: |
cd /var/www/today-play
git pull origin main || exit 1
npm install || exit 1
npm run build || exit 1
pm2 restart all
각 명령어 뒤에 || exit 1을 붙이면 해당 명령어가 실패했을 때 스크립트 전체가 중단됩니다. git pull이 실패했는데 npm install이 실행되는 상황을 방지할 수 있습니다.
특정 파일이 변경됐을 때만 배포를 실행하고 싶은 경우도 있습니다. README만 수정했는데 배포가 돌아가는 건 낭비니까요.
yaml
on:
push:
branches: [ main ]
paths-ignore:
- '**.md'
- 'docs/**'
paths-ignore에 지정한 경로의 파일만 변경됐으면 워크플로우가 실행되지 않습니다. 마크다운 파일이나 문서 폴더의 변경은 배포와 무관하니까 제외하는 겁니다.
배포 알림을 보내는 것도 유용합니다. 배포가 성공했는지 실패했는지를 슬랙이나 디스코드로 알림 보내는 액션들이 커뮤니티에 많이 있습니다.
워크플로우 실행 결과 확인하기
워크플로우가 실행되면 GitHub 저장소의 Actions 탭에서 결과를 확인할 수 있습니다. 각 워크플로우 실행 기록이 목록으로 나오고, 클릭하면 잡별로 스텝 단위의 로그를 볼 수 있습니다.
초록색 체크 표시는 성공, 빨간색 X 표시는 실패입니다. 실패한 스텝을 클릭하면 에러 로그가 나오기 때문에 원인을 파악할 수 있습니다. npm test에서 실패했다면 어떤 테스트가 통과하지 못했는지, SSH 접속에서 실패했다면 키가 맞는지, 서버 IP가 맞는지를 로그에서 확인합니다.
워크플로우가 실행 중일 때 취소할 수도 있습니다. 실수로 잘못된 코드를 푸시했을 때 배포가 진행되고 있다면 Actions 탭에서 Cancel workflow run 버튼을 눌러서 중단할 수 있습니다.
다른 CI/CD 도구와의 비교
GitHub Actions 외에도 CI/CD 도구는 여러 가지가 있습니다.
Jenkins는 오래된 오픈소스 CI/CD 도구입니다. 플러그인이 방대하고 자유도가 높지만 직접 서버를 설치하고 관리해야 합니다. 대규모 팀이나 복잡한 파이프라인이 필요한 프로젝트에서 많이 사용합니다.
GitLab CI/CD는 GitLab에 내장된 CI/CD 서비스입니다. GitHub Actions와 비슷한 개념이지만 GitLab 저장소를 사용할 때 쓸 수 있습니다.
CircleCI, Travis CI 같은 전문 CI/CD 서비스도 있습니다. 각각의 장단점이 있지만, GitHub에 저장소를 두고 있다면 별도 서비스를 연동하는 것보다 GitHub Actions가 설정도 간편하고 관리할 것도 적습니다.
개인 프로젝트나 소규모 팀이라면 GitHub Actions만으로 충분하고, 파이프라인이 복잡해지거나 특수한 요구 사항이 생기면 그때 다른 도구를 검토해도 됩니다.
마무리
CI/CD는 결국 사람이 반복하던 작업을 자동화하는 것입니다. GitHub Actions를 사용하면 저장소에 YAML 파일 하나 만드는 것만으로 코드 푸시부터 테스트, 배포까지 전체 흐름을 자동화할 수 있습니다. 처음에는 간단한 테스트 자동화부터 시작하고, 익숙해지면 배포까지 연결하면 됩니다. 한 번 설정해두면 이후로는 코드를 푸시하는 것만으로 모든 과정이 알아서 돌아갑니다. 다음 글에서는 여러 개의 컨테이너를 동시에 관리할 수 있는 Docker Compose에 대해 다뤄보겠습니다.
Leave a Reply