본문으로 건너뛰기
입문약 18분

완전한 테스트는 불가능하다 — 한계와 기대치 조정

왜 모든 입력 조합을 테스트할 수 없는지 숫자로 확인하고, 버그 없음을 보장해달라는 요청에 QA가 어떻게 답해야 하는지 연습한다.

  • #테스트한계
  • #소진적테스트
  • #리스크기반
  • #QA기초

개념

먼저 결론부터

이 레슨의 결론은 한 문장이다. 테스트는 전수 검사가 아니라 표본 추출이다.

QA를 처음 시작할 때 가장 흔한 오해가 "테스트란 모든 경우를 다 확인해서 버그가 없음을 확인하는 일"이라는 것이다. 이건 불가능하다 — 시간이 부족해서가 아니라 수학적으로 불가능하다. 그러면 QA는 무엇을 하는 사람인가? 다 볼 수 없는 공간에서 어디를 볼지 근거를 갖고 고르고, 무엇을 안 봤는지까지 말할 수 있는 사람이다.

이 레슨은 그 "불가능"을 숫자로 확인하고, 거기서 자연스럽게 나오는 세 가지 실무 능력으로 이어진다.

  1. 조합이 왜 폭발하는지 계산으로 설명하기
  2. 무엇을 기준으로 표본을 고를지 축을 갖기
  3. "버그 없이 나가죠?"에 근거 있게 답하기

조합은 덧셈이 아니라 곱셈으로 늘어난다

간단해 보이는 폼도 모든 입력 조합을 따지면 숫자가 순식간에 감당 못 할 크기가 된다. 핵심은 조합이 더해지는 게 아니라 곱해진다는 점이다. 필드를 하나 추가하면 케이스가 몇 개 늘어나는 게 아니라 몇 배가 된다.

회원가입 폼에 필드 5개(이메일, 비밀번호, 닉네임, 생년월일, 약관 동의)가 있고, 각 필드에 "유효값 / 경계값 / 비유효값" 정도로만 값을 잡아도 필드당 최소 3가지 경우가 나온다.

5개 필드 × 3가지 경우 = 3⁵ = 243가지 조합. 아직은 해볼 만해 보인다. 하지만 여기서 멈추면 실무 감각이 생기지 않는다. 진짜 문제는 이 숫자가 현실에서 얼마나 더 커지느냐다.

실무 규모로 다시 계산해보자

같은 회원가입 화면을 실무에서 보는 방식으로 다시 세어보자. 한 줄씩 현실적인 조건을 더할 때마다 숫자가 어떻게 되는지가 이 표의 전부다.

단계무엇을 더했나배수누적 조합 수
1필드 5개 × 값 3가지—243
2값을 실무 수준으로(필드당 6가지)×327,776
3회원 상태 3가지(비로그인 / 초대링크 / 휴면복구)×323,328
4디바이스·브라우저 조합 8가지×8186,624
5언어·통화 4가지×4746,496
6앞뒤로 이어지는 화면 3개(각 20가지 경로)×8,000약 60억

2단계의 "필드당 6가지"는 부풀린 숫자가 아니다. 이메일 하나만 봐도 정상값, 빈값, @ 없음, 도메인 없음, 최대 길이 초과, 앞뒤 공백 포함, 대문자 혼용, 이미 가입된 주소… 6가지는 오히려 후한 편이다.

이제 시간으로 바꿔보자. 이 60억 가지를 완전히 자동화해서 한 건당 10초에 돌린다고 하자.

  • 60억 × 10초 = 600억 초 ≈ 약 1,900년
  • 장비 100대를 병렬로 돌려도 약 19년

기능 하나짜리 화면 묶음에 19년이다. 그사이 제품은 수백 번 바뀐다. 그래서 소진적 테스트(exhaustive testing, 모든 입력과 조건의 조합을 다 실행하는 것)는 현실적으로 어렵다가 아니라 원리적으로 불가능하다.

필드 하나로도 이미 불가능하다

조합을 여러 개 곱하지 않아도 결론은 같다. 숫자를 입력받는 필드가 딱 하나만 있다고 하자. 32비트 정수라면 가능한 값은 2³² = 약 42억 9천만 가지다. 초당 1,000건을 검사해도 약 50일이 걸린다. 필드가 두 개면 1.8×10¹⁹ 가지 — 같은 속도로 5억 8천만 년이다.

