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
이 워크플로우의 치명적인 문제 세 가지:
- 변경 감지 없음:
packages/utils고쳤는데packages/frontend,packages/backend다 빌드함 - 병렬화 없음: 테스트가 순차로 돌아감
- 캐싱 없음: 매번 처음부터 시작
세 개 다 고쳐봅시다.
변경 감지: 바뀐 것만 빌드하기
핵심은 이거예요: 모노레포에서 대부분의 커밋은 일부 패키지만 건드려요. 진짜 바뀐 것만 빌드하고 테스트하면 되죠.
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 필요
안 쓰는 게 나은 경우:
- 작은 팀, 간단한 빌드
- 인프라 관리 못 함
- 보안 격리가 최우선
셀프 호스티드 러너 설정
-
GitHub에서 러너 생성: Settings → Actions → Runners → New self-hosted runner
-
서버에서:
# 러너 다운로드 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
- 워크플로우에서 사용:
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: '*'
셀프 호스티드 러너 오프라인
흔한 원인:
- 머신 리부팅됐는데 서비스 안 켜짐
- 토큰 만료 (30일마다 갱신)
- 빌드 아티팩트로 디스크 꽉 참
모니터링 설정:
# 러너 상태 확인 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분으로
이 기법들로:
- 빌드 시간 80%+ 단축 - 변경 감지, 캐싱, 병렬화
- 비용 50%+ 절감 - 똑똑한 러너 선택, 동시성 제어
- 자신 있게 스케일 - 셀프 호스티드 러너, ARC
- 안전한 배포 - OIDC, 환경 보호
GitHub Actions가 단순 CI 도구에서 강력한 자동화 플랫폼으로 성장했어요. 이걸 마스터한 팀은 배포 속도와 개발자 경험에서 엄청난 우위를 가져요.
한 가지 최적화로 시작하세요—변경 감지나 원격 캐싱 같은 거. 개선 측정하고. 반복하세요. 미래의 나 (그리고 팀)가 고마워할 거예요.
이제 파이프라인 빠르게 만들러 가세요 🚀
관련 도구 둘러보기
Pockit의 무료 개발자 도구를 사용해 보세요