개념
검증(Verification) vs 확인(Validation)
두 단어가 한국어로도 영어로도 헷갈리기 쉬운데, 질문 형태로 외우면 구분이 쉽다.
| 질문 | 확인 대상 | 시점 | |
|---|---|---|---|
| 검증 (Verification) | "우리가 제대로 만들고 있는가?" | 산출물이 명세·설계와 일치하는가 | 개발 각 단계마다 (문서, 설계, 코드) |
| 확인 (Validation) | "우리가 맞는 것을 만들고 있는가?" | 실제로 사용자 요구를 충족하는가 | 보통 동작하는 소프트웨어로, 더 나중에 |
검증은 "명세대로 만들었는가"를 보고, 확인은 "그 명세 자체가 맞았는가, 실제로 쓸모 있는가"를 본다. 이 둘은 독립적으로 실패할 수 있다.
- 검증은 통과, 확인은 실패: 명세에 적힌 대로 정확히 만들었지만, 명세 자체가 사용자의 실제 흐름과 안 맞아서 아무도 못 쓴다.
- 검증은 실패, 확인은 (우연히) 통과: 명세와 다르게 만들었는데, 그게 오히려 사용자에게 더 자연스러워서 아무도 불만을 못 느낀다. (드물지만 일어난다 — 이 경우 명세를 갱신해야 한다는 신호다.)
코드 리뷰, 단위 테스트, 명세 대조는 검증에 가깝고, 사용자 인수 테스트(UAT), 베타 테스트, 실사용자 피드백은 확인에 가깝다.
오류 → 결함 → 실패의 전파
세 단어를 QA에서는 엄격하게 구분한다.
- 오류(Error): 사람이 저지른 실수. 요구사항을 잘못 이해했거나, 코드를 잘못 짰거나.
- 결함(Defect/Fault): 그 실수가 산출물(코드, 문서, 설계)에 남긴 흔적. 아직 실행되지 않았을 수도 있다.
- 실패(Failure): 그 결함이 특정 조건에서 실행되어 겉으로 드러난 오작동.
// 오류: 개발자가 "빈 배열도 유효한 입력"이라는 걸 놓쳤다 (사람의 실수)
function getDiscountRate(items) {
// 결함: items가 빈 배열일 때를 고려하지 않은 코드가 여기 남아 있다
return items[0].price > 100000 ? 0.1 : 0.05;
}
getDiscountRate([{ price: 150000 }]); // 0.1 — 정상 동작, 실패가 드러나지 않는다
getDiscountRate([]); // TypeError — 여기서 실패가 발생한다이 코드는 결함이 있어도 대부분의 입력에서는 실패하지 않는다. 빈 배열이 들어오는 경로를 실행해야만 실패로 드러난다. 그래서 "지금까지 문제가 없었다"는 "결함이 없다"는 뜻이 아니라 **"그 결함을 건드리는 조건을 아직 실행해보지 않았다"**는 뜻일 수 있다.
이 전파 경로(오류 → 결함 → 실패)를 알면 결함 리포트를 쓸 때도 도움이 된다 — "무엇이 실패했는가"뿐 아니라 "어떤 조건이 그 결함을 건드렸는가"까지 적어야 재현과 수정이 빨라진다. 결함 리포트 작성법은 모듈 6에서 자세히 다룬다.
실무에서 왜 필요한가
실무에서 가장 흔한 오해는 "명세대로 만들었으니 QA 책임은 끝났다"는 생각이다. 하지만 명세 자체가 틀렸다면(확인 실패), 그건 QA가 검증만 하고 확인을 건너뛰었기 때문일 수 있다. 예를 들어 결제 폼이 요구사항 문서와 한 글자도 다르지 않게 구현됐지만(검증 통과), 실제 사용자 테스트에서 결제 단계가 너무 복잡해 다들 중간에 이탈한다면(확인 실패), 그 결함은 테스트 케이스로는 절대 못 잡는다 — 실제 사용자 흐름을 봐야 잡힌다.
또한 "결함이 있어도 실패가 안 보이면 괜찮다"는 생각도 위험하다. 위 코드 예시처럼, 결함은 그대로 남아있다가 몇 달 뒤 특정 이벤트(예: 프로모션으로 빈 장바구니 관련 로직이 새로 실행되는 경로)에서 갑자기 실패로 터지는 경우가 실무에서 자주 있다.
실습 과제
과제 1 — 검증인가 확인인가 (10분)
다음 활동이 검증(Verification)인지 확인(Validation)인지 분류한다.
- 설계 문서와 실제 구현된 API 응답 필드가 일치하는지 대조한다.
- 실제 사용자 5명에게 신규 가입 흐름을 써보게 하고 어디서 헤매는지 관찰한다.
- 요구사항 문서의 각 항목이 테스트 케이스로 하나씩 커버되는지 추적표로 확인한다.
- 베타 사용자 그룹에 기능을 먼저 공개하고 실제 사용 데이터를 본다.
과제 2 — 전파 경로 추적하기 (10분)
아래 코드에서 오류·결함·실패를 각각 찾아 적는다. "이 결함이 실패로 드러나려면 어떤 입력이 필요한가?"도 함께 적는다.
def calculate_average(scores):
total = sum(scores)
return total / len(scores)자가 체크리스트
- 검증과 확인을 각각 질문 형태("제대로 만들고 있는가" / "맞는 것을 만들고 있는가")로 설명할 수 있다
- 검증은 통과하지만 확인은 실패하는 사례를 스스로 만들 수 있다
- 오류·결함·실패 세 단어를 순서대로, 서로 다른 개념으로 구분해 쓸 수 있다
- "결함이 있어도 실패가 관찰되지 않을 수 있다"는 이유를 코드 예시로 설명할 수 있다
- 결함 리포트에 "어떤 조건이 결함을 건드렸는가"를 왜 적어야 하는지 설명할 수 있다
흔한 실수
- 검증과 확인을 뒤바꿔 쓴다. "확인 테스트"라고 부르면서 실제로는 명세 대조(검증)만 하는 경우가 흔하다. 실제 사용자 관점이 빠졌다면 확인이 아니다.
- "버그가 안 보인다 = 결함이 없다"고 결론짓는다. 실행되지 않은 코드 경로에 결함이 잠들어 있을 수 있다. 커버리지(모듈 3, 10에서 다룸)가 낮으면 이 위험이 커진다.
- 오류의 책임을 개인에게 돌리는 데 집중한다. 오류는 사람이 만들지만, 결함이 실패로 이어지기까지 여러 단계(리뷰, 테스트)를 통과했다는 뜻이기도 하다. "누가 실수했나"보다 "왜 이 단계들에서 못 잡았나"가 더 생산적인 질문이다.
참고 자료
- ISTQB Glossary — Verification / Validation — 공식 용어 정의
- Barry Boehm, Software Engineering Economics — "Are we building the product right? / Are we building the right product?" 표현의 원출처로 자주 인용된다