여기에 아직 아무것도 안 더했다. 시간(자정 넘김, 윤년, 서머타임), 순서(뒤로가기, 새로고침, 두 번 클릭), 동시성(같은 계정으로 두 창에서 동시에), 네트워크 상태(끊김, 느림, 중복 응답)… QA가 실제로 마주치는 결함의 상당수는 이런 입력 값이 아닌 차원에서 나온다. 그 차원까지 곱하기 시작하면 숫자는 세는 의미가 없어진다.

데이크스트라의 말 — 테스트가 증명할 수 있는 것

그래서 에츠허르 데이크스트라(Edsger Dijkstra)의 유명한 말이 있다.

"테스트는 결함이 있다는 것을 보여줄 수 있을 뿐, 결함이 없다는 것을 보여주지는 못한다." (Testing shows the presence of defects, not their absence)

논리적으로 보면 이유가 분명하다. 테스트 한 건이 실패하면 "결함이 있다"는 결론이 곧바로 나온다. 반례 하나면 충분하기 때문이다. 반대로 테스트가 전부 통과했을 때 나오는 결론은 "결함이 없다"가 아니라 "내가 시도한 조건에서는 문제가 보이지 않았다" 뿐이다. 60억 중 500건을 통과시킨 것으로 나머지 59억 9999만 건을 보증할 수는 없다.

이 비대칭을 몸에 익히는 것이 이 레슨의 핵심이다. 그래서 결함 리포트에 "버그 있음"은 단정해서 쓰고, 통과 보고에 "버그 없음"은 절대 단정해서 쓰지 않는다.

그래서 테스트는 표본 추출이다

모든 조합을 볼 수 없으니, QA는 어디를 볼지 전략적으로 고른다. 여론조사를 떠올리면 감이 온다. 5천만 명에게 다 물어볼 수 없으니 1,000명을 뽑아 묻는다. 이때 결과의 품질을 결정하는 건 "몇 명에게 물었나"보다 "어떻게 뽑았나" 다. 편향된 1,000명은 무의미하고, 잘 뽑은 1,000명은 꽤 정확하다.

테스트도 똑같다. 케이스를 1,000건 실행했다는 사실 자체는 품질을 말해주지 않는다. 그 1,000건을 무슨 근거로 골랐는지가 품질을 말해준다. 여기서 QA의 일이 "많이 클릭하는 일"에서 "고르는 일"로 바뀐다.

무엇을 기준으로 표본을 고르는가 — 다섯 가지 축

표본을 고르는 기준은 하나가 아니다. 실무에서는 아래 축들을 겹쳐서 쓴다. 지금은 각 축의 상세 기법을 배우는 자리가 아니라 어떤 축들이 있는지 지도를 갖는 자리다. 각 축이 어디서 자세히 다뤄지는지는 오른쪽 열에 적었다.

축던지는 질문대표적인 접근자세히는
입력 값의 구조같은 취급을 받는 값 무리는 어디까지인가, 경계는 어디인가동등분할, 경계값 분석, 결정 테이블, 페어와이즈모듈 3
사용 빈도사용자가 실제로 많이 지나는 길은 어디인가사용 통계·운영 프로파일 기반 우선순위모듈 4
리스크망가졌을 때 손해가 큰 곳은 어디인가발생 가능성 × 영향도모듈 4
결함 이력과거에 버그가 몰렸던 곳은 어디인가결함 밀집(defect clustering) 분석모듈 6
변경 이력이번 릴리즈에서 실제로 바뀐 곳은 어디인가변경 영향 분석, PR diff 읽기모듈 9

이 중 결함 이력 축은 조금 반직관적이라 짚어둘 만하다. 결함은 코드 전체에 고르게 흩어지지 않고 특정 모듈에 몰리는 경향이 있다. 복잡하거나, 자주 바뀌거나, 여러 사람이 손댔거나, 급하게 만들어진 곳이다. 그래서 "지난 분기 결함이 가장 많았던 화면 3개"는 다음 분기에도 결함이 나올 확률이 높다. 이걸 결함 밀집(defect clustering) 이라고 부른다. 표본을 고를 때 과거 결함 이력만큼 값싸고 강한 근거가 드물다.

축을 여러 개 겹치면 우선순위가 자연스럽게 나온다. 예를 들어 "이번에 코드가 바뀌었고(변경) + 결제 흐름이며(리스크) + 작년에 결함이 3건 났던 화면(이력)"은 세 축에서 동시에 걸린다. 그런 곳에 시간을 몰아주는 것이 표본 추출 전략이다.

같은 테스트를 반복하면 효과가 줄어든다

표본을 잘 골랐다고 끝이 아니다. 같은 표본을 계속 쓰면 그 표본의 효과가 시간이 지날수록 떨어진다.

