CodePipeline에서 GitHub Actions로, CI/CD 전환기

재영·2026년 1월 5일
post-thumbnail

들어가며

본 글은 기존 AWS CodePipeline 기반의 CI/CD 환경을 GitHub Actions로 전환하여 구성한 과정을 정리한 글입니다.

기존 프로젝트에서는 CI는 GitHub Actions를, CD는 AWS CodePipeline과 CodeBuild를 사용하는 구조로 운영되고 있었습니다. 해당 구성은 초기에는 큰 문제 없이 동작하였으나, 프로젝트 구조가 모노레포 형태로 확장되면서 불필요한 소스 다운로드 시간이 증가하였고, CI 설정이 GitHub Actions와 CodeBuild 두 곳에서 중복 관리되는 구조적 비효율이 확인되었습니다.

이에 따라 CI와 CD를 분리하여 운영하던 기존 구조를 재검토하게 되었으며, 설정 통합과 워크플로우 최적화 측면에서 CI/CD 전반을 GitHub Actions로 통합하는 방향이 보다 적합하다고 판단하였습니다.

본 글에서는

  • AWS CodePipeline에서 GitHub Actions로 전환하게 된 배경과 판단 근거
  • GitHub Actions의 개념과 워크플로우 구성 방식
  • 프로젝트에 적용한 CI/CD 설정 및 Discord 알림 연동

을 중심으로 정리하겠습니다.

전환 배경

기존 프로젝트는 CI는 GitHub Actions로, CD는 AWS CodePipeline으로 분리하여 운영하고 있었습니다. 그러나 프로젝트 구조가 모노레포로 전환되면서 기존 구성 방식의 한계가 확인되었습니다.

1. 모노레포 환경에서의 소스 다운로드 비효율

프로젝트는 web, app, admin 코드를 하나의 저장소에서 관리하는 모노레포 구조로 구성되어 있습니다.

CodePipeline은 트리거 발생 시 저장소 전체를 다운로드하도록 구성되어 있어, 특정 서비스만 배포하는 경우에도 불필요한 코드가 함께 다운로드되었습니다. 특히 React Native 기반의 app 디렉토리 크기가 크다 보니 다운로드 시간이 크게 증가하였습니다.

  • 모노레포 도입 이전: 약 5초
  • 모노레포 도입 이후: 40초 이상

빌드 이전 단계에서 발생하는 불필요한 대기 시간으로, 배포 효율성을 저하시키는 요인으로 확인되었습니다.

2. CI 설정의 중복 관리

CI는 GitHub Actions를 통해 PR 생성 및 리뷰 과정에서 자동으로 실행되도록 구성되어 있었습니다. 그러나 CodePipeline 배포 과정에서도 배포 전 CI가 필요하여 CodeBuild에서 동일한 작업을 수행하도록 설정되어 있었습니다.

결과적으로 CI 설정이 GitHub Actions와 CodeBuild 두 곳에서 관리되는 구조였으며, 설정 변경 시 양쪽 모두 수정해야 하는 번거로움이 존재하였습니다. CI는 단일 지점에서 관리하는 것이 적합하다고 판단하였습니다.

3. UI 기반 설정과 레퍼런스 부족

CodePipeline의 설정은 대부분 AWS 콘솔 UI를 통해 이루어집니다. UI는 주기적으로 변경되며, 검색을 통해 확인한 자료의 화면과 실제 UI가 일치하지 않는 경우가 많았습니다.

또한 CodePipeline 관련 레퍼런스는 상대적으로 제한적이어서 문제 해결 시 참고 자료를 찾기 어려웠습니다. 반면 GitHub Actions는 YAML 기반 설정과 풍부한 커뮤니티 자료를 통해 설정 의도를 명확히 파악하고 적용하기에 용이하였습니다.

4. 설정 변경을 위한 콘솔 접근 비용

