CI를 붙였다는 사실보다 중요한 것은 어떤 오류가 발생하면 병합과 배포를 멈출지 명확히 정하는 일이다. PHP 프로젝트에서는 의존성 정의, 문법, 단위 테스트를 서로 다른 단계로 분리하면 실패 지점을 빠르게 찾을 수 있다.
먼저 정할 품질 게이트
최소 게이트는 composer validate, PHP 문법 검사, 단위 테스트 세 가지로 잡는다. 어느 하나라도 0이 아닌 종료 코드를 반환하면 job이 실패하므로, 뒤에 배포 job을 연결하더라도 성공한 코드만 다음 단계로 넘어간다.
- 의존성 검사:
composer.json과 잠금 파일의 일관성 확인 - 문법 검사: 저장소의 PHP 파일을
php -l로 확인 - 회귀 검사: PHPUnit 테스트 실행
- 버전 검사: 운영 후보인 PHP 8.2와 8.3을 matrix로 각각 실행
PHP 버전별 workflow 작성하기
다음 예시는 읽기 권한만 부여하고, 두 PHP 버전에서 같은 검사를 수행한다. 프로젝트의 PHP 지원 범위와 PHPUnit 실행 경로는 실제 composer.json에 맞게 조정한다.
name: php-quality-gate
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3']
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- run: composer validate --strict
- run: composer install --no-interaction --prefer-dist
- name: PHP syntax
run: find . -type f -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l
- name: Unit tests
run: vendor/bin/phpunit
외부 action은 태그보다 커밋 SHA로 고정하면 공급망 변경 위험을 더 줄일 수 있다. 캐시를 추가할 때도 먼저 캐시 없이 성공하는 기준 workflow를 확정한 뒤 최적화하는 편이 문제를 분리하기 쉽다.
성공 후 실패를 주입해 차단 확인하기
- 테스트 브랜치에서 workflow를 실행하고 PHP 8.2와 8.3 job이 모두 성공하는지 확인한다.
- 성공 화면을 캡처해
images/php-matrix-success.png로 저장한다. - 테스트 브랜치에만 실패하는 assertion을 하나 추가한 뒤 다시 실행한다.
- 단위 테스트 단계에서 job이 멈춘 화면을
images/quality-gate-failure.png로 저장한다. - 의도적 실패 코드를 되돌리고 최종 workflow가 다시 성공하는지 확인한다.


CI 결과를 병합 게이트로 연결할 때
로컬에서는 성공하지만 Actions에서 실패한다
PHP 버전, 확장 모듈, 환경 변수와 잠금 파일이 다른지 먼저 비교한다. 테스트가 외부 DB나 시간대에 의존한다면 서비스 컨테이너와 timezone을 workflow에 명시해야 재현성이 높아진다.
한 버전의 실패가 다른 matrix 결과를 가린다
fail-fast: false를 사용하면 한 버전이 실패해도 나머지 버전의 결과를 수집할 수 있다. 호환성 문제인지 공통 코드 문제인지 구분하기 쉬워진다.
검사는 있는데 병합을 막지 못한다
workflow 성공만 확인하지 말고 저장소의 branch protection 또는 ruleset에서 해당 check를 필수 상태 검사로 지정해야 한다.
병합 전 점검 체크리스트
- 지원하는 모든 PHP matrix job이 실행되는가?
- 의존성, 문법과 단위 테스트가 각각 실패 조건인가?
- 의도적인 테스트 실패가 배포 흐름을 중단하는가?
- 저장소 ruleset에서 해당 check를 필수로 지정했는가?
참고 문서
한 줄 요약
PHP CI는 의존성·문법·테스트를 버전별 필수 검사로 만들고, 실제 실패를 주입해 병합 전에 확실히 차단되는지 확인해야 한다.
댓글 0