같은 테스트 스위트를 계속 똑같이 돌리면, 그 스위트가 잡을 수 있는 결함은 이미 다 잡혀서 더 이상 새로운 결함을 못 찾는다 — 이걸 살충제 패러독스(Pesticide Paradox) 라고 부른다. 같은 살충제를 계속 쓰면 벌레가 내성이 생기는 것에 빗댄 표현이다.

여기서 오해하기 쉬운 지점이 있다. 살충제 패러독스는 "회귀 테스트가 쓸모없다"는 뜻이 아니다. 기존 스위트는 이미 고친 것이 다시 깨지지 않았음을 확인하는 안전망으로서 여전히 유효하다(모듈 1 레슨 4의 회귀 테스트). 다만 그 스위트는 새로운 결함을 찾는 도구로는 점점 무력해진다. 두 가지 역할을 구분하지 않으면 "회귀 스위트가 다 통과했으니 품질이 좋다"는 잘못된 안심에 빠진다.

사례 연구 — 3년째 100% 통과하는 회귀 스위트

살충제 패러독스가 실무에서 어떤 모습으로 나타나는지, 하나의 흐름으로 따라가 보자.

상황. 이커머스 결제팀의 회귀 스위트는 420건이다. 매 릴리즈마다 자동으로 돌고, 최근 1년간 통과율은 거의 항상 100%였다. 팀은 이 숫자를 대시보드에 띄워두고 자랑스러워했다.

문제. 그런데 최근 6개월 동안 프로덕션(사용자가 실제로 쓰고 있는 환경) 장애가 5건 났다. 포인트 부분사용 시 잔액이 음수로 찍히는 문제, 간편결제 재시도에서 이중 결제가 되는 문제, 정기결제 갱신일이 월말에 밀리는 문제 등이다. 다섯 건 모두 회귀 스위트를 100% 통과한 릴리즈에서 발생했다.

1단계 — 케이스의 나이를 세어봤다. 420건이 각각 언제 만들어졌는지 확인했다.

작성 시기케이스 수비중
3년 전 최초 구축31274%
2년 전6816%
1년 전338%
최근 6개월72%

즉 이 스위트가 검사하고 있는 제품은 사실상 3년 전의 제품이었다. 그사이 추가된 간편결제, 포인트 부분사용, 정기결제 기능에 해당하는 케이스는 다 합쳐도 10건이 안 됐다.

2단계 — 최근 결함을 역추적했다. 지난 6개월에 발견된 결함 60건을 놓고 "이 결함을 기존 420건 중 어떤 케이스가 잡을 수 있었을까"를 하나씩 대조했다. 답은 7건이었다. 나머지 53건은 스위트의 사정권 밖에 있었다.

3단계 — 통과율 100%의 의미를 다시 읽었다. 통과율 100%가 오래 유지된다는 건 좋은 신호가 아니라 경고 신호였다. "이 스위트는 더 이상 새로운 정보를 만들어내지 못한다"는 뜻이기 때문이다. 매 릴리즈 40분씩 쓰면서 얻는 정보량이 0에 수렴하고 있었다.

4단계 — 처방. 팀이 실제로 한 조치는 네 가지다.

조치내용
신규 기능 케이스 보강최근 1년 내 추가된 기능에 우선 케이스를 채운다
값 로테이션고정된 테스트 데이터(항상 같은 계정·금액·쿠폰)를 릴리즈마다 바꿔 돌린다
결함 피드백 루프프로덕션 결함이 나오면 그 결함을 잡는 케이스를 회귀 스위트에 반드시 추가한다
탐색적 세션 배정릴리즈마다 스크립트 없는 탐색적 테스팅 시간을 고정 배정한다(모듈 5)

교훈. 스위트가 낡는 건 스위트를 잘못 만들어서가 아니라 제품이 움직이기 때문이다. 그래서 회귀 스위트는 만들어두고 방치하는 자산이 아니라 주기적으로 손봐야 하는 자산이다. 통과율만 보지 말고 "이 스위트가 최근에 잡아낸 결함이 몇 건인가"를 같이 봐야 한다.

커버리지 100%라는 말의 함정

"테스트 커버리지 100% 달성"이라는 말을 들으면 "이제 버그가 없겠구나" 싶다. 하지만 커버리지가 100%여도 결함은 남는다. 왜 그런지는 모듈 1 레슨 2에서 배운 도달·감염·전파로 설명하면 가장 깔끔하다.