개발 과정에서 GitHub는 항상 열어두고 작업하는 반면, AWS 콘솔은 필요한 경우에만 접근하는 환경이었습니다. 그럼에도 환경 변수나 트리거 변경과 같은 사소한 작업에도 AWS 콘솔 접속이 필요하였습니다.

CI/CD 설정을 코드와 함께 GitHub 저장소에서 관리하는 것이 개발 흐름과 더 자연스럽게 연결된다고 판단하였습니다.


위와 같은 배경으로 CI/CD를 GitHub Actions로 통합하여 구성하는 방향으로 전환을 진행하였습니다.

GitHub Actions 개요

GitHub Actions는 GitHub 저장소에서 발생하는 이벤트를 기준으로 자동화된 작업을 실행할 수 있는 CI/CD 도구입니다. 코드 변경, PR 생성, 리뷰 요청과 같은 이벤트가 발생하면 사전에 정의한 워크플로우가 실행되는 구조로 동작합니다.

워크플로우 구성 방식

GitHub Actions는 저장소 내 .github/workflows 디렉토리에 정의된 YAML 파일을 기준으로 동작합니다. 각 YAML 파일은 하나의 워크플로우를 의미하며, 다음과 같은 정보를 포함합니다.

  • on: 어떤 이벤트를 기준으로 실행할 것인지 (push, pull_request, pull_request_review 등)
  • runs-on: 어떤 환경에서 작업을 수행할 것인지
  • jobs, steps: 어떤 작업을 어떤 순서로 실행할 것인지

GitHub 저장소에서 이벤트가 발생하면, 해당 이벤트 조건에 부합하는 워크플로우를 탐색하여 실행합니다. 이후 워크플로우에 정의된 잡(Job)이 실행되고, 각 잡 내부의 스텝(Step)이 순차적으로 수행됩니다.

이러한 구조를 통해 "언제 실행할 것인지"와 "무엇을 실행할 것인지"를 명확히 분리하여 관리할 수 있으며, CI/CD를 포함한 다양한 자동화 작업을 코드 기반으로 구성할 수 있습니다.

다음 섹션에서는 본 프로젝트에 적용한 CI/CD 워크플로우의 구체적인 트리거 설정과 작업 실행 순서, 그리고 Discord 알림 연동을 통한 개발 환경 개선 사례를 다루겠습니다.

프로젝트에 적용한 CI/CD 구성

본 프로젝트의 CI/CD 구성은 다음과 같은 요구사항을 기준으로 설계되었습니다.

  • PR 생성 및 push 시 린트와 타입 체크를 수행하는 CI 자동 실행
  • dev, main 브랜치 머지 시 최종 CI 수행 후 배포까지 자동 진행
  • CI 로직은 단일 워크플로우로 관리하여 중복 제거

이를 위해 다음과 같이 3개의 워크플로우 파일로 구성하였습니다.

워크플로우 파일 구조

전체 워크플로우는 다음과 같은 파일로 구성되어 있습니다.

frontend-ci-base.yml
공통 CI 로직을 정의한 재사용 가능 워크플로우입니다. workflow_call 이벤트를 통해 다른 워크플로우에서 호출할 수 있도록 구성하였습니다.

frontend-pr-ci.yml
PR 단계에서 실행되는 워크플로우로, frontend-ci-base.yml을 호출하여 코드 품질 검증을 수행합니다.

frontend-deploy.yml
dev, main 브랜치에 대한 빌드 및 배포를 수행하는 워크플로우입니다. CI 수행 후 S3 업로드와 CloudFront 캐시 무효화를 순차적으로 진행합니다.

공통 CI 워크플로우 (frontend-ci-base.yml)

공통 CI 워크플로우는 소스 체크아웃부터 빌드까지의 전체 과정을 포함하며, run-build 입력값에 따라 빌드 수행 여부를 제어할 수 있도록 구성하였습니다.

name: Frontend CI Base

on:
  workflow_call:
    inputs:
      run-build:
        required: false
        type: boolean
        default: false

