ゲートとは「止める基準」を決める仕事だ
한국어 원문으로 표시합니다.
한 줄 요약
테스트 게이트의 본질은 테스트를 많이 돌리는 게 아니라, 어떤 신호가 나왔을 때 병합을 막을지 미리 합의하고 그 판정을 기계가 대신하게 만드는 일이다.
왜 이게 필요했나
결함은 늦게 잡을수록 비싸진다. IBM 연구로 널리 인용되는 배수는 개발 단계 1, QA 10, 프로덕션 100 이상이다. 같은 버그라도 내 브랜치에서 잡으면 30분이면 끝나지만, 프로덕션에서 잡으면 장애 대응·핫픽스·회고·고객 커뮤니케이션까지 붙는다. 게이트는 이 비용 곡선의 왼쪽 끝에서 잡으려고 두는 장치다.
문제는 어떤 테스트를 얼마나 두느냐다. 테스트 피라미드의 권장 비율은 유닛 70 / 통합 20 / E2E 10 이다. 이게 뒤집힌 역피라미드(아이스크림 콘)가 되면 네 가지가 한꺼번에 온다. E2E 는 느려 피드백 루프가 길어지고, 플레이키가 빈번하며, 실패해도 어디가 원인인지 파악이 어렵고, 유지보수 비용이 기하급수적으로 는다. 테스트가 많아졌는데 아무도 결과를 믿지 않는 상태가 바로 이 모양이다.
어떻게 동작하나
게이트는 세 부분으로 나뉜다.
첫째, 판정 기준. 커버리지는 핵심 비즈니스 로직 80% 이상, 전체 60~80% 를 목표로 잡되, 100% 커버리지보다 의미 있는 assertion 을 가진 테스트가 더 중요하다는 원칙을 잊으면 안 된다. 숫자만 맞추는 테스트는 커버리지 리포트만 예쁘게 만들고 버그는 그대로 통과시킨다.
둘째, 차단 정책. 유닛과 통합은 반드시 차단한다. E2E 는 주요 흐름만 차단하고 나머지는 관찰로 둔다. 성능은 임계치를 넘을 때만, 보안 스캔은 CRITICAL 만 차단한다. 카오스 테스트는 비차단으로 두고 지표만 본다. 전부 차단으로 걸면 사람들이 게이트를 우회하는 방법을 먼저 배운다.
셋째, 강제 지점. 게이트가 실제로 강제되는 곳은 파이프라인 스크립트가 아니라 브랜치 보호의 required status checks 다. 스크립트는 결과만 만들고, 병합을 막는 권한은 저장소 설정이 갖는다. IaC 쪽에도 같은 판이 있다. Plan 은 PR 에서 보여 주고 Apply 는 merge 후 자동 실행한다.
플레이키 테스트는 게이트의 신뢰를 갉아먹는 주범이라 따로 다뤄야 한다. 고정 sleep 대신 특정 조건을 기다리는 명시적 대기를 쓰고, 테스트 간 의존성을 끊고, 네트워크는 모킹하거나 상한이 있는 재시도로 감싸고, 날짜 같은 동적 데이터는 고정한다. 재시도는 반드시 상한이 있어야 한다. 무한 재시도는 안정화가 아니라 실패를 감추는 것이다. 그래도 흔들리는 테스트는 격리 목록에 넣어 병합을 막지 않게 하되, 격리됐다는 사실을 리포트에 남겨 잊히지 않게 한다.
현장에서 만나는 모습
게이트를 처음 켜면 반드시 "지금 당장 급한데 이것 때문에 못 나간다"는 요청이 온다. 그때 필요한 건 예외 승인 절차이지 게이트를 끄는 스위치가 아니다. 또 하나 자주 보는 장면은 실패한 테스트를 고치는 대신 재시도 횟수를 늘리는 것이다. 재시도 횟수는 늘어나는데 실패율은 그대로라면, 그 테스트는 이미 신호가 아니라 소음이다.
게이트는 빨라야 지켜진다
사람들이 게이트를 우회하는 가장 큰 이유는 규칙이 엄격해서가 아니라 느려서다. 병합까지 40분을 기다려야 하면 개발자는 작은 변경을 모아 큰 덩어리로 올리게 되고, 큰 덩어리는 리뷰가 어렵고 되돌리기도 어렵다. 게이트의 목적이 정확히 그 반대였다는 점에서 이것은 단순한 불편이 아니라 설계 실패다.
속도를 얻는 방법은 정해져 있다.
- 변경된 것만 돈다. 모노레포라면 어떤 패키지가 바뀌었는지 보고 그 영향 범위만 시험한다. 단일 저장소라도 문서만 바뀐 커밋에 전체 시험을 돌릴 이유는 없다.
- 단계를 나눈다. 빠른 것을 먼저 돌려 몇 분 안에 실패를 알리고, 느린 것은 그 뒤에 둔다. 앞 단계에서 떨어지면 뒤는 아예 시작하지 않으므로 평균 대기 시간이 크게 줄어든다.
- 병렬로 돌린다. 시험을 여러 갈래로 나눠 동시에 실행한다. 이때 시험끼리 상태를 공유하면 병렬화가 곧 불안정으로 바뀌므로, 각 시험이 자기 자원을 스스로 만들고 치우는 것이 전제다.
- 캐시한다. 의존성 설치가 매번 몇 분씩 걸린다면 그것부터 고친다. 시험 코드를 줄이는 것보다 효과가 크다.
시간을 재는 것도 잊지 말아야 한다. 파이프라인 각 단계의 소요 시간을 기록해 두면, 어느 날 갑자기 느려졌을 때 원인을 찾을 수 있다. 이 기록이 없으면 "요즘 CI 가 느리다" 는 말만 반복되고 아무도 무엇이 언제부터 느려졌는지 답하지 못한다.
마지막으로 게이트의 성패를 가르는 문화적인 조건이 하나 있다. 빨간 파이프라인을 방치하지 않는 것이다. main 이 깨진 채로 하루가 지나면, 그 뒤에 올라오는 모든 변경이 자기 잘못인지 아닌지 알 수 없게 되어 게이트가 신호로서의 기능을 완전히 잃는다. 깨졌으면 고치거나 되돌리는 것이 다른 어떤 작업보다 우선한다는 합의가 필요하다. 되돌리는 쪽이 거의 언제나 빠르므로, 원인을 아직 모르더라도 일단 되돌려 main 을 초록으로 만들어 놓고 조사하는 것이 정석이다.
다음 실습에서 할 것
테스트 러너와 커버리지 게이트, 재시도, 격리를 셸로 직접 만든다. 실패해도 나머지를 끝까지 돌려 전체 그림을 보여 주는 러너, 경계값에서 정확히 판정하는 커버리지 게이트, 상한이 있는 재시도, 그리고 수치와 판정이 일치하는 JSON 리포트를 만들어 본다.