레슨 2에서 결함이 실패가 되려면 세 가지가 다 필요하다고 배웠다. 커버리지는 그중 첫 번째 하나만 보증한다.

단계의미커버리지가 보증하는가
도달(Reachability)그 코드 줄이 실행됐다예 — 구문 커버리지가 세는 게 정확히 이것이다
감염(Infection)실행 결과로 내부 상태가 잘못됐다아니오 — 어떤 값을 넣었느냐에 달렸다
전파(Propagation)잘못된 상태가 밖으로 드러나고, 그걸 확인했다아니오 — 단언(검증) 이 있어야 한다

구체적으로 보자. 아래 함수는 한 줄짜리라 아무 입력이나 한 번만 넣어도 구문 커버리지 100%가 된다.

function applyDiscount(price) {
  return price - price * 0.1   // 기획서에는 15% 할인이라고 쓰여 있다
}

applyDiscount(10000)을 실행하면 그 줄은 도달했다. 결과 9,000은 기대값 8,500과 달라 내부 상태도 감염됐다. 그런데 테스트가 결과를 확인하지 않거나, 기대값을 9,000으로 잘못 적어두었다면 전파가 일어나지 않아 테스트는 초록불로 통과한다. 커버리지 100%, 결함 그대로.

커버리지가 못 보는 구멍은 크게 세 가지다.

  • 단언이 없거나 약한 테스트. 실행만 하고 결과를 확인하지 않으면 커버리지는 올라가지만 아무것도 검증하지 않는다. 커버리지 지표를 목표로 걸면 실제로 이런 테스트가 양산된다.
  • 코드에 아예 없는 요구사항. 기획서에는 있는데 구현이 통째로 빠졌다면, 그 코드는 존재하지 않으므로 커버리지에도 잡히지 않는다. 누락 결함은 커버리지가 원리적으로 못 잡는다.
  • 비기능 품질. 성능, 보안, 접근성, 사용성은 "몇 퍼센트의 줄을 실행했는가"와 무관하다(모듈 1 레슨 4의 비기능 테스트).

그렇다고 커버리지가 쓸모없다는 뜻은 아니다. 낮은 커버리지는 확실한 나쁜 신호다. 20%라면 80%의 코드는 한 번도 실행해보지 않은 것이니 위험 신호로 읽을 수 있다. 다만 높은 커버리지가 좋은 품질의 증거는 아니다. 커버리지의 종류(구문·분기·조건)와 실무에서 목표치를 어떻게 잡는지는 모듈 10에서 자세히 다룬다.

그럼 언제 멈추는가 — 테스트 종료 조건 맛보기

"다 볼 수 없다"의 자연스러운 다음 질문은 "그럼 언제 멈추나" 다. 실무에서는 이걸 감이 아니라 테스트 종료 조건(exit criteria) 으로 미리 정해둔다. 상세한 설계는 모듈 4에서 다루고, 여기서는 어떤 신호들을 보는지만 먼저 익혀두자.

신호무엇을 보는가이것만 보면 생기는 함정
계획 범위 소화계획한 케이스의 실행률과 통과율케이스 자체가 얕으면 숫자만 채워진다
결함 발견율 감소시간당 새로 나오는 결함 수가 꺾였는가살충제 패러독스일 수도 있다 — 같은 곳만 보고 있어서 안 나오는 것일 수 있다
잔여 결함 심각도미해결 결함 중 Critical·High가 남아 있는가심각도 판정이 낙관적으로 기울 수 있다
리스크 커버사전에 뽑은 고위험 항목이 모두 다뤄졌는가리스크 목록 자체가 빠뜨린 항목은 보이지 않는다
시간·비용 예산남은 일정과 인력이것 하나만이 기준이면 그건 종료 조건이 아니라 그냥 마감이다

현실에서는 마지막 항목이 사실상 결정권을 갖는 경우가 많다. 그래서 QA에게 중요한 건 "시간이 없어서 멈춘다"를 막는 게 아니라, 멈추는 시점에 "여기까지 봤고 여기는 못 봤다"를 명시적으로 남기는 것이다. 남긴 기록이 있으면 그건 팀의 의사결정이 되고, 없으면 나중에 QA 개인의 실수가 된다.

이 원칙이 뜻하지 않는 것

"완전한 테스트는 불가능하다"는 문장은 자주 잘못 인용된다. 오해와 정확한 해석을 나란히 놓으면 이렇다.