워크플로우는 다음 단계로 구성됩니다.

  1. 소스 체크아웃 및 pnpm 환경 설정
    Corepack을 활성화하고 pnpm 10.12.1 버전을 설정하였습니다.

  2. 의존성 캐싱
    pnpm store를 캐싱하여 반복 실행 시 설치 시간을 단축하였습니다.

  3. 린트 및 타입 체크
    모든 실행에서 공통적으로 수행됩니다.

  4. 조건부 빌드
    run-build 입력값이 true일 경우에만 빌드를 수행합니다.

- name: Build
  if: inputs.run-build == true
  env:
    CHANNEL_TALK_PLUGIN_KEY: ${{ secrets.CHANNEL_TALK_PLUGIN_KEY }}
    SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
    CLARITY_PROJECT_ID: ${{ secrets.CLARITY_PROJECT_ID }}
    API_BASE_URL: ${{ vars.API_BASE_URL }}
    MONITORING_STATUS_URL: ${{ vars.MONITORING_STATUS_URL }}
    SERVER_TYPE: ${{ vars.SERVER_TYPE }}
  run: pnpm run web:build

빌드가 수행될 경우, 결과물은 아티팩트로 업로드되어 후속 job에서 사용할 수 있도록 구성하였습니다.

실제 워크플로우 실행 시에는 위와 같이 Setup, Checkout, pnpm 환경 구성, 캐싱, 의존성 설치, Lint, Type Check, Build, 아티팩트 업로드 순서로 단계가 진행됩니다.

PR 단계 CI (frontend-pr-ci.yml)

PR이 생성되거나 업데이트될 때 자동으로 실행되며, pull_request 이벤트를 트리거로 사용합니다.

name: Frontend PR CI

on:
  pull_request:

jobs:
  pre-build:
    uses: ./.github/workflows/frontend-ci-base.yml
    with:
      run-build: false

PR 단계에서는 빌드를 수행하지 않고 린트와 타입 체크만 실행하도록 run-build: false를 전달하였습니다. 이를 통해 코드 변경에 대한 빠른 피드백을 제공하면서 불필요한 빌드 비용을 줄일 수 있습니다.

배포 워크플로우 (frontend-deploy.yml)

dev, main 브랜치에 코드가 push될 때 실행되며, 빌드부터 배포까지 전체 과정을 자동화하였습니다.

on:
  push:
    branches:
      - main
      - dev

concurrency:
  group: deploy-${{ github.ref_name }}
  cancel-in-progress: true

concurrency 설정을 통해 동일 브랜치에서 배포가 중복 실행되는 것을 방지하였습니다. 새로운 배포가 시작되면 진행 중인 배포는 취소됩니다.

1. Build Job

배포 워크플로우는 먼저 공통 CI 워크플로우를 호출하여 빌드까지 수행합니다.

jobs:
  build:
    uses: ./.github/workflows/frontend-ci-base.yml
    secrets: inherit
    with:
      run-build: true

secrets: inherit를 통해 환경별 시크릿 값을 전달하며, 빌드 결과물은 아티팩트로 저장됩니다.

2. Deploy Job

빌드가 완료되면 아티팩트를 다운로드하여 S3에 업로드하고, CloudFront 캐시를 무효화합니다.

deploy:
  runs-on: ubuntu-latest
  needs: build
  environment: ${{ github.ref_name == 'main' && 'prod' || 'dev' }}

현재 브랜치를 기준으로 실행 환경(prod 또는 dev)을 자동으로 결정하며, 각 환경에 정의된 시크릿과 변수를 사용합니다.

S3 업로드는 --delete 옵션을 사용하여 삭제된 파일도 반영되도록 구성하였습니다.

- name: Upload to S3
  run: |
    aws s3 sync web/dist s3://${{ secrets.S3_BUCKET }}/build-origin --delete

배포 후에는 CloudFront 캐시를 전체 무효화하여 변경사항이 즉시 반영되도록 하였습니다.

