핵심 디버깅 과정 4단계
1. 재현 : 신뢰할 수 있고 쉬운 재현 방법 찾기
2. 진단 : 가설을 세우고 원인에 대한 확신을 할 수 있을 때 까지 테스트
3. 수정 : 문제를 고치기 위해 어떻게 수정할지 설계하고 구현함
4. 반영 : 버그에서 교훈 얻기, 동일한 버그 존재 가능성 찾기, 재발 방지법 찾기
재현
고민하기 전에 재현부터 해보자.
재현이 중요한 이유는 버그를 수정했다고 생각해도 정말 문제가 수정됐는지 알 수 없기 때문임.
재현을 할 때는 관련 변수들을 버그 당시와 동일하게 설정해야 한다.
- 소프트웨어 그 자체 : 버그가 발생한 소프트웨어와 동일한 버전을 써야 한다.
(그 밖에 컴파일러, 런타임 라이브러리, 서드파티 코드 등)
- 실행 환경 : 외부 시스템과 연동이 있다면 개발자 시스템도 동일하게 설정한다.
- 입력 값 : 버그 발생 시의 사용자 입력 값을 동일하게 사용한다.
입력 값 설정 시 필요한 정보가 부족하다면 선택은 두 가지이다.
추론하거나 로그로 입력을 기록하거나.
추론 시에는 다음을 고려해보자.
경계 값 분석 : 입력 값 범위 경계에 에러가 있을 가능성이 높다.(null이나 공백도)
부하와 스트레스
어떤 버그는 어느 정도 부하를 받을 때만 발생한다. 이럴 때는 부하 테스트 도구를 써서 비슷한 상황을 만들자.
- 비결정적인 버그를 결정적인 것으로 만들기
컴퓨터는 정확히 사람이 시키는 대로 한다. 시작 상태가 같다면 결과도 항상 같다.
하지만 매번 똑같이 해놔도 이랬다저랬다 할 때가 있다.
이런 비결정성을 일으킬만한 몇 안 되는 원인들은 다음과 같다.
1. 내부 상태를 초기화하지 않은 채로 사용 : 초기화 안 된 메모리를 읽음
2. 외부 시스템과의 상호 작용 : 디버깅용 시스템이나 test double로 바꿔보자.
3. 일부러 넣은 임의성 : 시드 값을 똑같이 주면 똑같은 난수를 얻을 수 있다.
4. 다중 스레드 : 경쟁 상태를 만들 수 있게 sleep() 을 사용.
정말로 재현할 수 없다면??
정말 모든 가능성을 다 따져 본 것인지 생각해보자.
같은 영역에 있는 다른 버그부터 해결해보자.
다른 사람을 끌어들이자.
왜 이렇게 실행되는지 논리만으로 증명하는 방법 사용.
진단
기본적인 규칙 중 하나는 한 번에 하나만 고쳐야 한다는 것!
하나만 고친 후 결과를 확인해야 이번에 바꾼 것 때문에 그렇다는 걸 확신할 수 있다.
하지만 사람들은 이 원칙을 자주 까먹는다.
(버그가 잘 안잡혀서 스트레스를 받으면 집중력이 떨어져서 의미없는 테스트를 하게 되는 것 같다. 잘 안될 때 휴식도 하고 심호흡도 하고 생각을 정리해서 차근차근해나가는게 중요한 것 같다.)
며칠이나 몇 주 동안 같은 버그를 잡고 있다면 했던 실험과 결과를 기록해두자.
이미했던 실험을 반복하지 말고 효율적으로 할 필요가 있다.
아무것도 무시하지 않는다.
A나 B라는 결과를 기대하고 했던 실험에서 C라는 예기치 않는 결과가 나온 경우
찾는 버그와 직접적인 연관이 없어보이더라도 예상 못한 일이 생겼다는 것은 우리가 세운 가정 중에 틀린 곳이 있다는 것이다. 이 부분도 기록하고 찾아볼 필요가 있다.
어떤 버그는 널리 쓰이는 기술 때문에 생겼을 수 있다.
막막하다면 구글링을해서 의외로 쉽게 원인과 해결책을 찾을 수도 있다.
여러 가능성 중에 무엇을 먼저 확인할지 선택해야 한다면 가장 간단한 것부터 확인해보는게 좋다.
여러 원인으로 발생하는 버그를 진단할 때는
1. 문제를 격리한 후 여러 원인 중 하나의 원인에만 의존하는 버그를 재현할 수 있는 방법을 찾는다.
2. 같은 영역에 있을 것으로 추측되는 다른 버그를 먼저 확인한다.
3. 1,2 번이 불가능하다면 먼저 심호흡을 하자. 어려운 문제를 만난 것이다.
문제를 설명하는 것은 생각 정리에 도움이 된다. 설명할 사람이 없다면 종이에 쓰거나 이메일로 보내보자.
어려운 문제를 만나 전혀 진전이 없다면 휴식이 필요하다.
일단 묵혀두고 머리를 비우고 잠재의식을 활용해보자.
가끔 전혀 감이 오지 않을 때는 그냥 아무거나 바꿔보는 것도 좋다.
수정
- 땜질 코딩
근본 원인을 알고 있지만 땜질 코딩으로 임시방편적인 수정을 할 수도 있다. 버그가 아키텍처 깊숙한 곳에 있거나 제대로 고치려면 광범위하게 수정해야 하는 경우 등.
땜질 코딩도 나름 괜찮을 때가 있지만.. 극히 드물다. 항상 최후의 수단으로만 생각해야 한다!
리팩토링 시 절대로 코드의 작동을 변경하면서 동시에 리팩토링하면 안 된다.
둘 중 하나만 해야한다. 테스트 실패의 원인이 리팩토링 때문인지 작동 변경 때문인지 알기 어렵다.
로직 하나 바꿀 때마다 VCS(Version Control System)에 커밋한다.
버그 발생 시 무엇때문에 생겼는지 알기 쉽고 이전 상태로 돌아가기 쉽도록 하기 위함이다.(특히 본인 외 다른 팀원이 하기에)
반영
- 동료에게 그가 한 실수를 얘기해줄 때
좋은 의도로 피드백 해주는게 가장 중요하다. 뻐기고 싶은 마음에 그 사람의 실수를 얘기하려는 것이면 닥치고 가만히 있자. 진짜 의도가 훤히 보일 것이다.
모든 사람이 우리 같은 다른 사람에게 이성적으로 모범이 되는 사람은 아니라는 것도 잊지 말자.
'개발' 카테고리의 다른 글
모바일 API 디자인 참고 사항 (0) | 2018.04.04 |
---|---|
클린 코더 - 1장 프로의 마음가짐 (0) | 2017.10.20 |
실용주의 디버깅 - 2부 큰 그림 (2) | 2016.10.18 |
객체 지향 설계 원칙 5가지 (0) | 2016.07.10 |
리눅스 /bin/sh^M: bad interpreter 에러 (0) | 2016.07.04 |