잘못된 결론정확한 결론
어차피 다 못 보니 대충 본다다 못 보니 어디를 볼지 근거를 갖고 고른다
버그는 원래 남는 거니 QA 책임은 없다무엇이 남았는지 말할 수 있어야 책임을 팀이 나눠 진다
테스트 통과 = 품질 보증테스트 통과 = 시도한 조건에서 문제가 안 보였다
커버리지 100%면 끝난 것커버리지는 도달의 지표일 뿐이다
자동화를 늘리면 언젠가 전부 볼 수 있다자동화는 표본을 빠르게 반복할 뿐, 표본을 전수로 만들지 않는다
케이스를 많이 만들수록 좋다잘 고른 200건이 아무렇게나 만든 2,000건보다 낫다

무엇이 "충분"인지는 맥락에 따라 완전히 다르다. 개인 블로그의 댓글 기능과 의료기기 소프트웨어의 투여량 계산은 요구되는 테스트 수준이 같을 수 없다. 그래서 이 원칙의 결론은 "적당히"가 아니라 "얼마나 볼지를 의도적으로, 근거를 갖고 정하라" 다. 그 결정 방법이 모듈 4(리스크 기반 테스트)의 주제이고, 표본을 실제로 뽑아내는 기법이 모듈 3(테스트 설계 기법)의 주제다. 이 레슨은 그 두 모듈이 왜 존재하는지를 설명하는 자리인 셈이다.

실무에서 왜 필요한가

"버그 없이 나가는 거죠?"에 답해야 한다. 기획자나 경영진이 이렇게 묻는 상황은 드물지 않다. 세 가지 답을 비교해보자.

답변무엇이 문제인가
"네, 문제없습니다."지킬 수 없는 약속이다. 한 건이라도 터지면 QA 전체의 신뢰가 깎인다
"버그가 없다고는 말씀드릴 수 없습니다."사실이지만 정보가 0이다. QA가 책임을 피하는 것처럼 들린다
"핵심 결제 흐름은 경계값까지 촘촘히 봤고 발견 결함은 모두 수정·재확인했습니다. 다만 모든 조합을 볼 수는 없어 리스크가 낮다고 판단한 영역(예: 관리자 설정 화면)은 얕게 봤습니다. 이 부분이 남은 리스크입니다."문제없음 — 무엇을 봤고, 무엇을 왜 안 봤는지가 다 들어 있다

세 번째 답의 핵심은 마지막 문장이다. "무엇을 왜 안 봤는지"까지 말할 수 있는 것이 신뢰를 만든다. 안 본 영역을 숨기면 그건 QA 혼자 진 리스크가 되고, 밝히면 팀이 함께 진 리스크가 된다.

테스트 일정을 협상할 수 있게 된다. "시간이 부족합니다"는 협상이 되지 않는다. 반면 "지금 계획으로는 결제·로그인·주문 조회까지 봅니다. 이틀을 더 주시면 쿠폰 조합과 모바일 브라우저 2종을 추가로 볼 수 있습니다. 이틀이 없다면 그 두 영역이 남은 리스크입니다"는 협상이 된다. 표본을 고르는 축을 갖고 있으면 시간을 결과물의 언어로 환산해서 말할 수 있다.

릴리즈 후 사고가 났을 때 회고의 방향이 바뀐다. "왜 이걸 못 잡았냐"는 질문에 "죄송합니다"로 답하면 다음에 달라지는 게 없다. "이 케이스는 저희 표본 축 어디에도 걸리지 않았습니다 — 사용 빈도가 낮고 최근 변경도 없어서 우선순위가 낮았는데, 실제로는 영향도가 컸습니다. 리스크 평가에서 영향도를 과소평가한 것이 원인이라 판단합니다"로 답하면 표본 전략을 고치는 회고가 된다. 개인 책임 추궁이 아니라 시스템 개선으로 가는 길이 여기서 갈린다.

자동화 스위트를 관리하는 눈이 생긴다. 자동화 케이스 수와 통과율만 보고 있으면 앞의 사례 연구 같은 상황을 못 알아챈다. "최근 6개월간 이 스위트가 잡아낸 결함이 몇 건인가", "케이스의 평균 나이가 얼마인가"를 같이 보는 습관이 스테이지 4(자동화)에서 계속 쓰인다.

신입 QA의 소진을 막는다. 다 봐야 한다고 믿으면 끝없이 클릭하다 지치고, 정작 중요한 곳은 얕게 본다. "다 볼 수 없다"는 사실을 먼저 받아들여야 우선순위를 정하는 일에 에너지를 쓸 수 있다. 이건 태도의 문제가 아니라 직무 정의의 문제다.

실습 과제

과제 1 — 조합 폭발 계산하기 (10분)

