GitHub Actions, 워크플로우를 왜 2개로 쪼개야 할까

아린·2026년 4월 20일
post-thumbnail

📝 TL;DR

PR이랑 push를 한 워크플로우에 우겨넣으면 커버리지가 이중으로 올라가거나 검증이 빠지는 구멍이 생긴다. 역할별로 두 개로 쪼개고 path filter까지 살짝 얹어주면 머리도 편하고 CI 실행 시간도 줄어든다.

🔍 상세

배경 / 동기

팀 프론트 저장소에 CI를 새로 깔면서 기존 저장소들을 구경하는데, 어떤 곳은 워크플로우가 2개였고 어떤 곳은 1개였다. opened만 쓰는 곳, synchronize까지 쓰는 곳도 섞여 있었다. "이게 다 무슨 차이지?" 싶어서 한번 정리해봤다.

push랑 pull_request, 뭐가 다른데?

이름만 보면 비슷해 보이지만, 둘은 성격이 꽤 다르다.

항목pushpull_request
트리거 시점커밋이 브랜치에 push될 때PR 생성/업데이트 시
GITHUB_SHApush된 커밋 그 자체PR의 머지 시뮬레이션 커밋
GITHUB_REFrefs/heads/브랜치명refs/pull/NUMBER/merge
테스트 대상현재 브랜치 코드base와 머지했을 때의 코드

pull_request는 좀 똑똑한 친구라서 "이 PR 머지되면 어떻게 될까?"를 미리 시뮬레이션한 커밋을 만들어놓고 돌린다. 그래서 PR 검증엔 이쪽이 훨씬 어울린다.

두 워크플로우의 역할 분리

1) test_and_coverage.yml (push 트리거)

on:
  push:
    branches:
      - master
      - develop
      - 'feature/*/main'
    paths:
      - 'src/**'
      - 'package.json'
      - 'yarn.lock'
      - 'tsconfig*.json'
      - 'vitest.config.*'

역할은 테스트 돌리고 커버리지를 Codecov에 업로드하는 것. 커버리지 숫자는 base 브랜치 기준으로 쭉 쌓여야 트렌드가 보이는데, PR마다 올려버리면 base 숫자가 흔들려서 영 의미가 없어진다.

2) pull_request_stats.yml (pull_request 트리거)

on:
  pull_request:
    types: [opened, synchronize]

이쪽은 Lint, Test, Build를 병렬로 돌리는 PR 게이트키퍼. 개발자가 PR 페이지 열었을 때 초록불/빨간불 바로 보여주는 담당이다.

opened만? 아니면 synchronize까지?

여기가 은근히 재밌는 설계 갈림길이다.

패턴pull_request_stats 트리거조건
A[opened]test_and_coverage모든 push를 커버할 때
B[opened, synchronize]test_and_coverage특정 브랜치만 커버할 때

패턴 A에서는 feature 브랜치에 push해도 test_and_coverage가 알아서 잡아주니까 synchronize가 없어도 괜찮다. 반대로 패턴 B에서는 master/develop/feature/*/main 같은 주요 브랜치만 잡기 때문에, 일반 feature 브랜치로 push한 커밋은 아무도 검증하지 않는 빈틈이 생긴다. 이걸 synchronize 없이 놔두면 PR 처음 열 때만 검사하고 그다음 커밋부턴 무사통과되는 사태가 벌어진다.

즉, test_and_coverage가 커버하는 브랜치 범위가 pull_request_stats의 트리거를 결정한다. 이 한 쌍이 핵심 축.

path filter로 불필요한 실행 줄이기

GitHub 공식 문서에서도 밀어주는 기능인데, 꽤 쏠쏠하다. README 한 줄 고치는데 테스트 30분 돌릴 이유가 없잖아?

paths:
  - 'src/**'              # 소스 코드
  - 'package.json'        # 의존성
  - 'yarn.lock'           # 의존성 락파일
  - 'tsconfig*.json'      # TS 설정
  - 'vitest.config.*'     # 테스트 설정

단, 함정이 하나 있다. branches랑 paths를 같이 쓰면 둘 다 만족할 때만 실행된다. 이 규칙 모르면 "아니 왜 내 워크플로우가 안 돌지?" 하고 한참 헤매게 된다. (경험담 아님.)

주의할 점

  • || true로 lint 실패 삼키지 말기: biome ci src || true 이렇게 쓰면 lint가 뭐라 해도 job은 초록불이다. 경고만 띄우고 싶으면 continue-on-error: true 쪽이 의도가 훨씬 선명하다.
  • Yarn 4(Corepack) 환경 체크: 옛날 워크플로우에서 자주 보이는 npm install --global yarn은 Yarn 4에선 안 먹힌다. corepack enable + yarn install --immutable 조합으로 갈아줘야 한다.

💡 적용 포인트

  • 새 저장소에 CI 깔 때, 기존 워크플로우를 "왜 이렇게 나눠놨지?" 먼저 읽어보고 들고 오기. 복붙은 쉽지만 트리거 전략은 복붙거리가 아니다.
  • test_and_coverage의 브랜치 범위랑 pull_request_statssynchronize 포함 여부, 이 한 쌍이 전체 설계의 핵심 축이다.

📚 참고

profile
💻 FE Developer

0개의 댓글