본문으로 건너뛰기
고급약 45분

플래키 추적과 격리 — 정의·자동 격리·복귀 기준

흔들리는 테스트를 조직 차원에서 관리하는 운영 규칙을 세운다. 플래키를 측정 가능하게 정의하는 방법, 재실행 비교와 이력 집계로 탐지하기, 플래키율 계산, 자동 격리 규칙과 격리가 쓰레기통이 되는 문제를 막는 장치, 복귀 기준, 그리고 플래키 예산으로 상한을 두는 방식을 다룬다.

  • #TestOps
  • #플래키
  • #격리
  • #quarantine
  • #플래키율
  • #테스트신뢰도

개념

이 레슨의 경계 — 원인 진단이 아니라 운영 규칙

m13 레슨 6은 흔들리는 테스트 하나를 붙잡고 왜 흔들리는지 좁히는 방법을 다뤘다. 대기 전략, 공유 상태, 순서 의존, 외부 의존 — 네 관점으로 원인을 분류하고 재시도가 해결책이 아닌 이유를 짚었다. 그 레슨은 테스트 한 건의 진단 이다.

이 레슨은 그 진단을 할 수 있을 만큼의 정보를 조직이 모으고, 진단 전까지 그 테스트를 어떻게 다루고, 누가 언제까지 고치고, 고쳐졌는지 어떻게 판정하는지를 다룬다. 500개 테스트 중 어떤 20개가 흔들리는지 모르면 m13 의 진단 기법은 쓸 자리가 없다. 플래키 관리의 절반은 목록을 갖는 일 이다.

플래키를 측정 가능하게 정의한다

"가끔 깨지는 테스트"는 정의가 아니다. 집계할 수 없고, 따라서 줄었는지도 알 수 없다. 운영에 쓸 정의는 이렇다.

플래키 테스트 — 동일한 환경 좌표와 동일한 커밋에서 실행했을 때 결과가 통과와 실패로 갈리는 테스트.

이 정의의 두 전제가 중요하다. 동일한 커밋 이 아니면 제품 변경이 원인일 수 있고, 동일한 환경 좌표 가 아니면 환경 변경이 원인일 수 있다. 레슨 3의 좌표 기록이 없으면 이 정의를 적용조차 할 수 없다 — 그래서 플래키 관리는 좌표 기록 다음에 온다.

정의에서 명확히 제외해야 할 것도 있다.

현상플래키인가어디로 보내는가
같은 커밋·같은 좌표에서 결과가 갈린다그렇다이 레슨의 장부
특정 커밋 이후로 항상 실패한다아니다제품 결함 또는 테스트 결함 (레슨 7)
특정 시간대에만 실패한다조건부 — 좌표 밖 요인환경 문제로 분류 후 원인 제거 (레슨 3)
환경 장애 시간대의 실패아니다집계 제외 표시 (레슨 1의 소유 규칙)
병렬 수를 올린 뒤 생긴 실패플래키처럼 보이지만 설비 자원 문제일 수 있다러너 자원 확인 먼저 (레슨 2)

마지막 행이 실무에서 가장 자주 오분류된다. 자원이 모자란 러너의 타임아웃은 플래키 장부를 오염시키고, 테스트를 고치려는 헛수고를 만든다. 장부에 넣기 전에 설비를 한 번 본다 는 절차를 규칙에 넣어 두는 것이 좋다.

탐지 — 세 가지 신호를 모은다

플래키는 한 번의 실행으로는 알 수 없다. 같은 조건의 복수 실행을 비교해야 한다. 실무에서 쓰는 신호는 셋이고, 셋 다 두는 것이 가장 좋다.

① 재실행 결과 차이(가장 값싸다). 실패한 테스트를 같은 실행 안에서 한 번 더 돌려 통과하면, 그 건은 "재실행 통과"로 기록된다. 이 기록 자체가 플래키 신호다. 주의할 점은 재실행을 조용히 성공으로 덮지 않는 것 이다. 재시도로 통과한 결과를 그냥 초록으로 보고하면 신호가 사라진다. m55 레슨 3이 리포트에서 이 둘을 가르는 방법을 다뤘고, 여기서는 그 기록을 집계에 쓴다.