로그인 폼에 필드가 3개(아이디, 비밀번호, "로그인 상태 유지" 체크박스) 있다. 아이디와 비밀번호는 각각 4가지 경우(정상 / 빈값 / 형식오류 / 존재하지않음), 체크박스는 2가지 경우다.

① 전체 조합 수를 계산한다. ② 여기에 "브라우저 3종 × 로그인 실패 누적 횟수 3단계(0회/4회/5회 잠금)"를 더하면 몇 가지가 되는지 계산한다. ③ 이 모든 조합을 다 볼 필요가 있는지, 어떤 조합을 줄일 수 있을지 한 문단으로 적는다.

해설 보기

① 4 × 4 × 2 = 32가지 ② 32 × 3 × 3 = 288가지

③ 줄일 수 있는 근거는 여러 가지다.

  • 의미 없는 조합 제거: 아이디가 빈값이면 비밀번호가 무엇이든 결과는 같다. "빈값 × 4가지 비밀번호"를 다 볼 이유가 없다.
  • 체크박스 분리: "로그인 상태 유지"는 로그인 성공 경로에서만 의미가 있다. 실패 케이스마다 체크박스 2가지를 곱할 필요가 없다.
  • 브라우저 축 분리: 브라우저는 로직이 아니라 렌더링·저장소 차이를 본다. 전체 조합에 곱하지 말고 핵심 경로 1~2개만 브라우저별로 돌린다.

여기서 실제로 한 일은 곱셈으로 묶여 있던 축을 떼어내는 것이다. 288가지가 "성공 경로 상세 + 실패 경로 대표값 + 브라우저 스모크(핵심만 훑는 가벼운 점검)" 정도로 20~30건까지 줄어든다. 이 사고방식이 모듈 3의 페어와이즈·결정 테이블로 정식화된다.

과제 2 — 우리 서비스 규모로 확장하기 (15분)

본인이 담당하는(혹은 자주 쓰는) 서비스의 화면 하나를 골라, 본문의 표처럼 단계를 쌓아가며 조합 수를 계산해본다.

  • 1단계: 그 화면의 입력 필드 수 × 필드당 값 가짓수
  • 2단계: 사용자 상태(비로그인 / 신규 / 기존 / 정지 등)
  • 3단계: 디바이스·브라우저·화면 크기
  • 4단계: 그 화면 앞뒤로 이어지는 화면의 경로 수

마지막에 "한 건당 30초로 수동 테스트한다면 몇 년 걸리는지"를 계산해 적는다.

해설 보기

정답 숫자는 없다. 이 과제의 목적은 숫자를 직접 만들어보는 것이다. 다만 잘 풀었다면 아래 두 가지를 느꼈을 것이다.

  • 대부분의 화면이 수만~수억 단위로 금방 넘어간다. 3~4단계쯤에서 "이건 세는 게 의미가 없다"는 지점이 온다.
  • 4단계(앞뒤 화면 경로)가 가장 크게 폭발한다. 실무 결함이 화면 하나가 아니라 화면 사이의 흐름에서 많이 나오는 이유와 연결된다.

한 걸음 더 나아가려면, 계산 끝에 "그래서 이 화면에서 내가 실제로 보는 건 몇 건인가" 를 세어보자. 그 두 숫자의 비율이 곧 본인이 다루고 있는 표본 비율이다. 이 숫자를 한 번 체감해두면 "테스트 다 했어요"라는 말이 다시는 편하게 나오지 않는다.

과제 3 — 표본 축으로 우선순위 매기기 (15분)

다음 5개 영역을 놓고, 본문의 다섯 가지 축(입력 값 구조 / 사용 빈도 / 리스크 / 결함 이력 / 변경 이력)을 적용해 이번 릴리즈에서 가장 시간을 많이 써야 할 순서로 정렬한다. 각 순위에 근거를 한 줄씩 붙인다.

영역이번 릴리즈 변경일 사용자최근 1년 결함실패 시 영향
A. 결제있음(간편결제 추가)12,0009건매출 직접 손실
B. 상품 검색없음80,0002건이탈 증가
C. 관리자 통계있음(그래프 개편)301건내부 불편
D. 회원가입없음1,5006건신규 유입 차단
E. 알림 설정없음4000건경미
해설 보기

