재현 안 되는 결함 처리법 — 닫지 말고 ‘재현 대기’로 옮기는 4단계
기획자나 QA는 분명히 봤는데 개발자 환경에서는 안 나오는 결함, 흔히 ‘재현 안 됨(NRT)’으로 닫히곤 합니다. 이 글은 재현 안 되는 결함이 자주 일어나는 4가지 원인과, 닫기 전에 거치면 좋은 4단계 처리법, 그리고 팀 운영 룰까지 정리했습니다.

같은 화면을 보고도 한 명만 보이는 결함
QA나 기획자가 분명히 한 번 본 결함이 있습니다. 그런데 개발자가 똑같이 따라가면 안 나옵니다. 두 번 돌려도 안 나오고, 세 번 돌려도 멀쩡합니다. 한 시간 즈음 지나면 결국 누군가 입을 엽니다. "재현이 안 되네요." 그렇게 디펙 티켓은 'Not Reproducible(NRT)'로 닫힙니다. 그리고 두어 주 뒤에 비슷한 결함이 사용자 문의로 다시 올라옵니다.
재현 안 되는 결함은 사실 '없어진 결함'이 아니라 '아직 조건을 못 찾은 결함' 인 경우가 많습니다. 닫는 순간 그 조건은 그대로 묻히고, 다음번 사고가 잡힐 때까지의 시간만 길어집니다. 이 글은 재현 안 되는 결함이 흔히 어디서 오는지, 그리고 닫기 전에 한 번 거치면 좋은 4단계 처리법을 정리합니다.
'재현 안 되는 결함'이 자주 나타나는 4가지 원인
원인의 8할은 아래 네 가지에 들어갑니다. 어디부터 점검할지 가설을 세우려면 이 표가 출발점이 됩니다.
| 원인 | 자주 보이는 신호 |
|---|---|
| 환경·데이터 차이 | 특정 사용자·특정 브라우저·특정 시점에만 발생 |
| 타이밍과 비동기 | "빨리 누르면" 또는 "동시에 누르면"이 단서 |
| 외부 의존과 캐시 | 같은 입력인데 어제와 오늘 응답이 다름 |
| 시나리오의 미세한 차이 | 입력값·이동 경로·로그인 상태가 보고서와 살짝 다름 |
환경·데이터 차이
가장 흔합니다. 기획자가 본 화면은 운영 데이터·운영 캐시·운영 권한 위에 있고, 개발자가 보는 화면은 로컬 더미 데이터 위에 있습니다. 그 사이에는 A/B 실험, 베타 플래그, 사용자 등급에 따른 분기가 끼어 있습니다. 같은 코드를 똑같이 돌려도 입력 조건이 다르면 다른 결과가 나오는 게 정상입니다.
타이밍과 비동기
화면이 그려지기 전에 데이터가 도착하면 정상이고, 늦게 도착하면 깨지는 종류입니다. 사용자 PC가 빠르거나 회선이 좋으면 평생 못 보고, 누군가의 느린 회선에서만 가끔 나옵니다. 보고에 "빨리 두 번 눌렀더니"라는 표현이 있으면 거의 이쪽입니다.
외부 의존과 캐시
서드파티 API, CDN, 사용자 단말의 캐시, 운영 DB의 리플리카 지연 — 한 번이라도 외부에 의존하는 응답은 같은 입력에 다른 출력이 나올 수 있습니다. 어제까지 됐는데 오늘 안 되는 결함은 보통 이 줄을 의심합니다. 이런 외부 변수 의존 문제는 폴백 안전망 설계에서 짚은 "본 경로의 의존성"과 그대로 겹칩니다.
시나리오의 미세한 차이
보고서에는 "장바구니에서 결제 들어가니 에러"라고 적혀 있지만, 실제로는 로그인 후 30분 지난 상태에서 쿠폰을 두 번 적용했을 때 였을 수 있습니다. 입력값 한 줄·이동 경로 한 단계 차이가 결과를 완전히 가릅니다.
닫지 말고 '재현 대기'로 옮기는 4단계
여기서부터가 운영 팁입니다. 한 번 못 잡았다고 바로 닫지 말고, 아래 4단계를 거친 뒤 결정합니다.
1단계 — 보고자에게 컨텍스트를 한 번 더
가장 빠른 단계입니다. 보고자에게 추가 정보 5가지를 묻습니다 — 발생 시각(초 단위), 사용자 ID, 디바이스·브라우저, 직전에 누른 버튼, 직전 화면의 데이터 상태. 이 다섯 가지만 있어도 가설의 절반이 채워집니다. 보고 양식에 처음부터 넣어두는 팀은 이 단계를 거의 건너뜁니다.
2단계 — 로그·모니터링에서 거꾸로 재구성
발생 시각을 받으면 그 시간대의 서버 로그, 에러 트래커, 사용자 세션 리플레이를 뒤집니다. 보고자 자신은 화면만 봤지만 시스템은 요청 ID(correlation ID), 응답 코드, 응답 시간, 캐시 히트 여부를 다 기록하고 있습니다. 결함이 한 번이라도 일어났다면, 로그 위에는 반드시 흔적이 있습니다.
3단계 — 환경 변수를 따라가며 점검
같은 사용자 계정으로, 같은 디바이스로, 같은 시간대(가능하면 같은 트래픽 조건으로)에 다시 시도합니다. 안 되면 한 변수씩 운영 조건에 맞춰갑니다 — A/B 실험군, 베타 플래그, 사용자 등급, 캐시 무력화. 변수 하나씩 차이를 좁히면 가설이 좁혀집니다.
4단계 — 그래도 안 나오면 '관찰 대기'
여기까지 갔는데도 못 잡았다면 닫지 말고 '관찰 대기(Wait & Observe)' 상태로 보관합니다. 이때 같이 챙기는 게 세 가지입니다 — 모니터링 알람 추가, 영향도 추정(전체 사용자 중 N%), 자동 정리 기한(예: 30일). 다시 발생하면 알람이 울리고, 발생하지 않으면 기한이 끝날 때 자동 정리됩니다.
닫기 전에 챙기는 3가지
'재현 안 됨'으로 닫는 결정을 내리기 전에는 최소한 아래 세 가지를 티켓에 기록해 둡니다.
- 재현 확률 표기 — "10번 중 0번" / "10번 중 2번" — 이후 비슷한 보고가 들어왔을 때 합산하기 쉽습니다.
- 영향도 추정 — 전체 사용자 중 몇 %가 잠재 영향권인지, 어떤 비즈니스 흐름이 막히는지.
- 다음에 보이면 알람 — 같은 패턴이 다시 잡히면 자동으로 티켓이 깨어나도록 모니터링 규칙을 걸어둡니다.
이 세 줄을 챙기면, '재현 안 됨'으로 닫더라도 같은 결함이 두 번째로 올라왔을 때 손해를 보지 않습니다.
팀의 운영 룰로 만들기
개별 결함마다 매번 똑같은 판단을 반복하지 않으려면, 팀 룰을 한 번 정해두는 게 효율적입니다.
- 상태를 둘로 분리:
Not Reproducible(닫음)/Wait & Observe(관찰 중)— 후자에는 자동 정리 기한이 따라붙습니다. - 책임 분리: 재현 시도는 QA, 가설과 코드 점검은 개발자, 비즈니스 영향 판단은 기획자 — 단계마다 누가 무엇을 하는지가 분명해야 NRT가 책임 떠넘기기로 흐르지 않습니다.
- 단어를 맞춰두기: 앞서 스모크 테스트 글에서 정리한 것처럼, '테스트'와 마찬가지로 '재현 안 됨'도 양쪽의 정의가 다르면 갈등이 생깁니다. 닫는 기준과 관찰 대기로 옮기는 기준을 글로 합의해두면 매번 다투지 않아도 됩니다.
한 줄로 다시 정리
재현 안 되는 결함의 가장 큰 함정은 닫는 순간 정보가 사라진다는 점 입니다. 그래서 처리법의 핵심은 가설을 빨리 세우는 것보다, 컨텍스트를 모으고 관찰 대기로 옮기고 다시 깨어나게 만드는 구조를 갖춰두는 일 에 가깝습니다. 한 번 못 잡은 결함은 두 번째로 올라왔을 때 잡으면 됩니다. 두 번째 보고가 올라왔을 때 첫 번째 티켓이 닫혀 있지 않다면, 잡는 속도가 완전히 달라집니다.
재현 안 되는 결함을 '잘 모르겠으니 닫자'에서 '아직 조건을 못 찾았으니 옆에 두자'로 한 칸만 옮기면, 같은 결함을 두 번 디버깅하지 않아도 됩니다.