② 이력 집계. 같은 테스트의 지난 N 회 결과를 모아 통과·실패가 섞였는지 본다. 커밋이 다른 실행도 섞이지만, 윈도우가 길어지면 제품 변경의 영향은 평균화되므로 실용적으로 쓸 수 있다. 윈도우는 보통 최근 20~50회 실행으로 둔다.

③ 전용 반복 실행. 주기적으로(예: 주 1회 야간) 같은 커밋에서 테스트 묶음을 여러 번 반복 실행한다. 정의에 가장 충실한 신호이고 가장 비싸다. 전체를 반복하지 않고 ①·②로 의심 목록을 뽑은 뒤 그 목록만 10회 반복하면 비용이 감당 가능해진다.

플래키 탐지 파이프라인
 
매 실행      → 재실행 통과 건을 'flaky-signal' 로 기록 (신호 ①)
매일 집계    → 최근 30회 윈도우에서 결과가 섞인 테스트 목록 생성 (신호 ②)
주 1회 야간  → 위 목록만 같은 커밋으로 10회 반복, 갈리는 건을 확정 (신호 ③)
확정 건      → 플래키 장부에 등록, 임계 초과 시 자동 격리 심사

플래키율 — 두 가지를 따로 센다

플래키율이라는 말은 두 가지로 쓰이고, 섞으면 대화가 꼬인다.

테스트 단위 플래키율
  = (윈도우 안에서 결과가 갈린 테스트 수) / (전체 테스트 수)
  → "우리 테스트 중 몇 %가 믿을 수 없는가"  → 기술 부채의 크기
 
실행 단위 플래키 발생률
  = (플래키로 분류된 실패 건수) / (전체 실행 횟수)
  → "한 번 돌릴 때 평균 몇 건의 가짜 실패를 보는가"  → 사람이 겪는 고통의 크기
 
예) 전체 500개, 윈도우 30회, 결과가 갈린 테스트 15개, 그 15개가 30회 동안
    만든 가짜 실패 90건
    테스트 단위 = 15/500 = 3.0%
    실행 단위   = 90/30 = 실행당 3건

둘을 같이 봐야 하는 이유는 개선 효과가 다르게 나타나기 때문이다. 아주 자주 흔들리는 테스트 하나를 고치면 실행 단위는 크게 떨어지지만 테스트 단위는 거의 안 움직인다. 사람의 체감을 빨리 개선하려면 실행 단위 기준 상위 건부터 손대는 것이 맞고, 부채를 실제로 줄이려면 테스트 단위를 추세로 본다.

플래키 장부 — 격리 운영의 단일 출처

장부는 스프레드시트든 저장소 안의 파일이든 상관없지만, 열은 고정해야 한다. 열이 빠지면 그 열에 해당하는 운영이 무너진다.

플래키 장부 (필수 열)
 
테스트 ID | 최초 탐지일 | 최근 30회 실패율 | 상태 | 소유자 | 격리일 | 격리 만료일
| 추정 원인 범주 | 커버하는 시나리오 | 대체 수단
 
상태 값: 관찰중 / 격리 / 복귀심사 / 해결 / 삭제승인

세 열이 특히 중요하고, 셋 다 격리의 부작용을 막기 위해 있다.

  • 소유자 — 비어 있으면 격리를 허용하지 않는다. 주인 없는 격리가 쓰레기통의 시작이다.
  • 격리 만료일 — 격리에는 기한이 있다. 기한이 지나면 자동으로 복귀심사나 삭제승인 중 하나로 넘어간다.
  • 커버하는 시나리오와 대체 수단 — 이 테스트가 격리된 동안 무엇이 검증되지 않는지, 그 공백을 수동 확인이나 다른 테스트로 메우는지. 이 열이 격리의 실제 비용이다.

자동 격리 규칙

