Back

GitHub Actions 2026 완전 정복: 모노레포 CI/CD부터 Self-hosted Runner까지

GitHub Actions 워크플로우가 45분 걸려요. 팀원들 짜증나죠. 푸시할 때마다 모노레포 전체가 다시 빌드되고요. 매월 무료 분수가 순식간에 바닥나요.

익숙한 상황이죠? 혼자 겪는 거 아니에요. 코드베이스가 커지고 모노레포가 대세가 되면서, 패키지 하나짜리에서 잘 돌아가던 CI/CD가 갑자기 병목이 돼버려요. 근데 GitHub Actions도 많이 발전했거든요. 대부분 개발자들이 아직 다 활용을 못 하고 있을 뿐이에요.

이 글에서는 2026년 GitHub Actions의 거의 모든 것을 다뤄요. 모노레포 워크플로우 최적화부터 셀프 호스티드 러너 설정, 고급 캐싱 전략부터 비용 관리까지. CI/CD를 병목에서 경쟁력으로 바꿔봅시다.

모노레포의 함정: 왜 빌드가 느린가

요즘 모노레포 안 쓰는 데가 없죠. Turborepo, Nx, Lerna, Rush—도구는 성숙해졌어요. 근데 CI/CD는 대부분 팀에서 아직도 옛날 그대로예요.

문제 상황

# 멍청한 방법: 매 푸시마다 전부 빌드 name: CI on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run build - run: npm test

이 워크플로우의 치명적인 문제 세 가지:

  1. 변경 감지 없음: packages/utils 고쳤는데 packages/frontend, packages/backend 다 빌드함
  2. 병렬화 없음: 테스트가 순차로 돌아감
  3. 캐싱 없음: 매번 처음부터 시작

세 개 다 고쳐봅시다.

변경 감지: 바뀐 것만 빌드하기

핵심은 이거예요: 모노레포에서 대부분의 커밋은 일부 패키지만 건드려요. 진짜 바뀐 것만 빌드하고 테스트하면 되죠.

paths-filter 쓰기

