PR은 리뷰 요청이 아니라 배포 실패를 막는 첫 번째 검증 지점이다
1. 배포 실패를 main에서 발견했던 경험
이전에는 기능을 구현하고 PR을 만든 뒤, main 브랜치에 머지하고 배포하는 흐름을 자연스럽게 생각했다. PR은 주로 코드 리뷰를 받기 위한 공간이라고 봤고, 실제 문제가 있는지는 배포 과정에서 확인되는 경우가 많았다.
그런데 한 번은 기능 구현을 마친 뒤 main에 머지하고 배포를 진행했는데, 배포 단계에서 실패가 발생했다. 이미 main 브랜치에 반영된 뒤에야 문제가 드러났다. 결국 다시 수정하고, 다시 커밋하고, 다시 배포해야 했다.
문제 자체도 번거로웠지만 더 크게 느낀 점은 피드백 루프가 너무 길다는 것이었다. 기능 브랜치에서 바로 알 수 있었던 문제를 main에 들어간 뒤에야 발견했고, 그만큼 수정 비용도 커졌다.
이 경험에서 배웠다. 실패는 가능한 한 main 브랜치에 들어가기 전에 발견해야 한다. 배포 단계는 마지막 확인 지점이지, 처음으로 문제가 발견되는 장소가 되면 안 된다.
2. PR의 역할을 다시 보다
처음에는 PR을 코드 리뷰 요청으로만 생각했다. 내가 작성한 코드를 팀원에게 보여주고, 구조나 로직을 두고 피드백을 받는 공간이라고 봤다.
하지만 협업 관점에서 보면 PR은 그보다 더 앞에 있는 검증 지점이다. main 브랜치에 들어가기 전에 이 코드가 최소한 빌드 가능한지, 테스트가 깨지지 않는지, 배포 가능한 기본 상태인지를 확인하는 관문이다.
사람이 봐야 하는 영역이 있고, 기계가 먼저 확인할 수 있는 영역이 있다. 빌드가 되는지, 테스트가 통과하는지, 정적 분석 기준을 만족하는지는 사람이 리뷰하기 전에 자동으로 확인할 수 있다.
PR 단계에서 실패가 발견되면 main 브랜치를 건드리지 않고 기능 브랜치에서 수정하면 된다. 반대로 main에 들어간 뒤 실패가 발견되면 이미 기준 브랜치가 불안정해진 상태에서 문제를 해결해야 한다.
그래서 이제 PR은 단순한 리뷰 요청이 아니라, main에 들어가기 전 첫 번째 자동 검증 지점이라고 생각하게 되었다.
3. GitHub Actions가 PR에서 검증하는 것
현재 프로젝트의 GitHub Actions 설정을 확인해보면 .github/workflows/jekyll.yml 하나가 있다.
이 workflow의 이름은 Deploy Jekyll site to Pages이고, 실행 조건은 다음과 같다.
1
2
3
4
5
on:
push:
branches: ["master"]
workflow_dispatch:
현재 설정은 PR이 아니라 master 브랜치에 push될 때 실행된다. GitHub Actions 탭에서 수동으로 실행할 수도 있다.
실제 workflow는 크게 두 단계로 나뉜다.
첫 번째는 build job이다. 여기서는 코드를 체크아웃하고 Ruby 3.1 환경을 설정한 뒤 Bundler 캐시로 의존성을 준비한다. 그 다음 Jekyll 사이트를 빌드한다.
중요한 빌드 명령어는 다음과 같다.
1
bundle exec jekyll build --baseurl "$"
이 명령어는 Jekyll 사이트가 실제로 빌드 가능한 상태인지 확인한다. 블로그 글, 레이아웃, 설정, 플러그인 문제로 빌드가 실패하면 이 단계에서 잡을 수 있다.
두 번째는 deploy job이다.
1
uses: actions/deploy-pages@v4
deploy job은 build job이 성공한 뒤에 실행된다. 현재 구조에서는 master에 반영된 뒤 Jekyll 빌드를 하고 성공하면 GitHub Pages로 배포하는 흐름이다.
다만 현재 workflow에는 pull_request 이벤트가 없다. 그래서 PR을 만들었을 때 자동으로 빌드 검증이 실행되지는 않는다. 또한 Docker build나 Gradle build/test 같은 백엔드 빌드 명령은 이 레포의 workflow에는 없다.
현재 프로젝트 기준으로 보면 이 Actions 설정은 “PR 검증용 CI”라기보다는 “master push 이후 GitHub Pages 빌드 및 배포 workflow”에 가깝다.
4. PR 단계에서 검증해야 하는 이유
PR 단계에서 자동 검증이 필요한 이유는 main 브랜치를 보호하기 위해서다.
기능 브랜치에서는 실패해도 수정하면 된다. 하지만 main 브랜치에 들어간 뒤 실패하면 이야기가 달라진다. main은 팀이 공유하는 기준 브랜치이고, 배포의 출발점이 되는 경우가 많다. 이 브랜치가 깨지면 개인의 문제가 아니라 팀 전체의 문제가 된다.
GitHub Actions의 목적은 단순히 자동으로 명령어를 실행하는 것이 아니라, 문제가 main 브랜치에 들어가기 전에 발견되도록 검증 시점을 앞당기는 것이다.
PR 단계에서 빌드와 테스트를 실행하면 배포 실패를 더 이른 시점에 발견할 수 있다. 리뷰어도 “이 코드가 일단 빌드는 되는가?” 같은 기본적인 걱정을 덜고, 구조나 책임 분리 같은 더 중요한 부분에 집중할 수 있다.
또 로컬 환경에서는 잘 되던 코드가 CI 환경에서 실패하는 경우도 있다. 로컬에는 캐시가 남아 있거나, 특정 버전의 도구가 이미 설치되어 있거나, 환경 변수가 다르게 설정되어 있을 수 있다. CI는 더 깨끗한 환경에서 실행되기 때문에 “내 컴퓨터에서는 됐는데” 문제를 줄이는 데 도움이 된다.
PR 검증은 단순한 자동화가 아니라 협업 안정성을 높이는 장치다.
5. 사람의 코드 리뷰와 GitHub Actions의 역할 차이
GitHub Actions와 코드 리뷰는 서로 대체 관계가 아니다. 둘은 확인하는 영역이 다르다.
GitHub Actions는 기계적으로 판단할 수 있는 것을 검증한다.
- 빌드가 되는가
- 테스트가 통과하는가
- 정적 분석 기준을 만족하는가
- 배포 가능한 기본 상태인가
반면 코드 리뷰는 사람이 봐야 하는 영역을 검토한다.
- 책임 분리가 적절한가
- 도메인 규칙이 잘 드러나는가
- 예외 처리가 명확한가
- 유지보수하기 좋은 구조인가
- 협업자가 이해하기 쉬운 코드인가
빌드가 실패하는 코드를 사람이 먼저 리뷰할 필요는 없다. 그런 부분은 CI가 먼저 걸러주는 것이 좋다. 그리고 CI가 통과한 뒤에야 리뷰어는 코드의 의도와 구조에 더 집중할 수 있다.
그래서 좋은 PR 흐름은 GitHub Actions가 기본 검증을 맡고, 사람의 리뷰가 설계와 가독성, 유지보수성을 보는 구조라고 생각한다.
6. 현재 프로젝트에 적용해보며 느낀 점
현재 프로젝트의 workflow는 master 브랜치에 push되거나 수동 실행될 때 동작한다. PR 이벤트에서는 실행되지 않는다.
workflow 안에서는 Ruby 환경을 설정하고 Bundler 캐시를 쓴 뒤 다음 명령어로 Jekyll 사이트를 빌드한다.
1
bundle exec jekyll build --baseurl "$"
그리고 빌드 결과물을 GitHub Pages artifact로 업로드한 뒤, deploy-pages 액션으로 배포한다.
이 구조는 master에 반영된 코드가 실제로 Jekyll 빌드 가능한지 확인하고 배포까지 이어주는 역할을 한다. 다만 PR 단계에서 실행되는 workflow가 아니기 때문에, main에 해당하는 master 브랜치에 머지되기 전에 자동으로 빌드 실패를 막아주지는 않는다.
그래서 이 프로젝트에 PR 검증 흐름을 추가한다면, 배포 workflow와는 별도로 pull_request 이벤트에서 실행되는 CI workflow를 두는 것이 좋아 보인다. 예를 들어 PR 단계에서는 배포를 하지 않고 Jekyll 빌드만 실행해도 충분히 의미가 있다.
또 package.json을 보면 npm test가 JS lint와 SCSS lint를 실행하도록 되어 있다. 현재 GitHub Actions workflow에서는 이 명령어를 실행하지 않지만, PR 검증 workflow에 포함하면 스타일이나 스크립트 관련 문제도 main에 들어가기 전에 잡을 수 있다.
배포 관련 검증과 PR 검증은 목적이 다르다. 배포 workflow는 실제 배포를 수행하는 흐름이고, PR 검증 workflow는 배포 전에 문제가 있는 코드를 걸러내는 흐름이다. 둘을 분리해서 보면 Actions의 역할이 더 명확해진다.
7. 오늘의 결론
PR은 코드 리뷰 요청이면서 동시에 main에 들어가기 전 검증 관문이다.
GitHub Actions는 main 브랜치를 보호하는 안전장치다. CI의 핵심은 실패를 늦게 발견하는 것이 아니라 더 이른 시점에 발견하는 것이다.
예전에는 배포가 실패하면 그때 고치면 된다고 생각했다. 하지만 main에 들어간 뒤 실패가 발견되면 수정 비용이 커지고, 다시 배포해야 하며, 팀의 기준 브랜치도 불안정해진다.
배포 실패를 경험하고 나서야 CI가 단순한 편의 기능이 아니라 main 브랜치를 보호하는 안전장치라는 것을 이해했다.
앞으로는 PR을 만들 때 단순히 “리뷰 부탁드립니다”라고 생각하기보다, “이 코드가 main에 들어가도 되는지 먼저 검증받는 단계”라고 생각해야겠다.