정답이 하나는 아니지만, 축을 겹쳐 읽으면 대체로 이 순서가 나온다.

  1. A. 결제 — 변경 있음 + 리스크 최상 + 결함 이력 최다. 세 축이 동시에 걸린다. 논쟁의 여지가 거의 없는 1순위다.
  2. D. 회원가입 — 변경은 없지만 결함 이력이 6건으로 많고(결함 밀집), 실패하면 신규 유입이 통째로 막힌다. 회귀 관점에서 확인이 필요하다.
  3. B. 상품 검색 — 변경도 없고 결함도 적지만 사용 빈도가 압도적이다. 문제가 나면 영향 범위가 가장 넓으므로 핵심 경로 스모크는 반드시 돈다.
  4. C. 관리자 통계 — 변경이 있어서 무시할 수 없지만, 사용자가 30명이고 영향이 내부에 그친다. 얕게 본다.
  5. E. 알림 설정 — 모든 축에서 낮다. 이번엔 건너뛰고, 건너뛰었다는 사실을 기록에 남긴다.

두 가지가 이 과제의 진짜 포인트다.

  • B가 3위인 것에 불편함을 느꼈다면 잘 본 것이다. 사용 빈도만 보면 B가 1위여야 한다. 축들이 서로 다른 답을 낼 때 무엇을 우선할지 정하는 것이 리스크 기반 사고이고, 모듈 4에서 이를 발생 가능성 × 영향도로 정식화한다.
  • 5위를 "안 함"이 아니라 "기록에 남김"으로 처리한 것. 안 본 것을 기록하는 순간 그건 팀의 결정이 된다.

과제 4 — "버그 없이 나가는 거죠?"에 답하기 (10분)

이 질문에 답하는 3~4문장짜리 대답을 직접 써본다. 조건이 있다.

  • "네" 또는 "아니오"로 시작하지 않는다
  • 무엇을 어느 깊이로 봤는지 구체적으로 쓴다
  • 무엇을 안 봤는지와 그 이유를 반드시 포함한다
  • 상대가 다음에 무엇을 결정하면 되는지로 끝맺는다
해설 보기

모범 답안의 한 예다.

"이번 릴리즈에서 바뀐 결제·쿠폰 흐름은 경계값과 실패 경로까지 포함해 집중적으로 봤고, 발견된 결함 14건은 모두 수정·재확인 완료했습니다. 다만 모든 입력 조합을 볼 수는 없어서, 이번에 변경이 없고 사용량이 적은 관리자 설정 화면과 구형 브라우저 2종은 스모크 수준으로만 확인했습니다. 이 두 영역이 현재 남아 있는 리스크이고, 지금 상태로 나가도 된다고 보지만 만약 이 부분이 걱정되시면 반나절이면 추가로 볼 수 있습니다."

구조를 뜯어보면 네 부분이다.

부분문장역할
본 것결제·쿠폰을 경계값까지일한 내용을 구체적으로
처리 결과결함 14건 수정·재확인검증 가능한 사실
안 본 것 + 이유관리자 설정·구형 브라우저, 변경 없음·사용량 적음신뢰의 핵심
결정 요청반나절이면 추가 가능판단을 팀으로 넘김

자기 답을 이 네 부분으로 나눠보고, 빠진 부분이 있으면 채워 넣어보자. 대부분 세 번째("안 본 것") 가 빠져 있다.

과제 5 — 회귀 스위트 나이 점검하기 (선택, 20분)

실제 업무 환경이 있다면, 현재 팀이 쓰는 회귀 테스트 스위트(자동화든 수동 체크리스트든)를 놓고 세 가지를 확인해본다.

  1. 케이스들이 언제 만들어졌는지 — 최근 1년 내에 추가된 비중은 몇 %인가
  2. 최근 6개월에 발견된 결함 중, 이 스위트가 잡아낸 것이 몇 건인가
  3. 테스트 데이터가 매번 같은 값인가(같은 계정, 같은 금액, 같은 쿠폰)

업무 환경이 없다면, 본인이 앞선 레슨들의 실습에서 만든 테스트 케이스 목록을 대상으로 3번만 확인해도 된다. 세 질문 중 하나라도 걸린다면 그 스위트는 이미 살충제 패러독스 구간에 들어와 있는 것이다.