격리(quarantine)는 테스트를 지우지 않고 결과가 게이트를 막지 않게 하는 조치다. 테스트는 계속 돌지만 실패해도 빌드를 빨갛게 만들지 않고, 결과는 별도로 집계된다. "끄기"와 다른 점이 여기 있다 — 끄면 데이터가 사라지고, 격리하면 데이터는 계속 쌓인다. 복귀 판정이 가능한 이유도 데이터가 쌓이기 때문이다.

자동 격리 규칙은 임계와 윈도우로 쓴다.

자동 격리 규칙 (예시 — 조직 상황에 맞게 조정)
 
진입 조건 (하나만 충족해도 심사 대상)
- 최근 30회 실행 중 재실행 통과가 3회 이상
- 최근 30회 실행 중 실패율이 10% 이상이면서 같은 커밋에서 갈린 사례가 1회 이상
- 전용 반복 실행 10회에서 결과가 갈린다
 
자동 집행 조건
- 위 진입 조건 + 소유자가 지정됨 → 즉시 격리, 만료일 = 격리일 + 14일
- 소유자 미지정 → 격리하지 않고 '관찰중' 유지 + 팀 채널에 소유자 지정 요청
 
격리 중 동작
- 테스트는 계속 실행하되 게이트에서 제외
- 결과는 '격리 집계' 로 별도 기록 (통과율 지표에서 분리)
- 주간 리뷰 안건에 자동 포함
 
금지
- 전체의 2% 를 넘는 격리는 자동 집행하지 않고 리뷰로 올린다 (플래키 예산)

마지막 줄이 플래키 예산 이다. 격리 가능한 총량에 상한을 두는 장치다. 상한이 없으면 격리는 가장 손쉬운 해결책이 되고, 손쉬운 해결책은 항상 선택된다. 상한에 닿으면 새 격리를 위해 기존 격리를 해결해야 하므로, 격리가 자동으로 유한 자원이 된다.

예산을 넘겼을 때의 조치도 미리 정해 둔다. 보통 "새 기능 작업을 멈추고 플래키 정리에 한 스프린트를 쓴다" 같은 형태가 되고, 이 조항은 사전 합의 없이는 집행되지 않는다. 예산 수치보다 합의 문서가 있다는 사실 이 더 중요하다.

격리가 쓰레기통이 되는 문제

격리 제도를 도입한 조직에서 1년 뒤 관찰되는 전형적 상태는 이렇다. 격리 목록에 40개가 있고, 절반은 누가 왜 넣었는지 모르고, 그중 몇 개는 사실 제품 결함을 숨기고 있다. 마지막 항목이 가장 위험하다 — 격리된 테스트가 진짜 결함을 가리면, 그 결함은 사용자가 발견한다.

이 결과를 막는 장치는 다섯 개이고, 전부 저렴하다.

  1. 소유자 필수 — 주인 없는 격리 금지. 규칙 한 줄로 집행된다.
  2. 만료일 자동 — 기한이 지나면 복귀심사 또는 삭제승인으로 강제 이동.
  3. 총량 상한(예산) — 넘으면 새 격리가 안 된다.
  4. 주간 리뷰 — 격리 목록 전체를 15분 안에 훑는다. 길어졌다면 목록이 너무 큰 것이다.
  5. 격리 중 실패 패턴 확인 — 격리된 테스트가 항상 실패로 바뀌었다면 그건 더 이상 플래키가 아니다. 제품 결함 또는 확정된 테스트 결함이므로 즉시 레슨 7의 트리아지로 보낸다.

다섯 번째가 자주 빠진다. 격리는 "결과를 안 본다"가 아니라 "게이트에서만 뺀다"는 뜻이고, 결과를 계속 보는 것이 격리의 전제다.

복귀 기준

고쳤다고 선언하는 기준이 없으면 격리는 단방향이 된다. 복귀 기준은 두 조건을 모두 요구한다.

복귀 조건
(1) 변경 근거 — 원인을 설명하는 수정 커밋이 있다.
    "요즘 안 깨지더라" 는 근거가 아니다.
(2) 통계 근거 — 같은 커밋에서 20회 연속 통과했다.
    (전용 반복 실행으로 확인. 횟수는 조직의 허용 위험에 따라 10~50회)
 