- name: CloudFront Invalidation
  run: |
    aws cloudfront create-invalidation \
      --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
      --paths "/*"

3. Notify Job

배포가 완료되면 다음에 설명드릴 Discord로 알림을 전송합니다. 환경별로 다른 메시지와 색상을 사용하여 운영 배포와 개발 배포를 구분할 수 있도록 구성하였습니다.

notify:
  runs-on: ubuntu-latest
  needs: deploy

  steps:
    - name: Send Post-Deploy Notification
      uses: ./.github/actions/discord-notify
      with:
        webhook: ${{ secrets.DISCORD_WEBHOOK_DEPLOY }}
        content: ${{ github.ref_name == 'main' && '🚀 **운영 서버에 새로운 버전이 배포되었습니다!**' || '🧪 **개발 서버에 새로운 버전이 배포되었습니다!**' }}

전체 배포 워크플로우는 위와 같이 build (53초) → deploy (18초) → notify (5초) 순서로 실행되며, 각 job이 순차적으로 완료되는 것을 확인할 수 있습니다.

환경별 변수 관리

환경별 설정은 GitHub Actions의 Environment 기능을 활용하여 관리하였습니다. proddev 환경을 분리하여 각각에 필요한 변수와 시크릿을 정의하였으며, 워크플로우에서는 브랜치명을 기준으로 자동으로 환경을 선택합니다.

민감한 값(API Key, AWS 자격 증명 등)은 Secrets로, 비민감 설정(API URL, 서버 타입 등)은 Variables로 구분하여 관리하였습니다. 이를 통해 코드 수정 없이 환경별 설정을 유연하게 변경할 수 있습니다.


이와 같은 구조를 통해 CI 로직은 단일 워크플로우에서 관리하면서도, PR 검증과 배포 과정을 명확히 분리하여 운영할 수 있도록 구성하였습니다.

Discord 알림을 통한 개발 환경 개선

GitHub Actions를 활용한 CI/CD 구성 외에도, PR 리뷰 과정에서의 커뮤니케이션 효율을 개선하기 위해 Discord 알림 기능을 추가로 구성하였습니다.

기존에는 PR 생성, 리뷰 요청, 승인 등의 상태 변화를 확인하기 위해 GitHub 페이지를 주기적으로 확인해야 했습니다. 이러한 방식은 알림 확인이 지연되거나 누락될 가능성이 존재하였으며, 개발 흐름이 단절되는 요인으로 작용하였습니다.

이에 따라 PR 리뷰 프로세스의 주요 시점마다 Discord로 자동 알림을 전송하도록 구성하여, GitHub를 별도로 확인하지 않아도 리뷰 상태를 즉시 파악할 수 있도록 개선하였습니다. 구성한 알림은 다음과 같습니다.

  1. PR 생성 시 리뷰어에게 알림
  2. Request Changes 시 PR 작성자에게 알림
  3. Re-review 요청 시 리뷰어에게 알림
  4. 최소 2명 이상 승인 완료 시 알림

Custom Action을 통한 알림 기능 공통화

Discord 알림은 PR 생성, 리뷰 요청, 승인 완료, 배포 완료 등 여러 시점에서 사용됩니다. 각 워크플로우에서 동일한 알림 로직을 반복 작성하는 대신, Custom Action으로 공통화하여 재사용할 수 있도록 구성하였습니다.

name: 'Discord Notify'
description: 'Send Discord webhook'

# 사용처에서 받고자 하는 input
inputs:
  webhook:
    description: 'Discord Webhook URL'
    required: true
  content:
    description: 'Notification content'
    required: true
  mentions:
    description: 'Notification mentions'
    required: false
  title:
    description: 'Embed title'
    required: true
  url:
    description: 'Embed URL'
    required: true
  color:
    description: 'Embed color'
    required: false
    default: '15158332'
  fields:
    description: 'Embed fields'
    required: false
    default: '[]'

