GitHub Actions CI/CD 설계 가이드
파이프라인은 단계보다 목적이 중요하다
대부분의 워크플로우는 비슷해 보입니다. checkout, setup, install, test, build, deploy 순서로 흘러갑니다. 하지만 중요한 것은 순서보다 각 단계의 목적을 분리하는 것입니다.
- 검증 단계: 코드 품질, 테스트, 정적 분석
- 패키징 단계: 아티팩트 생성, 이미지 빌드
- 배포 단계: 환경별 반영
- 사후 단계: 알림, 릴리즈 노트, 결과 수집
이 경계가 명확해야 실패 시 원인을 빠르게 좁힐 수 있고, 환경별 정책도 붙이기 쉬워집니다.
기본 CI는 빠르고 예측 가능해야 한다
CI는 모든 변경에 가장 자주 실행되는 흐름이므로 속도와 안정성이 중요합니다. 설치 시간이 긴 스텝, 불필요한 배포 로직, flaky 테스트를 초기에 섞어 두면 파이프라인 전체 신뢰도가 급격히 떨어집니다.
name: ci
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- run: npm test
이 기본 구조에서 중요한 포인트는 다음과 같습니다.
npm install보다 재현 가능한npm ci사용- 캐시를 써서 설치 비용 절감
- PR 단계에서는 배포보다 검증에 집중
- flaky 테스트는 빨리 찾아 격리
환경 승격은 브랜치보다 정책으로 보는 편이 낫다
브랜치 이름만으로 배포를 제어하면 단순하지만, 조직이 커질수록 승격 정책이 필요해집니다. 스테이징과 프로덕션은 같은 배포 스크립트를 쓰더라도 승인, 시크릿, 실행 조건이 달라야 합니다.
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
needs: test
environment: staging
runs-on: ubuntu-latest
steps:
- name: Deploy to staging
run: ./deploy.sh staging
deploy-production:
if: github.ref == 'refs/heads/main'
needs: test
environment: production
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: ./deploy.sh production
environment를 활용하면 GitHub 상에서 승인 규칙, 환경별 시크릿, 보호 정책을 분리할 수 있습니다. 특히 운영 배포는 단순 branch push보다 더 명시적인 보호 장치를 두는 것이 좋습니다.
Docker 이미지 빌드는 태그 전략이 중요하다
이미지를 빌드하고 푸시하는 것 자체는 어렵지 않지만, 나중에 어떤 이미지를 어떤 코드에서 만들었는지 추적할 수 있어야 합니다. latest만 쓰면 장애 대응 시 회귀점이 흐려집니다.
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myapp:latest
myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
실무에서는 보통 아래를 함께 둡니다.
sha기반 immutable tag- 릴리즈 버전 태그
- 브랜치/환경 태그
- 빌드 provenance 추적 정보
시크릿은 쓰는 방식까지 설계해야 한다
GitHub Actions에서 시크릿을 저장하는 것만으로는 충분하지 않습니다. 어디서 어떤 범위로 노출되는지, 로그에 실수로 출력되지 않는지, 환경별로 분리되는지가 더 중요합니다.
- name: Deploy
env:
DB_URL: ${{ secrets.DB_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
run: ./deploy.sh
시크릿을 파일로 내려쓰는 경우에는 파일 생성 위치, 삭제 시점, 출력 가능성까지 같이 봐야 합니다. 배포 대상이 클라우드라면 장기 비밀번호보다 OIDC 기반 단기 자격 증명을 쓰는 편이 더 안전합니다.
재사용 가능한 워크플로우는 표준화를 만든다
여러 저장소에서 비슷한 빌드와 테스트를 반복한다면 reusable workflow가 큰 도움이 됩니다. 이 방식은 복붙을 줄이는 것보다, 팀의 기본 품질 기준을 표준화한다는 점에서 가치가 큽니다.
on:
workflow_call:
inputs:
node-version:
type: string
default: '22'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm test
다만 재사용 워크플로우가 너무 거대해지면 오히려 각 프로젝트의 차이를 흡수하지 못해 복잡해질 수 있습니다. 공통화는 “정말 공통인 부분”만 하는 편이 좋습니다.
자주 겪는 실패 패턴
가장 흔한 문제는 CI와 CD가 한 파일에 과도하게 섞여 있는 경우입니다. PR 확인만 하고 싶은데 배포 로직까지 얽혀 있으면 파이프라인 해석이 어려워집니다.
또 다른 문제는 테스트와 배포가 같은 신뢰 수준을 공유하지 않는 것입니다. flaky 테스트를 무시한 채 자동 배포를 이어가면, 결국 배포 파이프라인 자체에 대한 신뢰를 잃게 됩니다.
마지막으로 캐시를 과신하는 것도 문제입니다. 캐시는 속도를 높이지만, 오래된 상태를 끌고 오는 원인이 될 수도 있으므로 캐시 키 전략을 분명히 해야 합니다.
운영 관점 체크리스트
좋은 GitHub Actions 파이프라인은 다음 질문에 답할 수 있어야 합니다.
- 실패한 배포를 누가, 어떻게, 어디서 확인하는가
- 어떤 커밋이 어떤 환경에 배포되었는가
- 동일한 커밋을 재배포할 수 있는가
- 시크릿 접근 범위와 승인 절차는 적절한가
- 느린 단계와 flaky 테스트를 계측하고 있는가
마무리
GitHub Actions는 워크플로우 문법보다 운영 모델이 더 중요합니다. 테스트와 배포를 분리하고, 환경 승격 기준을 명확히 하고, 시크릿과 태그 전략을 체계화하면 파이프라인은 단순한 자동화가 아니라 팀의 배포 신뢰도를 높이는 기반이 됩니다. CI/CD는 빨리 돌기만 하는 것이 아니라, 실패해도 믿을 수 있어야 합니다.
운영 환경에서 어려워지는 지점
- GitHub Actions는 workflow가 전달 정책을 반영할 때 잘 확장된다. 소유권 없는 YAML 더미가 되면 금방 약해진다.
- 어려운 문제는 문법보다 신뢰성, secret 범위, 실행 비용, 승격 안전성이다.
- 유연하지만 시끄러운 CI 파이프라인은 결국 신뢰를 잃는다.
중요한 아키텍처 결정
- workflow는 트리거와 책임 기준으로 나눈다. 검증, 빌드, 릴리스, 배포를 구분한다.
- 정말 반복되는 정책에는 reusable workflow와 composite action을 사용한다.
- 환경 보호와 secret 접근은 최소 권한과 필요한 승인 절차로 통제한다.
실무 예시
깔끔한 파이프라인은 보통 신뢰 수준이 다른 단계를 분리한다.
pull_request -> lint + test
main merge -> build + package
release tag -> artifact publish
deploy trigger -> 환경별 rollout
피해야 할 안티패턴
- 모든 브랜치 규칙을 하나의 거대한 workflow 파일에 몰아넣는 것.
- 필요하지 않은 job까지 넓은 secret을 공유하는 것.
- flake test를 일상적인 배경 소음으로 받아들이는 것.
운영 체크리스트
- workflow 소요 시간, flake 비율, 재실행 빈도를 추적한다.
- cache 전략과 artifact 보관 비용을 검토한다.
- reusable workflow를 신중히 버전 관리한다.
- 성공 경로뿐 아니라 실패 가시성과 롤백 절차도 테스트한다.
최종 판단
GitHub Actions는 전달 정책을 명확하고 예측 가능하게 코드화할 때 가장 강하다. 영리하지만 시끄러운 파이프라인은 배포 신뢰도를 떨어뜨린다.
Continue Reading
다음으로 읽기 좋은 글
빌드 출처 증명과 배포 게이트 운영법
소프트웨어 공급망 보안은 스캔 한 번으로 끝나지 않습니다. 빌드 출처 증명과 배포 게이트를 운영에 연결하는 기준을 정리합니다.
🚀 DevOpsCI/CD를 위한 소프트웨어 공급망 Attestation
SBOM, provenance, attestation, 릴리스 검증을 중심으로 현대 배포 파이프라인을 강화하는 실무 가이드입니다.
🧪 Test릴리스 게이트용 스모크 테스트 설계
모든 테스트를 배포 직전에 돌릴 수는 없습니다. 릴리스 게이트에서 꼭 살아 있어야 할 스모크 테스트를 어떻게 고를지 정리합니다.
🧪 Test테스트 데이터 수명주기 관리 가이드
테스트 실패의 많은 원인은 코드가 아니라 데이터입니다. 생성, 공유, 정리 규칙이 없으면 테스트는 점점 불안정해집니다.
다음 탐색