name: CI on: push: branches: [main] pull_request: jobs: changes: runs-on: ubuntu-latest outputs: frontend: ${{ steps.filter.outputs.frontend }} backend: ${{ steps.filter.outputs.backend }} shared: ${{ steps.filter.outputs.shared }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: filters: | frontend: - 'packages/frontend/**' - 'packages/shared/**' backend: - 'packages/backend/**' - 'packages/shared/**' shared: - 'packages/shared/**' frontend: needs: changes if: ${{ needs.changes.outputs.frontend == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run build --workspace=packages/frontend - run: npm test --workspace=packages/frontend backend: needs: changes if: ${{ needs.changes.outputs.backend == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run build --workspace=packages/backend - run: npm test --workspace=packages/backend

결과: packages/frontend/src/Button.tsx만 고치면 frontend 잡만 돌아요. backend는 아예 스킵.

Turborepo 내장 감지 기능

Turborepo 쓰면 변경 감지가 내장돼 있어요:

jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 변경 감지에 필요 - uses: pnpm/action-setup@v3 - uses: actions/setup-node@v4 with: node-version: 22 cache: 'pnpm' - run: pnpm install - run: pnpm turbo build --filter='...[origin/main]' - run: pnpm turbo test --filter='...[origin/main]'

--filter='...[origin/main]'가 origin/main 이후 바뀐 패키지만 돌리라는 뜻이에요.

고급 캐싱: 기본을 넘어서

캐싱에서 대부분 팀이 성능을 놓쳐요. actions/cache 넘어서 가봅시다.

1단계: 패키지 매니저 캐시

이건 기본이에요. 제대로 하고 있는지 확인:

- uses: actions/setup-node@v4 with: node-version: 22 cache: 'pnpm' # 또는 'npm', 'yarn'

lockfile 해시 기반으로 node_modules를 캐시해요.

2단계: Turborepo 빌드 캐시

Turborepo 원격 캐싱, 이거 진짜 다릅니다:

- run: pnpm turbo build --filter='...[origin/main]' env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }}

원격 캐싱 켜두면, 팀원이 이미 packages/utils를 같은 입력으로 빌드했다면 CI 머신에서도 캐시 히트돼요—새 머신에서도요.

3단계: 무거운 의존성 별도 캐싱

설치 오래 걸리는 건 따로 캐시:

- name: Playwright 브라우저 캐시 uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - name: Playwright 설치 run: npx playwright install --with-deps if: steps.cache-playwright.outputs.cache-hit != 'true'

4단계: Docker 레이어 캐싱

Docker 이미지 빌드하면:

- uses: docker/build-push-action@v5 with: context: . push: true tags: myapp:latest cache-from: type=gha cache-to: type=gha,mode=max

type=gha가 GitHub Actions 캐시를 Docker 레이어에 써요. Docker 빌드 시간 80% 이상 단축 가능.

매트릭스 빌드: 다 병렬로

매트릭스 빌드로 같은 잡을 다른 설정으로 병렬 실행할 수 있어요.

기본 매트릭스

jobs: test: runs-on: ubuntu-latest strategy: matrix: node: [18, 20, 22] os: [ubuntu-latest, windows-latest, macos-latest] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: npm ci - run: npm test

이러면 9개 병렬 잡이 생겨요 (Node 버전 3개 × OS 3개).

모노레포용 동적 매트릭스

바뀐 패키지 기반으로 매트릭스 동적 생성:

jobs: detect: runs-on: ubuntu-latest outputs: packages: ${{ steps.detect.outputs.packages }} steps: - uses: actions/checkout@v4 - id: detect run: | packages=$(ls -d packages/*/ | jq -R -s -c 'split("\n")[:-1]') echo "packages=$packages" >> $GITHUB_OUTPUT test: needs: detect runs-on: ubuntu-latest strategy: matrix: package: ${{ fromJson(needs.detect.outputs.packages) }} steps: - uses: actions/checkout@v4 - run: npm ci - run: npm test --workspace=${{ matrix.package }}

이제 각 패키지가 자기 병렬 잡에서 테스트돼요.

Fail-Fast vs 전체 완료

기본적으로 매트릭스 잡 하나가 실패하면 나머지 다 취소돼요. 근데 가끔은 다 끝까지 돌려보고 싶잖아요:

strategy: fail-fast: false # 하나 실패해도 나머지 계속 matrix: node: [18, 20, 22]

Self-Hosted Runners: 언제, 어떻게

GitHub 호스티드 러너는 편하지만 한계가 있어요:

  • 7GB RAM, 2 CPU (스탠다드)
  • 영구 스토리지 없음
  • 분당 과금 누적
  • GPU 없음

셀프 호스티드 러너가 다 해결해요.

셀프 호스티드 쓸 때

쓰면 좋은 경우:

  • 리소스 더 필요 (RAM, CPU, GPU)
  • 오래 걸리는 잡이 호스티드에서 비쌈
  • 온프레미스 자원 접근 필요
  • ML 워크로드에 GPU 필요

안 쓰는 게 나은 경우:

  • 작은 팀, 간단한 빌드
  • 인프라 관리 못 함
  • 보안 격리가 최우선

셀프 호스티드 러너 설정

  1. GitHub에서 러너 생성: Settings → Actions → Runners → New self-hosted runner

  2. 서버에서:

# 러너 다운로드 mkdir actions-runner && cd actions-runner curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/download/v2.320.0/actions-runner-linux-x64.tar.gz tar xzf actions-runner-linux-x64.tar.gz # 설정 ./config.sh --url https://github.com/your-org/your-repo \ --token YOUR_TOKEN \ --labels gpu,linux,x64 # 서비스로 실행 sudo ./svc.sh install sudo ./svc.sh start
  1. 워크플로우에서 사용:
jobs: ml-training: runs-on: [self-hosted, gpu, linux] steps: - uses: actions/checkout@v4 - run: python train.py

Actions Runner Controller (ARC)로 스케일링

쿠버네티스 환경이면 ARC가 수요 기반으로 러너를 자동 스케일:

# ARC용 values.yaml controllerServiceAccount: namespace: arc-systems name: arc-controller githubConfigUrl: "https://github.com/your-org" githubConfigSecret: github-config-secret maxRunners: 10 minRunners: 1 template: spec: containers: - name: runner image: ghcr.io/actions/actions-runner:latest resources: requests: cpu: 2 memory: 4Gi

잡 큐에 들어오면 러너가 스핀업, 대기 상태면 스핀다운.

비용 최적화 전략

GitHub Actions 청구가 예상 외로 나올 수 있어요. 비용 잡는 법:

1. macOS/Windows보다 Ubuntu

러너분당 비용
ubuntu-latest$0.008
windows-latest$0.016 (2배)
macos-latest$0.08 (10배)

macOS는 iOS 빌드나 macOS 특정 테스트에만 쓰세요.

2. 중복 실행 취소

빠르게 여러 커밋 푸시할 때 옛날 실행 취소:

concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true

3. 큰 러너 전략적으로

GitHub에 더 큰 러너 있어요 (4배, 8배, 16배). 역설적으로 더 쌀 수도:

jobs: build: runs-on: ubuntu-latest-8-cores # 2코어 대신 8코어

빌드가 2코어에서 20분인데 8코어에서 6분이면, 분당 요금 높아도 비용 절약.

4. 잡 타임아웃

폭주 잡이 무한정 돌아가는 거 방지:

jobs: build: runs-on: ubuntu-latest timeout-minutes: 30 # 30분 후 킬

5. 급하지 않은 잡 스케줄링

비싼 잡을 한가한 시간에:

on: schedule: - cron: '0 2 * * *' # 매일 UTC 오전 2시

고급 패턴

재사용 가능 워크플로우

레포끼리 반복 방지:

# .github/workflows/reusable-test.yml name: Reusable Test Workflow on: workflow_call: inputs: node-version: required: true type: string 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

다른 워크플로우에서 사용:

jobs: call-reusable: uses: ./.github/workflows/reusable-test.yml with: node-version: '22'

Composite Actions

여러 스텝을 재사용 가능한 액션으로 묶기:

# .github/actions/setup-project/action.yml name: 'Setup Project' description: 'Setup Node.js, install deps, and cache' runs: using: 'composite' steps: - uses: pnpm/action-setup@v3 with: version: 9 - uses: actions/setup-node@v4 with: node-version: 22 cache: 'pnpm' - run: pnpm install --frozen-lockfile shell: bash

사용:

steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-project - run: pnpm build

환경 보호 규칙

프로덕션 배포에 승인 필요하게:

jobs: deploy-prod: runs-on: ubuntu-latest environment: name: production url: https://myapp.com steps: - run: ./deploy.sh

레포 설정에서 production 환경에 리뷰 필요하게 설정하세요.

OIDC로 클라우드 인증

오래가는 클라우드 키 저장 이제 그만. OIDC 쓰세요:

jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole aws-region: us-east-1 - run: aws s3 sync ./dist s3://my-bucket

시크릿 저장 없음—GitHub가 OIDC로 임시 자격증명 생성.

트러블슈팅: 흔한 문제들

"Resource not accessible by integration"

워크플로우에 권한이 없는 거예요:

permissions: contents: read pull-requests: write issues: write

캐시 복구 안 됨

캐시 키 확인. 흔한 문제:

  • lockfile이 해시에 안 들어감
  • 저장 때와 복구 때 러너 OS가 다름
  • 캐시 한도 초과 (레포당 10GB)
- uses: actions/cache@v4 with: path: ~/.npm key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | npm-${{ runner.os }}-

매트릭스 잡 타임아웃

잡이 멈추면 명시적 타임아웃과 디버깅 추가:

jobs: test: timeout-minutes: 30 steps: - run: npm test timeout-minutes: 20 env: DEBUG: '*'

셀프 호스티드 러너 오프라인

흔한 원인:

  1. 머신 리부팅됐는데 서비스 안 켜짐
  2. 토큰 만료 (30일마다 갱신)
  3. 빌드 아티팩트로 디스크 꽉 참

모니터링 설정:

# 러너 상태 확인 sudo ./svc.sh status # 로그 보기 sudo journalctl -u actions.runner.*

완전한 모노레포 워크플로우

다 합친 프로덕션 레디 워크플로우:

name: CI/CD on: push: branches: [main] pull_request: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: changes: runs-on: ubuntu-latest outputs: packages: ${{ steps.filter.outputs.changes }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: filters: | frontend: - 'packages/frontend/**' backend: - 'packages/backend/**' shared: - 'packages/shared/**' build-and-test: needs: changes if: ${{ needs.changes.outputs.packages != '[]' }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: package: ${{ fromJson(needs.changes.outputs.packages) }} steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-project - name: Build run: pnpm turbo build --filter=${{ matrix.package }} env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }} - name: Test run: pnpm turbo test --filter=${{ matrix.package }} - name: Upload coverage uses: codecov/codecov-action@v4 with: flags: ${{ matrix.package }} deploy: needs: build-and-test if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-project - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: us-east-1 - run: pnpm deploy

마무리: 45분에서 5분으로

이 기법들로:

  1. 빌드 시간 80%+ 단축 - 변경 감지, 캐싱, 병렬화
  2. 비용 50%+ 절감 - 똑똑한 러너 선택, 동시성 제어
  3. 자신 있게 스케일 - 셀프 호스티드 러너, ARC
  4. 안전한 배포 - OIDC, 환경 보호

GitHub Actions가 단순 CI 도구에서 강력한 자동화 플랫폼으로 성장했어요. 이걸 마스터한 팀은 배포 속도와 개발자 경험에서 엄청난 우위를 가져요.

한 가지 최적화로 시작하세요—변경 감지나 원격 캐싱 같은 거. 개선 측정하고. 반복하세요. 미래의 나 (그리고 팀)가 고마워할 거예요.

이제 파이프라인 빠르게 만들러 가세요 🚀

GitHub ActionsCI/CDDevOpsMonorepoAutomationSelf-Hosted Runners

관련 도구 둘러보기

Pockit의 무료 개발자 도구를 사용해 보세요