反証できない仮説は当て推量だ
한국어 원문으로 표시합니다.
한 줄 요약
디버깅은 가능한 것을 하나 만들어 내는 일이 아니라 가능한 원인들을 하나만 남을 때까지 지우는 일이고, 지우려면 틀렸을 때 무엇이 보일지가 미리 정해져 있어야 한다.
왜 이게 필요했나
"가끔 느려요" 는 진단할 수 없습니다. 진단할 수 있는 것은 측정값뿐입니다. 그래서 고객의 말을 받은 다음 첫 작업은 원인을 찾는 것이 아니라 증상을 명령 하나로 고정하는 것입니다.
# 20회 반복해 실패율을 잰다
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8001/quote
done | sort | uniq -c
20번 중 5번이 실패한다면, 장애는 전해 들은 이야기에서 수치로 바뀝니다. 그리고 이 명령은 이후 모든 단계에서 수리 여부를 판정하는 잣대로 재사용됩니다. 고쳤다고 말하려면 같은 잣대로 다시 재서 0 이 나와야 합니다.
재현이 안 되는 것도 정보입니다. 특정 사용자, 특정 시간대, 특정 경로에서만 난다는 뜻이고 질문이 그만큼 좁혀집니다.
어떻게 동작하나
숫자가 손에 들어오면 다음은 가설입니다. 그런데 대부분의 사람이 여기서 가설이라고 부르는 것은 가설이 아닙니다.
"네트워크 문제인 것 같다" 는 가설이 아닙니다. 틀렸을 때 무엇이 보일지가 정해져 있지 않기 때문에, 무슨 결과가 나와도 이 문장은 살아남습니다. 살아남는 추측은 후보를 줄여 주지 않습니다.
쓸 수 있는 가설은 세 줄짜리입니다.
가설: 요청이 게이트웨이와 애플리케이션 사이에서 끊긴다
맞다면: 게이트웨이 로그에 5초 타임아웃, 앱 로그에 해당 요청 아이디 없음
틀리면: 앱 로그에 요청이 들어와 있고 처리 중 실패한 흔적이 있음
차이는 뒤쪽 두 줄에 있습니다. 확인할 것이 지정되어 있고, 예상과 다르게 나오면 가설이 죽습니다. 죽는 가설만이 후보를 줄여 줍니다.
세 줄을 적는 데 1분이 걸립니다. 그 1분이 아까워 보이지만, 적지 않으면 30분 뒤에 자기가 이미 배제한 것을 다시 확인하고 있게 됩니다. 여러 사람이 붙은 장애에서는 효과가 더 큽니다. 배제 목록이 공유되지 않으면 세 사람이 같은 것을 세 번 확인합니다.
현장에서 만나는 모습
가설을 좁히는 가장 신뢰할 만한 절차는 계층을 절반씩 자르는 것입니다. 요청이 통과하는 층을 순서대로 적고 — 클라이언트, DNS, 로드밸런서, 게이트웨이, 애플리케이션, 캐시, 데이터베이스, 외부 연동 — 정확히 가운데를 골라 "여기까지는 정상인가" 를 묻습니다.
여덟 개 층을 순서대로 훑으면 평균 네 번을 봐야 하고, 절반씩 자르면 세 번 안에 끝납니다. 층이 스무 개로 늘면 격차는 열 번 대 다섯 번이 됩니다. 실제 장애에서 한 번 확인하는 데 몇 분이 걸리므로, 이 차이가 곧 복구 시간입니다.
여기서 핵심은 정상 여부를 판정할 수 있는 지점을 고르는 것입니다. 판정이 불가능한 지점에서 자르면 절반을 못 지웁니다. 계층 분리의 목적은 정답을 맞히는 것이 아니라 보지 않아도 되는 곳을 확정하는 것입니다.
한 가지 예외가 있습니다. 서비스가 지금 죽어 있는 동안에는 진단보다 완화가 먼저입니다. 롤백하거나 트래픽을 돌려 출혈을 멈춘 뒤에 좁히세요. 가설을 예쁘게 적는 것은 사용자가 기다리지 않을 때 할 일입니다.
가설을 문장으로 쓰는 형식
머릿속 짐작과 검증 가능한 가설의 차이는 문장으로 쓸 수 있는가 입니다.
❌ "DB 가 느린 것 같다"
→ 무엇을 확인하면 아닌 줄 알 수 있나? 정해져 있지 않다
✅ "결제 API 의 p95 지연 3초 중 2초 이상이 orders 테이블 조회에서 난다."
확인 방법: 그 구간에 타이머를 넣고 100건을 재본다
반증 조건: 조회가 200ms 이하면 이 가설은 틀렸다
반증 조건을 먼저 적는 것 이 핵심입니다. 그것이 없으면 어떤 결과가 나와도 "그럴 수도 있다" 로 넘어가게 되고, 조사가 끝나지 않습니다.
한 번에 하나만 바꾼다
여러 개를 동시에 바꾸면 무엇이 효과가 있었는지 모릅니다. 그리고 대개 하나는 좋아지고 하나는 나빠져 서로 상쇄됩니다.
❌ 인덱스 추가 + 커넥션 풀 확대 + 캐시 도입 → 20% 좋아짐
무엇 때문인가? 셋 중 하나는 오히려 해로웠을 수도 있다
✅ 인덱스 추가만 → 측정 → 되돌림 → 커넥션 풀만 → 측정 …
시간이 없어 여러 개를 함께 넣어야 한다면, 최소한 되돌릴 수 있게 각각을 별도 커밋으로 둡니다. 나중에 하나씩 빼며 확인할 수 있습니다.
측정을 신뢰할 수 있게 만들기
같은 조건에서 세 번 재고 흔들림 폭을 봅니다. 그보다 작은 개선은 개선이 아닙니다.
전: 2.9s, 3.1s, 3.0s → 폭 0.2s
후: 2.8s, 2.9s, 2.85s → 폭 0.1s, 평균 0.15s 개선
→ 흔들림과 개선폭이 비슷하다. 아직 결론을 낼 수 없다
그리고 측정 자체가 대상을 바꾸지 않는지 확인합니다. 로그를 켜서 재면 로그가 느려지게 만들고, 프로파일러를 붙이면 프로파일러 비용이 섞입니다. 가능하면 프로덕션과 같은 조건에서, 없으면 그 차이를 기록에 남깁니다.
조사를 끝내는 조건
가설 검증에는 끝이 있어야 합니다. 셋 중 하나면 끝냅니다.
- 원인을 특정했고 되돌림으로 확인했다 — 가장 좋은 끝.
- 증상이 사라졌고 왜 사라졌는지 안다 — 되돌려 확인하기 어려운 경우.
- 여기까지 확인했고 더 파는 비용이 이득보다 크다 — 이것도 정당한 끝입니다. 단, 어디까지 봤는지 적어 두어야 다음 사람이 거기서 이어받습니다.
3번을 인정하지 않으면 조사가 며칠씩 늘어집니다. 완화책이 있고 재발이 드물다면 멈추는 것이 옳은 판단일 때가 많습니다.
다음 실습에서 할 것
"가끔 안 된다" 는 신고가 들어온 견적 API 를 상대로, 실패율을 숫자로 고정하고, 세 줄짜리 가설을 문서로 쓰고, 배제한 계층을 근거와 함께 기록한 뒤, 같은 잣대로 다시 재서 비율이 안정적인지 확인합니다.