runs:
  using: 'composite'
  steps:
    - shell: bash
      run: |
        RAW_FIELDS='${{ inputs.fields }}'
        
        FINAL_FIELDS=$(echo "$RAW_FIELDS" | jq '
          map(. + {inline: true})
        ')
        
        curl -X POST \
          -H "Content-Type: application/json" \
          # 알림 내용
          -d "{
            \"username\": \"FE Bot\",
            \"content\": \"${{ inputs.content }}\n${{ inputs.mentions }}\",
            \"embeds\": [
              {
                \"title\": \"${{ inputs.title }}\",
                \"url\": \"${{ inputs.url }}\",
                \"color\": ${{ inputs.color }},
                \"fields\": $FINAL_FIELDS,
                \"footer\": { \"text\": \"자동 알림 메시지입니다\" }
              }
            ]
          }" \
          "${{ inputs.webhook }}"

이 Action은 Discord Webhook을 통해 메시지를 전송하며, 알림 내용, 멘션 대상, Embed 형식 등을 입력값으로 받아 유연하게 구성할 수 있도록 설계하였습니다. fields 입력값은 JSON 배열로 전달받아 jq를 통해 inline: true 속성을 추가한 뒤 전송합니다.

다른 워크플로우에서 이 Action을 사용할 때는 폴더 경로를 참조하는 방식으로 호출합니다.

- name: Send Notification
  uses: ./.github/actions/discord-notify
  with:
    webhook: ${{ secrets.DISCORD_WEBHOOK_PR }}
    content: "알림 내용"

중요: Custom Action을 사용할 때는 파일 경로가 아닌 폴더 경로를 지정해야 합니다. .github/actions/discord-notify/action.yml 경로에 정의하고, GitHub Actions는 지정된 폴더 내의 action.yml 파일을 자동으로 탐색하여 실행합니다.

Discord Webhook URL 생성 방법과 Discord ID 확인 방법은 다음 문서를 참고할 수 있습니다.

PR 생성 알림 구성

PR 생성 시 알림을 예시로 전체 구성 방식을 설명하겠습니다. PR이 생성되면 프론트엔드 팀원들에게 리뷰 요청 알림이 전송됩니다.

name: PR Open Notifications

on:
  pull_request:
    types: [opened, reopened, ready_for_review]

jobs:
  notify-reviewers:
    runs-on: ubuntu-latest
    if: github.event.pull_request.draft == false

ready_for_review 이벤트를 포함하여 Draft PR이 Ready 상태로 전환될 때도 알림이 발송되도록 구성하였으며, Draft 상태에서는 알림이 전송되지 않도록 조건을 설정하였습니다.

워크플로우는 다음 작업을 순차적으로 수행합니다.

1) Discord 멘션 대상 구성

.github/notify_ids.json 파일에 GitHub 계정과 Discord ID를 매핑하여 관리하고 있으며, 이를 기반으로 멘션 대상을 동적으로 구성합니다.

- name: Load Reviewers from JSON
  id: get-reviewers
  uses: actions/github-script@v6
  with:
    script: |
      const fs = require('fs');
      const mapping = JSON.parse(fs.readFileSync('.github/notify_ids.json', 'utf8'));
      const members = Object.keys(mapping);
      
      const author = context.payload.pull_request.user.login;
      # 멘션 대상에 author 제외
      const filtered = members.filter(member => member !== author);
      
      const mentions = filtered
	  # 디스코드 멘션 텍스트로 변경
        .map(login => `<@${mapping[login]}>`)
        .join(' ');
      
      # 다음 단계에서 사용할 수 있도록 output 설정
      core.setOutput("discord_mentions", mentions);

PR 작성자를 제외한 팀원들을 대상으로 Discord 멘션을 생성하며, 이 값은 다음 단계에서 알림 전송 시 사용됩니다.

2) Discord 알림 전송

