TestForge | Aidevops | 📊 Plogger ✍️ Blog 📚 Docs
plogger

AI DevOps Korea

AI 서비스 개발, 운영, 성능개선을 하나의 루프로 연결합니다

aidevops.kr에서 LLMOps, RAG, AI Agent, 관측성, 평가, 비용-성능 최적화를 실전 운영 관점으로 정리합니다.

GitHub Actions CI/CD 설계 가이드

· 수정 4월 16일
GitHub Actions CI/CD 설계 가이드 다이어그램
이 그림은 파이프라인을 검증, 아티팩트 승격, 배포 제어로 나눠 보여 주어 CI/CD 운영 모델을 더 쉽게 이해하도록 돕습니다.
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

다음으로 읽기 좋은 글

다음 탐색

이 주제를 시스템 관점으로 더 이어서 보기