이 글에서 얻는 것
- CI를 “돌아가기만 하는 자동화”가 아니라, 품질 게이트 + 재현 가능한 빌드로 설계할 수 있습니다.
- GitHub Actions의 트리거/캐시/아티팩트/잡 분리를 활용해 빠르고 안정적인 파이프라인을 구성할 수 있습니다.
- PR과 main 배포 경로를 분리해 시크릿/권한 사고를 줄이고, 실패 원인을 쉽게 재현할 수 있게 만듭니다.
0) 좋은 CI의 기준(실무 감각)
- 빠르다: PR마다 부담 없이 돌릴 수 있어야 한다
- 결정적이다: 같은 코드면 같은 결과(플레이키 최소화)
- 재현 가능하다: 실패하면 로컬/CI에서 원인을 좁힐 수 있다
- 안전하다: PR에서 시크릿/배포 권한이 노출되지 않는다
1) 기본 워크플로: PR 검증 + main 검증(필수)
기본 뼈대:
- 트리거:
pull_request,push(main) - 단계: checkout → 런타임 세팅 → 캐시 → 테스트/빌드 → 리포트(아티팩트)
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: gradle
- run: ./gradlew test jacocoTestReport
- uses: actions/upload-artifact@v4
with:
name: reports
path: build/reports/**
2) 속도 최적화: 캐시와 잡 분리
- Gradle/Maven 캐시를 쓰면 PR 피드백이 빨라집니다.
- 테스트/빌드/정적 분석을 한 잡에 다 넣으면 “느린 단계”가 전체를 잡아먹습니다.
필요하면 잡을 분리해 병렬로 돌립니다(예: unit test vs lint).
3) 트리거 최적화: 불필요한 실행 줄이기
- 문서 변경만 있을 때는
paths-ignore로 CI를 생략할 수 있습니다. - 브랜치 보호 규칙과 함께 “필수 체크”를 정해두면 main이 더 안전해집니다.
4) 로그/리포트: 실패 원인을 남기는 것이 CI의 가치
- Gradle은
--stacktrace --info로 실패 원인을 더 잘 남길 수 있습니다(노이즈와 트레이드오프). - 테스트 리포트/커버리지 리포트를 아티팩트로 남기면 PR에서 빠르게 확인할 수 있습니다.
5) 보안: PR에서 시크릿을 쓰지 않게 설계하라
가장 흔한 사고:
- PR 빌드에서 이미지 push/배포를 하려다 시크릿이 필요해지고,
- fork PR에서 시크릿이 노출되거나 파이프라인이 깨집니다.
권장:
- PR: 테스트/빌드/정적 분석까지만
- main: 이미지 push/배포 같은 “권한 필요 작업”
6) 확장 패턴(필요해질 때 도입)
- 통합 테스트 잡: Testcontainers(DB/Redis) 포함
- 아티팩트/이미지 생성: JAR 업로드 또는 Docker image push(조건부)
- 알림/리그레션 감지: 실패 시 슬랙/디스코드, flaky 테스트 추적
7) 자주 하는 실수
- CI가 느려져서 결국 안 돌리게 되는 경우(잡 분리/캐시 부재)
- PR에서 시크릿이 필요한 작업을 실행하려는 경우(권한 설계 문제)
- 실패 로그가 너무 적어서 원인을 못 찾는 경우(리포트/아티팩트 부재)
연습(추천)
- PR에서는
test만, main에서는test + build + image push가 되게 워크플로를 분리해보기 - 단위 테스트와 통합 테스트(Testcontainers)를 잡으로 분리하고, PR에서는 단위만 실행해보기
- 실패한 테스트의 리포트를 아티팩트로 남겨 PR에서 바로 확인 가능하게 만들기
💬 댓글