복귀 후 관찰
- 2주간 '관찰중' 상태로 두고 재발하면 즉시 재격리한다.
- 재격리가 2회 이상이면 '수정' 이 아니라 '설계 변경' 또는 '삭제' 를 검토한다.

(1) 없이 (2)만으로 복귀시키는 것이 가장 흔한 실수다. 통계만 보면 운 좋은 20회일 수 있고, 원인이 그대로 있으면 재발한다. 반대로 (1)만으로 복귀시키면 "고친 줄 알았는데 아니었다"가 된다.

삭제도 정당한 결말 이라는 점을 규칙에 명시해 두는 것이 좋다. 재격리를 반복하는 테스트는 그 시나리오를 다른 층(단위·API·수동 확인)에서 검증하도록 옮기고 지우는 것이 합리적일 때가 많다. 삭제할 때는 장부의 "커버하는 시나리오" 열을 근거로 대체 수단을 남기고, 그 결정을 기록한다. 기록 없는 삭제는 커버리지가 조용히 사라지는 경로다.

실무에서 왜 필요한가

플래키의 비용은 실패 건수가 아니라 신뢰 손실 로 나타난다. 실행당 가짜 실패가 3건이면 사람들은 빨간 결과를 보고 먼저 재실행 버튼을 누르게 되고, 재실행이 습관이 되면 진짜 실패도 두 번 돌려 보고 넘어간다. 그 상태의 자동화는 돌고 있지만 아무것도 막지 않는다. 레슨 1의 성숙도 눈금에서 1단계와 2단계를 가르는 선이 정확히 여기다.

시간 비용도 계산 가능하다. 실행당 가짜 실패 3건, 하루 실행 8회, 건당 분류 5분이면 하루 2시간이다. 여기에 "실패를 보고 멈추는" 심리적 전환 비용이 더 붙는다. 플래키율을 절반으로 줄이는 작업은 보통 새 테스트 20개를 추가하는 작업보다 투자 수익이 크다.

격리 제도가 필요한 이유는 현실적이다. 플래키를 발견한 즉시 고칠 수 있는 조직은 없다. 고칠 때까지 그 테스트가 게이트를 막으면 사람들은 게이트를 무시하기 시작하고, 그게 가장 나쁜 결말이다. 격리는 신뢰를 유지하면서 부채를 명시적으로 쌓는 장치 이고, 명시적으로 쌓은 부채는 관리할 수 있다.

실습 과제

과제 1 — 우리 플래키율 계산하기 (30분)

최근 30회 실행의 결과 데이터를 모아 두 지표를 계산한다.

  1. 테스트 단위 플래키율 — 윈도우 안에서 통과·실패가 섞인 테스트 수를 전체로 나눈다
  2. 실행 단위 플래키 발생률 — 플래키로 분류된 실패 건수를 실행 횟수로 나눈다
  3. 실행 단위 기준으로 상위 5개 테스트를 뽑는다. 이 5개가 전체 가짜 실패의 몇 %를 차지하는가 — 보통 소수가 대부분을 만든다
  4. 상위 5개에 대해 m13 레슨 6의 네 가지 원인 관점으로 범주를 추정해 적는다

과제 2 — 장부와 자동 격리 규칙 작성 (30분)

본문의 필수 열로 플래키 장부를 만들고, 과제 1에서 찾은 테스트들을 등록한다. 각 행마다 소유자를 지정한다 — 지정할 수 없는 행이 있으면 왜인지 적는다.

이어서 자동 격리 규칙을 우리 숫자로 쓴다.

  • 진입 조건의 임계(재실행 통과 횟수·실패율·윈도우 길이)
  • 격리 만료 기간
  • 플래키 예산(전체의 몇 %)과 초과 시 조치
  • 격리 중에도 결과를 계속 보는 방법(어느 화면에 나오는가)

과제 3 — 복귀·삭제 결정 연습 (20분)

장부의 각 행에 대해 복귀·유지·삭제 중 하나를 결정하고 근거를 적는다. 삭제를 고른 행에는 대체 수단(어느 층에서 그 시나리오를 검증할지)을 반드시 쓴다.