- name: Send PR Open Notification
  uses: ./.github/actions/discord-notify
  with:
    webhook: ${{ secrets.DISCORD_WEBHOOK_PR }}
    content: "🚀 새 PR이 생성되었습니다! 리뷰 부탁드립니다 🙏"
    # get-reviewers 단계에서 보내준 멘션 텍스트 사용
    mentions: ${{ steps.get-reviewers.outputs.discord_mentions }}
    title: ${{ github.event.pull_request.title }}
    url: ${{ github.event.pull_request.html_url }}
    color: 16753920
    fields: |
      [
        { "name": "작성자", "value": "${{ github.event.pull_request.user.login }}" }
      ]

앞서 정의한 Custom Action을 사용하여 리뷰어들에게 멘션과 함께 알림을 전송합니다.

실제 Discord 채널에서는 위와 같이 PR 제목, 작성자 정보와 함께 멘션된 팀원들에게 알림이 전송됩니다.

다른 알림 트리거 구성

Request Changes, Re-review 요청, 승인 완료 알림은 PR 생성 알림과 유사한 구조로 구성되며, 트리거 이벤트와 알림 메시지만 변경하여 적용하였습니다.

각 알림의 트리거 이벤트는 다음과 같습니다.

  • Request Changes: pull_request_review 이벤트의 submitted 타입, changes_requested 상태
  • Re-review 요청: pull_request 이벤트의 review_requested 타입
  • 승인 완료: pull_request_review 이벤트의 submitted 타입, 승인 수 2개 이상 조건

트리거 이벤트 설정에 대한 자세한 내용은 GitHub Docs - 워크플로를 트리거하는 이벤트에서 확인할 수 있습니다.

알림 구성의 효과 및 확장 가능성

이러한 알림 설정을 통해 다음과 같은 개선 효과를 확인할 수 있었습니다.

  • 리뷰 응답 속도 개선: GitHub 페이지를 주기적으로 확인하지 않아도 Discord 알림을 통해 즉시 리뷰 요청을 인지할 수 있습니다.
  • 컨텍스트 스위칭 감소: 개발 중 Discord로 알림을 받아 필요한 시점에만 GitHub로 이동하여 리뷰를 수행할 수 있습니다.
  • 리뷰 상태 추적 용이: PR 생성부터 승인까지의 전체 흐름을 Discord 채널에서 확인할 수 있어 리뷰 진행 상황을 파악하기 쉬워졌습니다.

Discord 알림 설정은 CI/CD 자동화와 함께 개발 프로세스 전반의 효율성을 개선하는 데 기여하였습니다.

마무리하며

본 글에서는 AWS CodePipeline에서 GitHub Actions로 CI/CD 환경을 전환한 과정과 Discord 알림을 통한 개발 환경 개선 사례를 정리하였습니다.

전환 과정에서 가장 중점을 둔 부분은 설정의 중복 제거와 재사용성 확보였습니다. workflow_call을 활용한 공통 CI 워크플로우 설계, Custom Action을 통한 알림 기능 공통화를 통해 동일한 로직이 여러 곳에서 관리되는 구조를 제거할 수 있었습니다.

이 과정에서 GitHub Actions의 워크플로우 파일 또한 코드라는 점을 다시 확인할 수 있었습니다. 일반적인 애플리케이션 코드와 마찬가지로 가독성과 재사용성을 고려하여 설계해야 하며, 중복을 제거하고 변경에 유연하게 대응할 수 있는 구조로 구성해야 합니다. CI/CD 설정을 코드로 관리함으로써 변경 이력 추적과 협업이 용이해졌으며, GitHub 중심의 개발 흐름과 자연스럽게 통합할 수 있었습니다.

본 글이 CI/CD 구성 시 다음 사항을 고려하는 데 참고가 되기를 바랍니다.

  • 워크플로우 설정도 코드로 접근하여 가독성과 재사용성을 고려할 것
  • 공통 로직은 재사용 가능한 형태로 설계할 것
  • 설정은 코드로 관리하여 변경 이력과 협업을 용이하게 할 것
  • 개발 도구를 통합하여 컨텍스트 스위칭을 최소화할 것

0개의 댓글