개념
변수는 입력 필드만이 아니다
모듈 3의 동등분할·페어와이즈는 명시적인 입력값(폼 필드, API 파라미터)을 변수로 다뤘다. 하지만 시스템에 영향을 주는 변수는 훨씬 많다 — 대부분 아무도 "이것도 변수다"라고 의식하지 않는 것들이다.
| 명시적 변수 (다들 안다) | 숨은 변수 (자주 잊는다) |
|---|---|
| 입력 필드 값 | 시스템 시각·타임존 |
| 선택 옵션 | 브라우저 확대/축소 비율 |
| 파일 형식 | 화면 크기, 기기 종류 |
| 장바구니에 담긴 상품 개수(데이터 양) | |
| 계정이 생성된 지 얼마나 됐는가 | |
| 쿠키를 지운 상태인가 | |
| 네트워크 속도, 광고 차단 확장 프로그램 유무 | |
| 같은 계정으로 다른 탭이 이미 열려 있는가 |
오른쪽 칸을 한 번 읽어 보면 공통점이 보인다. 전부 "화면에 입력하는 것" 이 아니다. 누르거나 적는 것이 아니라 그 순간에 그냥 그런 상태인 것 들이다. 그래서 요구사항에도 안 적히고, 케이스에도 안 들어가고, 아무도 시험할 생각을 안 한다.
비유하자면 요리에서 재료는 다들 확인하는데 불 세기·실온·냄비 상태는 안 적는 것과 같다. 똑같은 재료로 똑같이 해도 결과가 다르면, 원인은 거의 항상 안 적힌 쪽에 있다.
왜 "숨은" 이라고 부르나
세 가지 이유로 안 보인다.
하나, 아무도 정하지 않았다. 요구사항에 "장바구니는 최대 몇 개까지" 가 안 적혀 있으면, 100개짜리 장바구니는 아무도 결정한 적 없는 상태다. 결정한 적이 없으니 시험할 생각도 안 든다.
둘, 내 환경이 늘 같다. 테스터는 자기 노트북, 자기 브라우저, 자기 계정으로 수백 번 시험한다. 그 조합이 고정 상수가 되어 변수라는 의식에서 빠진다. 가장 자주 간과되는 숨은 변수가 바로 테스터 자신의 설정이다.
셋, 시간이 지나야 생긴다. 방금 만든 계정에는 주문 이력이 없고, 방금 띄운 화면에는 쌓인 것이 없다. "오래된 상태" 는 일부러 만들지 않으면 안 생긴다.
이 셋을 뒤집으면 찾는 방법이 나온다 — 안 정해진 것, 내가 안 바꾸는 것, 시간이 만드는 것을 의심한다.
숨은 변수 지도 — 일곱 묶음
브레인스토밍이 막힐 때 꺼내 쓰는 목록이다. 레슨 2의 "열 문장 틀" 처럼 제품 이름을 넣어 돌려 보는 용도다.
| 묶음 | 예 |
|---|---|
| 시간 | 자정 경계, 월말·연말, 서머타임, 만료 직전·직후, 주말 |
| 데이터 양 | 0건, 1건, 아주 많음, 아주 긴 글자, 빈 값, 중복된 값 |
| 계정 상태 | 신규·오래된 계정, 탈퇴 예정, 권한이 다름, 이력이 많음 |
| 기기·화면 | 작은 화면, 확대 배율, 가로·세로, 글자 크기 키움 |
| 환경 | 느린 통신, 끊김, 다른 브라우저, 확장 프로그램, 광고 차단 |
| 동시성 | 다른 탭에 같은 계정, 두 사람이 같은 것을 수정, 뒤로가기 |
| 설정·지역 | 언어, 통화, 시간대, 알림 꺼짐, 다크 모드 |
일곱 묶음 중 시간·데이터 양·동시성 셋이 사고를 가장 많이 만든다. 공통점이 있다 — 셋 다 테스터의 평소 환경에서는 절대 안 생기는 조건이다. 새 계정으로 한 건 만들어 혼자 테스트하는 것이 기본값이기 때문이다.
숨은 변수가 대참사를 만드는 방식
숨은 변수 하나만으로는 잘 안 터진다. 문제는 두세 개가 동시에 겹칠 때다.
예: "장바구니에 상품이 100개 이상 담긴 상태"(데이터 양)에서, "네트워크가 느릴 때"(네트워크 조건) 결제를 시도하면, 타임아웃 처리가 잘못되어 중복 주문이 생긴다.
이 예를 한 번 더 뜯어보면 왜 둘이 겹쳐야 터지는지가 보인다. 상품이 100개면 결제 처리에 시간이 오래 걸린다. 통신까지 느리면 응답이 더 늦는다. 그 사이에 사용자가 한 번 더 누른다. 둘 중 하나만으로는 응답이 충분히 빨라 두 번 누를 틈이 안 생긴다 — 그래서 각각은 통과하고 함께는 실패한다.
"각각은 통과하고 함께는 실패한다" 가 숨은 변수 결함의 전형적인 모양이다. 그래서 변수 하나씩 바꿔 보는 것만으로는 안 나오고, 조합이 필요하다.
이런 결함은 명세 기반 기법으로는 설계하기 거의 불가능하다 — 애초에 "장바구니 100개"와 "느린 네트워크"를 동시에 변수로 지정한 요구사항 자체가 없기 때문이다. 탐색적 테스팅이 실시간으로 여러 조건을 조합해보는 과정에서만 발견된다.
조합이 왜 그렇게 위험한지는 숫자로 보면 분명하다. 숨은 변수를 10개만 떠올려도 둘씩 짝지으면 45가지, 셋씩이면 120가지다. 전부 해 볼 수는 없다.
그래서 조합을 고를 때도 기준이 필요하다. 셋을 쓴다.
하나, 서로 다른 묶음에서 고른다. 같은 묶음 안의 둘(예: 느린 통신 + 끊김)은 비슷한 결함을 낸다. 시간 + 동시성, 데이터 양 + 환경 처럼 성격이 다른 둘을 겹치면 새로운 종류가 나온다.
둘, 한쪽이 "느려지는" 조건이면 좋다. 느린 통신, 많은 데이터, 오래된 계정은 전부 처리 시간을 늘린다. 시간이 늘면 그 사이에 다른 일이 끼어들 틈이 생기고, 그 틈에서 중복·누락·순서 꼬임이 나온다.
셋, 돈이나 상태가 바뀌는 지점을 고른다. 조회 화면에서 숨은 변수가 겹쳐 봐야 화면이 좀 이상할 뿐이다. 결제·취소·권한 변경처럼 되돌리기 어려운 지점에서 겹치면 그게 사고가 된다.
찾는 방법: "지금 이 순간, 내가 의식 못 하고 있는 게 뭐지?"
숨은 변수를 찾는 가장 실용적인 방법은, 탐험 중간중간 스스로에게 묻는 것이다 — "지금 이 상황에서, 평소와 다른 게 있다면 뭘까?"
이 질문이 잘 안 나오면 반대로 묻는다. "이게 누구에게는 다르게 보일까?" 또는 "이게 언제는 다르게 동작할까?" 주어를 바꾸는 것만으로 떠오르는 것이 달라진다. 예를 들어 "오늘은 평소보다 화면을 확대해서 보고 있다", "이 계정은 3년 전에 만든 오래된 계정이다", "지금 시간이 자정을 막 넘겼다" 같은 것들을 의식적으로 떠올려본다.
변수를 "만들어 두는" 쪽이 더 효율적이다
탐험 중에 떠올리는 것만으로는 한계가 있다. 오래된 계정이나 데이터가 많은 상태는 그 자리에서 만들 수 없기 때문이다. 그래서 숙련된 팀은 미리 만들어 둔다.
| 미리 만들어 둘 것 | 왜 |
|---|---|
| 주문 이력이 수백 건인 계정 | 목록·페이지 나눔·통계가 깨지는 곳 |
| 아주 긴 이름·주소를 가진 계정 | 화면 잘림, 출력물 깨짐 |
| 만료 직전 쿠폰·포인트 | 경계 시각 처리 |
| 탈퇴 예정·정지된 계정 | 권한이 중간 상태인 경우 |
| 이름에 특수문자·이모지가 든 계정 | 저장·검색·메일 발송 |
| 아주 오래전에 만든 계정 | 정책이 바뀌기 전 데이터 |
이런 계정 묶음을 흔히 시험 데이터 세트라 부르고, 한 번 만들어 두면 모든 탐험 세션에서 재사용된다. 만드는 비용은 한 번이고, 없으면 매번 "일반적인 새 계정" 으로만 시험하게 된다.
주의할 것이 하나 있다. 실제 사용자 데이터를 복사해 쓰지 않는다. 개인정보 문제이고, 많은 조직에서 금지돼 있다. 비슷한 모양의 가짜 데이터를 만들어 쓰는 것이 원칙이다. 자세한 관리 방법은 모듈 7에서 다룬다.
발견했으면 변수를 기록한다
숨은 변수 때문에 난 결함은 재현 절차만으로는 재현이 안 된다. 리포트에 절차만 적으면 개발자가 그대로 해 보고 "정상 동작" 이라고 닫는다. 실제로 가장 흔한 형태의 분쟁이다.
그래서 리포트에 "평소와 달랐던 것" 을 따로 적는다.
재현 절차: 장바구니에 상품 담기 → 쿠폰 적용 → 결제
환경·조건: 장바구니 상품 120개 / 통신 속도 제한(3G) / 계정 생성 2년 경과
/ 다른 탭에 같은 계정 로그인됨 / 시각 23:58아래 줄이 없으면 위 절차는 아무 데서나 성공한다. 그리고 아래 줄은 세션 노트에 적어 두지 않으면 30분 뒤에 재구성할 수 없다 — 레슨 4에서 시각을 적으라고 한 이유가 여기서도 통한다.
숨은 변수를 기능 요구로 바꾸기
찾은 변수를 결함 리포트로만 쓰고 끝내면 아깝다. 같은 종류가 계속 나오면 그건 설계 문제이고, 그때는 요구사항 쪽에 제안하는 편이 낫다.
| 반복해서 나오는 것 | 제안할 것 |
|---|---|
| 두 번 눌러 중복 생성 | 같은 요청을 두 번 받아도 한 번만 처리되게(멱등) |
| 목록 0건에서 깨짐 | 빈 상태 화면을 요구사항에 명시 |
| 긴 글자로 깨짐 | 입력 길이 제한을 정하고 화면에서도 자르기 규칙 |
| 월말·자정에서 틀림 | 날짜 경계 처리 기준을 문서로 |
| 권한 중간 상태 | 상태별 허용 동작 표를 만들게 요청 |
이건 모듈 2에서 배운 "당연해서 안 적힌 것을 묻는다" 와 같은 활동이다. 다만 여기서는 이미 터진 증거를 들고 묻는 것이라 훨씬 잘 통한다. "이렇게 될 수도 있지 않나요" 보다 "이 조건에서 실제로 이렇게 됐습니다" 가 요구사항을 바꾼다.
이 제안이 받아들여지면 같은 종류의 결함이 통째로 사라진다. 결함 하나를 리포트하는 것보다 값이 크고, 리드로 올라갈수록 이쪽 기여가 더 크게 평가된다.
어디까지 의심할 것인가
변수를 의식하기 시작하면 반대 문제가 생긴다. 모든 것이 변수로 보여 아무 결론도 못 내리는 상태다. "이게 결함인지 내 환경 때문인지 모르겠다" 가 반복되면 탐험이 멈춘다.
선을 긋는 방법은 하나다 — 변수를 하나씩 되돌려 본다.
- 이상한 것을 발견한다
- 가장 의심스러운 변수 하나만 평소대로 되돌리고 다시 해 본다
- 그래도 나면 그 변수는 원인이 아니다. 다음 변수로 넘어간다
- 안 나면 그 변수가 조건이다. 리포트에 적는다
한 번에 여러 개를 되돌리면 어느 것 때문인지 모른다. 하나씩 이 원칙이고, 이건 결함 보고의 기본이기도 하다(모듈 6 레슨 2).
그리고 결론이 안 나도 보고한다. "조건을 특정하지 못했지만 이런 상황에서 이런 현상을 봤습니다, 환경은 이렇습니다" 가 "확실하지 않아서 안 적었습니다" 보다 훨씬 낫다. 레슨 12가 그 보고를 어떻게 쓰는지 다룬다.
실무에서 왜 필요한가
숨은 변수로 인한 결함은 실무에서 "재현이 안 되는" 형태로 나타나는 경우가 많다(다음다음 레슨에서 다룬다) — 왜냐하면 재현을 시도하는 사람이 그 숨은 변수를 그대로 맞추지 못하기 때문이다. QA가 "오늘 뭔가 평소와 달랐던 게 있나?"를 습관적으로 점검하면, 이런 결함을 처음부터 더 잘 잡거나, 최소한 재현 조건을 더 정확히 좁힐 수 있다.
그리고 이 능력은 운영 중 장애를 다룰 때 특히 값을 한다. 장애 보고가 들어오면 가장 먼저 묻는 것이 "누구에게, 언제, 어떤 조건에서" 인데, 이게 바로 숨은 변수를 묻는 질문이다. 숨은 변수 목록을 들고 있는 사람은 "특정 기기인가요", "월말에만인가요", "데이터가 많은 계정인가요" 를 바로 물을 수 있고, 그 질문 셋이면 범위가 크게 좁혀진다.
반대로 이 습관이 없으면 장애 대응이 "전부 다시 해 보기" 가 된다. 같은 시간에 훨씬 적게 알아낸다.
변수를 바꾸는 방법 — 쉬운 것부터
"바꿔 보라" 고 해도 어떻게 바꾸는지 모르면 못 한다. 난이도 순으로 적어 둔다.
| 변수 | 바꾸는 법 | 난이도 |
|---|---|---|
| 화면 크기·배율 | 브라우저 창을 줄이거나 확대 배율을 바꾼다 | 쉬움 |
| 데이터 양 | 장바구니에 계속 담는다, 목록을 계속 만든다 | 쉬움 |
| 동시성 | 창을 두 개 띄운다. 하나는 일반 창, 하나는 시크릿 창 | 쉬움 |
| 통신 속도 | 개발자 도구의 속도 제한 기능 | 쉬움 |
| 언어·지역 | 브라우저 언어 설정, 기기 지역 설정 | 보통 |
| 계정 나이·이력 | 미리 만들어 둔다. 그 자리에서는 못 만든다 | 보통 |
| 시스템 시각 | 기기 시각을 바꾸거나, 가능하면 서버 쪽에 요청 | 어려움 |
시크릿 창 하나로 동시성을 만들 수 있다는 것은 알아 두면 매일 쓴다. 일반 창에 A 계정, 시크릿 창에 B 계정을 띄우면 권한 시험과 동시 수정 시험이 둘 다 된다. 레슨 6의 상호작용 탐험에서 바로 쓰인다.
시각 바꾸기는 어렵지만 효과가 가장 크다. 월말·연말·자정 경계는 실제로 사고가 자주 나는 자리인데, 그 조건을 못 만들면 영영 안 보인다. 기기 시각을 바꾸면 되는 경우가 많고, 서버가 판단하는 기능이면 개발자에게 시각을 조절할 수 있는 방법이 있는지 물어보는 것이 정답이다. 그 질문 자체가 모듈 9 레슨 9에서 다루는 테스트 용이성 요구가 된다.
사례 — 한 달에 한 번만 터지는 결함
숨은 변수가 어떻게 숨는지 한 건을 따라가 보자.
제보. "정산 금액이 가끔 안 맞는다." 운영팀이 몇 달째 손으로 고치고 있었다. QA 가 재현을 시도했지만 매번 정상이었다.
1차 시도 — 실패. 쿠폰·할인·환불 조합을 바꿔 가며 수십 건을 만들어 봤다. 전부 맞는다. "재현 불가" 로 닫힐 뻔했다.
질문을 바꿨다. "틀린 건들의 공통점이 뭐지?" 운영팀에 틀렸던 주문 목록을 받아 날짜를 봤다. 전부 월말이었다.
2차 시도. 시스템 시각을 월말로 맞추고 다시 해 봤다. 재현됐다. 정산 기준 기간을 계산하는 쪽이 말일이 31일이 아닌 달에서 하루를 빠뜨리고 있었다.
여기서 봐야 할 것이 둘이다.
첫째, 숨은 변수는 "시각" 이었는데 아무도 변수로 안 봤다. 요구사항에 "정산은 월 단위" 라고만 적혀 있었고, 매일 테스트하는 사람에게 오늘 날짜는 바꿀 수 있는 것이라는 생각 자체가 안 든다.
둘째, 재현의 열쇠는 데이터에 있었다. 머리로 떠올리는 대신 실제로 틀린 것들의 공통점을 찾았다. 숨은 변수를 찾는 가장 확실한 방법이고, 레슨 12(재현 불가능한 버그)의 핵심 전략이기도 하다.
숨은 변수는 어느 팀에나 비슷하게 있다
여러 제품을 다녀 보면 같은 변수가 반복해서 사고를 낸다. 아래 다섯은 도메인을 가리지 않고 나온다. 새 제품을 맡았을 때 가장 먼저 찔러 볼 곳이다.
하나, 시각과 시간대. 자정 경계, 월말, 서머타임, 해외 접속. 날짜 계산이 들어간 기능은 거의 다 여기서 한 번은 틀린다.
둘, 목록이 0건일 때. "아직 아무것도 없습니다" 화면을 안 만든 경우가 놀랍도록 많다. 빈 화면이 깨지거나, 통계가 0으로 나누거나, 평균이 계산 안 된다.
셋, 아주 긴 글자. 이름·제목·주소가 길면 화면이 깨지고, 출력물이 잘리고, 저장이 실패한다. 이모지가 섞이면 글자 수 계산이 또 달라진다.
넷, 두 번 누르기. 느린 통신에서 사용자는 반드시 두 번 누른다. 중복 생성·중복 결제가 여기서 나온다.
다섯, 권한이 중간 상태. 탈퇴 신청했지만 아직 안 된 계정, 정지된 계정, 권한이 방금 회수된 계정. 있음과 없음 사이가 거의 항상 비어 있다.
다섯을 외우고 있으면 처음 보는 제품에서도 30분 안에 한두 건은 나온다. 그리고 이 다섯은 레슨 14의 휴리스틱 치트시트에 그대로 들어간다.
자동화는 숨은 변수를 고정한다
자동화 시험이 전부 통과하는데 운영에서 터지는 이유 중 하나가 이것이다. 자동화는 매번 같은 조건에서 돌도록 만들어진다 — 깨끗한 계정, 한 건짜리 데이터, 빠른 내부망, 고정된 시각.
그게 자동화의 장점이기도 하다. 조건이 같아야 실패했을 때 코드 탓이라고 말할 수 있기 때문이다(모듈 14의 "믿을 수 없는 실패"). 다만 그 대가로 숨은 변수는 전부 꺼진다.
그래서 둘은 역할이 다르다.
| 자동화 | 탐험 | |
|---|---|---|
| 숨은 변수 | 일부러 고정 | 일부러 바꾼다 |
| 찾는 것 | 아는 것이 깨졌는지 | 모르는 조건에서 깨지는지 |
탐험에서 숨은 변수 하나를 찾아내면, 그 변수를 자동화에 조건으로 추가하는 것이 다음 수순이다. 월말 정산 사례라면 "말일이 31일이 아닌 달" 을 시험 데이터에 넣는다. 그렇게 탐험이 찾고 자동화가 지킨다(레슨 1).
채용시장에서의 위치
이 레슨의 내용은 면접에서 "가장 어려웠던 결함" 질문의 단골 소재다. 그리고 답하는 방식에서 수준이 드러난다.
- "재현이 안 되는 버그가 있었는데 결국 못 찾았습니다" — 흔한 답이다
- "재현이 안 돼서, 틀린 건들의 공통점을 데이터에서 찾아봤더니 전부 월말이었습니다" — 방법을 가진 사람으로 읽힌다
두 번째 답에는 이 레슨의 내용이 전부 들어 있다. 변수를 의심했고, 머리가 아니라 데이터에서 찾았고, 조건을 특정했다.
경력이 올라가면 질문이 바뀐다. "그 뒤에 무엇을 하셨나요" 를 묻는다. 좋은 답은 "그 조건을 시험 데이터와 자동화에 넣었습니다" 다 — 한 번 찾은 것을 다시는 놓치지 않게 만드는 것까지가 일이기 때문이다.
공고 문구로는 "다양한 환경에서의 검증 경험", "재현이 어려운 결함 분석", "테스트 데이터 설계" 같은 형태로 나온다. 전부 이 레슨의 다른 이름이다.