← posts/b.log()

blog92@web:~$ cat posts/github-08-actions-and-branch-protection.md

GIT3 min read

GitHub 완전 정복 8편 — GitHub Actions와 브랜치 보호 규칙

워크플로·잡·스텝의 기본 구조와 매트릭스 빌드·시크릿·잡 의존성, Next.js 배포 워크플로 예시와 액션 보안 주의사항, 그리고 Rulesets·필수 리뷰·필수 상태 체크·force push 차단·CODEOWNERS로 위험한 명령을 조직 차원에서 막는 방법을 다룹니다.

이전 편: 7편 — rebase -i, cherry-pick, bisect와 히스토리를 다루는 도구들

이 편은 GitHub 저장소가 제공하는 기능으로서 Actions를 한 편에 요약합니다. 표현식과 컨텍스트, job 분리와 아티팩트 전달, Runner와 디버깅까지 파고드는 이야기는 풀스택 개발자를 위한 CI/CD 2강 — GitHub Actions 해부와 첫 워크플로에 있습니다.

이 글의 예제에 나오는 액션 버전(@v4 등)은 작성 시점 기준입니다. 실제로 적용할 때는 각 액션 저장소의 최신 릴리스를 확인하세요.

6.1 GitHub Actions — CI/CD

.github/workflows/ 아래 YAML 파일을 두면 GitHub가 이벤트에 반응해 자동으로 실행합니다.

기본 구조

yaml
name: CI
 
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * *'        # 매일 자정 (UTC)
  workflow_dispatch:            # 수동 실행 버튼
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
          cache: 'npm'
 
      - run: npm ci
      - run: npm run lint
      - run: npm test

node-version-file: .nvmrc는 저장소 루트의 .nvmrc 한 곳에서 Node 버전을 읽습니다. 워크플로마다 버전을 박아두면 그 버전의 지원이 끝난 뒤에도 CI는 아무 말 없이 계속 돌기 때문에, 버전은 한 곳에만 두는 편이 안전합니다.

개념 정리

text
Workflow (파일 하나)
 └─ Job (병렬 실행, 각각 독립 VM)
     └─ Step (순차 실행)
         └─ Action(재사용 모듈) 또는 run(쉘 명령)

매트릭스 빌드 — 여러 환경 동시 테스트

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

매트릭스에 넣을 버전은 Node.js 릴리스 일정에서 지원 중인 LTS를 확인해 고르세요. 2026년 9월 기준 지원 중인 것은 22(Maintenance LTS)·24(Active LTS)·26(Current)이고, 18과 20은 각각 2025-04-30·2026-04-30에 지원이 끝났습니다.

시크릿과 환경

yaml
steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.API_KEY }}        # Settings → Secrets에 등록
      DB_URL: ${{ vars.DB_URL }}             # 민감하지 않은 변수
    run: ./deploy.sh

시크릿은 로그에 출력해도 ***로 마스킹됩니다. 단, 포크에서 온 pull_request 워크플로에는 GITHUB_TOKEN을 제외한 시크릿이 전달되지 않고, 그 GITHUB_TOKEN마저 읽기 전용으로 내려옵니다. 외부 기여자가 시크릿을 훔치는 걸 막기 위한 설계입니다. 같은 저장소의 브랜치에서 올린 PR에는 시크릿이 평소대로 전달되며, 예외적으로 pull_request_target 이벤트는 포크 PR에도 읽기·쓰기 토큰을 주기 때문에 위험합니다(아래 보안 주의사항 참고).

의존성과 조건

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps: [...]
 
  deploy:
    needs: build                                       # build 성공 후에만
    if: github.ref == 'refs/heads/main'                # main일 때만
    runs-on: ubuntu-latest
    environment: production                            # 승인 절차 연결 가능
    steps: [...]

실전 예시 — Next.js 블로그 자동 배포 + 품질 검사

yaml
name: Deploy
 
on:
  push:
    branches: [main]
 
concurrency:                      # 중복 실행 방지
  group: deploy-${{ github.ref }}
  cancel-in-progress: true
 
permissions:
  contents: read
 
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version-file: .nvmrc, cache: 'npm' }
      - run: npm ci
      - run: npm run lint
      - run: npx tsc --noEmit
      - run: npm test -- --coverage
      - uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/
 
  deploy:
    needs: quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version-file: .nvmrc, cache: 'npm' }
      - run: npm ci && npm run build
      - name: Deploy to Vercel
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
        run: npx vercel --prod --token="$VERCEL_TOKEN"

보안 주의사항

  • 서드파티 액션은 태그(@v4)가 아니라 커밋 SHA로 고정하는 것이 가장 안전합니다. 태그는 다시 붙일 수 있기 때문입니다.
  • permissions:를 최소 권한으로 명시하세요. 기본값이 넓을 수 있습니다.
  • pull_request_target 이벤트는 포크 코드에 시크릿 접근 권한을 줄 수 있어 위험합니다. 쓰지 말아야 할 이유가 없다면 쓰지 마세요.

6.2 브랜치 보호 규칙 / Rulesets

Settings → Rulesets — 기존 Branch protection rules(Settings → Branches)는 폐기되지 않았고 rulesets와 동시에 적용됩니다. 둘 다 걸려 있으면 양쪽을 모두 만족해야 머지됩니다. GitHub는 rulesets 쪽을 권장하며 기존 규칙을 ruleset으로 변환하는 버튼을 제공합니다.

핵심 설정:

  • ✅ Require a pull request before merging — main 직접 push 금지
    • Require approvals: 1~2명
    • Dismiss stale approvals — 새 커밋이 오면 승인 무효화
    • Require review from Code Owners
  • ✅ Require status checks to pass — CI 통과해야 머지 가능
    • Require branches to be up to date — 최신 main 기준으로 테스트
  • ✅ Require conversation resolution — 리뷰 코멘트 전부 해결해야 머지
  • ✅ Require signed commits
  • ✅ Require linear history — 병합 커밋 금지 (squash/rebase만)
  • ✅ Block force pushes ★
  • ✅ Restrict deletions ★

마지막 두 개가 5편에서 말한 --force 사고를 조직 차원에서 원천 차단하는 장치입니다. 개인의 주의력에 의존하지 말고 규칙으로 막으세요. 좋은 팀은 위험한 명령을 "조심해서 쓰자"가 아니라 "아예 못 쓰게" 만듭니다.

CODEOWNERS — .github/CODEOWNERS

text
# 기본 소유자
*                   @team-lead
 
# 경로별
/src/api/           @backend-team
/src/components/    @frontend-team
/.github/workflows/ @devops-team
*.sql               @dba-team

해당 경로가 바뀐 PR에는 지정된 사람/팀이 자동으로 리뷰어로 지정됩니다.

더 깊이

COMMENTS (…)

댓글을 불러오는 중이에요.

NEW COMMENT0 / 1000
⌘↵ 전송

blog92@web:~$ cd ..