마지막에 "격리된 테스트가 항상 실패로 바뀌었는지" 확인하는 점검을 설계한다 — 어떤 쿼리나 화면으로 주 1회 확인할 수 있는가.

자가 체크리스트

  • 플래키를 "같은 커밋·같은 좌표에서 결과가 갈리는 테스트"로 정의해 쓰고 있다
  • 플래키가 아닌 것(특정 커밋 이후 항상 실패·환경 장애·설비 자원 부족)을 구분해 다른 경로로 보낸다
  • 재실행으로 통과한 건이 조용히 초록으로 덮이지 않고 신호로 기록된다
  • 테스트 단위 플래키율과 실행 단위 발생률을 따로 계산할 수 있다
  • 플래키 장부가 있고 소유자·격리 만료일·커버 시나리오 열이 채워진다
  • 자동 격리의 진입 임계와 윈도우 길이가 숫자로 정해져 있다
  • 격리된 테스트가 계속 실행되고 결과가 별도 집계된다 (끈 것이 아니다)
  • 플래키 예산(격리 총량 상한)과 초과 시 조치가 합의되어 있다
  • 복귀 기준이 변경 근거와 통계 근거 둘 다를 요구한다
  • 격리 목록을 주간으로 리뷰하고, 항상 실패로 바뀐 건을 트리아지로 넘긴다

흔한 실수

재시도로 통과한 결과를 성공으로 보고한다. 가장 흔하고 가장 비싸다. 신호가 사라지므로 플래키 목록을 만들 수 없고, 그 상태에서 "우리는 플래키가 없다"는 결론이 나온다. 실제로는 보이지 않을 뿐이다.

격리를 "끄기"로 구현한다. 테스트를 건너뛰면 결과가 쌓이지 않아 복귀 판정이 불가능해진다. 격리는 실행하되 게이트에서 빼는 것이다.

소유자와 만료일 없이 격리한다. 이 둘이 없으면 격리 목록은 반드시 쓰레기통이 된다. 예외 없이 그렇게 된다.

격리된 테스트의 결과를 아무도 안 본다. 격리 중 "항상 실패"로 바뀐 테스트는 제품 결함을 숨기고 있을 수 있다. 주간 점검 한 줄로 막을 수 있는 위험이다.

통계만으로 복귀시킨다. 원인을 설명하는 수정 없이 20회 통과만 보고 복귀시키면 재발률이 높고, 재발은 신뢰를 다시 깎는다.

플래키를 전부 테스트 코드 문제로 가정한다. 설비 자원 부족, 환경 좌표 표류, 공용 기준 데이터 변경이 플래키로 나타나는 경로가 각각 레슨 2·3에 있다. 장부에 넣기 전에 설비와 좌표를 한 번 본다.

참고 자료

  • Google Testing Blog — 플래키 테스트 관련 글 묶음(대규모 환경에서의 플래키 비율과 대응). 개별 글 제목·게시 시점은 원문 확인
  • Microsoft Research / 학계의 flaky test 분류 연구 — 원인 범주와 탐지 기법의 분류 체계. 논문 제목·연도는 원문 확인
  • Playwright·Jest·pytest 공식 문서 — 재시도 설정과 "flaky" 상태 보고 방식. 플래그 이름과 리포트 형식은 버전마다 달라지므로 원문 확인
  • ISTQB CTAL-TAE v2.0 실러버스 — 자동화 테스트의 신뢰성·유지보수 절. 장 번호는 원문 확인
  • 이 커리큘럼 안의 관련 레슨: 모듈 13 레슨 5·6(대기 전략·플래키 원인 진단), 모듈 14 레슨 5(False Alarm 줄이기), 모듈 55 레슨 3(리포트에서 플래키 가르기), 이 모듈 레슨 5(플래키율 추세)·레슨 7(실패 분류)

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
CTAL-TAE — 상급 자격 · 테스트 자동화
상급 테스터 자격 중 자동화 설계를 다루는 과목.

용어집 전체 보기