자가 체크리스트

  • 조합이 덧셈이 아니라 곱셈으로 늘어난다는 것을 계산으로 보여줄 수 있다
  • 소진적 테스트가 "어렵다"가 아니라 "불가능하다"인 이유를 숫자로 설명할 수 있다
  • "테스트는 결함의 존재를 보여줄 뿐 부재를 증명하지 못한다"의 논리적 비대칭을 설명할 수 있다
  • 테스트를 표본 추출로 설명하고, 표본의 크기보다 선정 근거가 중요한 이유를 말할 수 있다
  • 표본을 고르는 다섯 가지 축을 열거하고 각각이 어떤 질문을 던지는지 설명할 수 있다
  • 결함 밀집(defect clustering)이 왜 표본 선정의 근거가 되는지 설명할 수 있다
  • 살충제 패러독스가 회귀 스위트에서 어떤 모습으로 나타나는지 예를 들 수 있다
  • 통과율 100%가 오래 유지되는 것이 왜 경고 신호일 수 있는지 설명할 수 있다
  • 커버리지 100%가 결함 없음을 뜻하지 않는 이유를 도달·감염·전파로 설명할 수 있다
  • 테스트 종료 조건으로 쓰이는 신호를 세 가지 이상 말하고 각각의 함정을 짚을 수 있다
  • "버그 없음을 보장해달라"는 요청에 안 본 영역까지 포함한 답변을 만들 수 있다
  • "완전한 테스트 불가능"이 "대충 해도 된다"는 뜻이 아님을 설명할 수 있다

흔한 실수

  • "테스트를 통과했다 = 버그가 없다"고 이해관계자에게 전달한다. 정확히는 "우리가 시도한 조건에서는 안 보였다"이다. 이 차이를 흐리면 나중에 반드시 신뢰 문제가 된다.
  • 모든 조합을 다 보려고 시도하다가 정작 위험한 곳을 놓친다. 시간은 유한하다. 낮은 리스크 영역에 시간을 쓰느라 높은 리스크 영역이 얕아지면 우선순위가 잘못된 것이다.
  • 안 본 영역을 보고에서 빼놓는다. 못 본 것을 숨기면 그 리스크는 QA 혼자 진 것이 되고, 밝히면 팀이 함께 진 것이 된다. 사고가 났을 때 이 차이가 전부다.
  • 같은 회귀 스위트를 몇 년째 그대로 돌리면서 "테스트하고 있다"고 안심한다. 살충제 패러독스대로, 새로운 결함은 그 스위트 바깥에 있을 가능성이 높다.
  • 통과율 100%를 성과로만 읽는다. 오래 유지되는 100%는 "새로운 정보를 못 만들고 있다"는 신호일 수 있다. 스위트가 최근에 잡아낸 결함 수를 함께 본다.
  • 커버리지 숫자를 품질 목표로 삼는다. 지표를 목표로 걸면 단언 없는 테스트가 양산된다. 낮은 커버리지는 나쁜 신호지만, 높은 커버리지는 좋은 품질의 증거가 아니다.
  • 케이스 수를 성과 지표로 쓴다. 잘 고른 200건이 아무렇게나 만든 2,000건보다 낫다. 개수는 표본의 크기일 뿐 대표성이 아니다.
  • "다 못 보니까 대충"으로 결론 낸다. 정확한 결론은 정반대다. 다 못 보기 때문에 어디를 볼지 더 신중하게 정해야 한다.
  • 자동화를 늘리면 언젠가 전부 볼 수 있다고 기대한다. 자동화는 표본을 빠르고 반복적으로 실행하게 해줄 뿐, 60억을 60억 전부로 만들어주지 않는다.
  • 종료 조건 없이 "시간 다 돼서" 끝낸다. 마감은 종료 조건이 아니다. 적어도 "무엇까지 봤고 무엇이 남았는지"는 남겨야 한다.

참고 자료

  • Edsger W. Dijkstra, "Notes on Structured Programming" (1970) — "테스트는 결함의 존재만 보여준다" 표현의 원출처
  • ISTQB Foundation Level Syllabus — Principles of Testing — 소진적 테스트 불가능, 살충제 패러독스, 결함 밀집 등 7원칙 전체
  • Glenford J. Myers, The Art of Software Testing — 테스트를 "결함을 찾으려는 의도로 실행하는 일"로 정의한 고전
  • Boris Beizer, Software Testing Techniques — 살충제 패러독스라는 표현이 널리 알려진 출처
  • Cem Kaner, Testing Computer Software — 완전한 테스트가 불가능한 이유를 입력 공간·시간·경로 차원에서 정리
  • 모듈 3(테스트 설계 기법) / 모듈 4(리스크 기반 테스트) — 이 레슨이 제기한 "그래서 무엇을 고를 것인가"에 답하는 모듈

낯선 말 풀이

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

QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
PR — 풀 리퀘스트
"내가 고친 코드를 본체에 합쳐 달라"고 올리는 요청. 합치기 전에 사람들이 보고 의견을 단다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.

용어집 전체 보기