<aside> 💡 코딩교육 플랫폼 엘리스에 재직하던 2018년 6월 11일에 github.io 블로그에 썼던 글을 살짝 편집해서 옮깁니다.

</aside>

오래되고 기능이 많은 레거시 소프트웨어를 관리하는 조직에서, 신규 기능을 추가하기보다는 여기저기서 터져나오는 문제를 해결하는데 시간을 훨씬 많이 쓰게 되는 경우가 있다. 특히 프로젝트 초기에 좋은 시스템 구조가 설계되지 않았거나, 프로젝트 도중 적절한 리팩토링을 거치지 않았거나, 또는 테스트 코드가 마련되지 않은 상태에서 개발이 진행되다 보면 그리 오랜 시간이 지나지 않아 이런 상황에 빠지기 쉽다. 이런 조직이 다시 개발 생산성을 높이기 위해서는 문제가 생겼을 때 단순히 그 문제를 빠르게 해결하기보다는 좀더 체계적인 방법으로 디버깅을 해야 한다. 그러면 어떻게 디버깅을 체계적으로 할 수 있을까. 그 고민의 결과물을 공유한다.

디버깅은 단순히 버그를 없애는 것이 아니다

위키피디아에 따르면 ‘버그’는 1870년대부터 기계적 결함을 나타내는 용어로 쓰여왔다. 컴퓨터 영역에서는, 1940년대에 초창기의 컴퓨터 하버드 마크 II가 패널에 나방이 끼는 바람에 오작동한 사건을 기록한 것에서부터 ‘버그Bug’와 ‘디버깅Debugging’이라는 용어가 널리 쓰이기 시작했다고 한다.

최초로 기록됐다고 알려진 컴퓨터 버그

최초로 기록됐다고 알려진 컴퓨터 버그

나는 이 디버깅이라는 단어 자체가 일종의 함정을 지니고 있다고 느낀다. 디버그라는 말을 들었을 때 가장 쉽게 연상되는 뜻은 de + bug, 즉 버그를 없앤다는 것이다(실제로 최초의 버그는 패널에 낀 나방을 제거함으로써 해결하긴 했을 것이다). 그래서인지 개발 경험이 적을수록 디버깅할 때 버그를 없애는 것에만 집중하는 경향을 보이는 듯하다. 존경하는 개발자인 김정훈 님으로부터 몇년 전 이런 얘기를 듣기 전까지 나도 그런 개발자였다.

훌륭한 개발자는 버그를 대하는 태도가 다릅니다. 버그를 만났을 때 일반적인 개발자는 짜증내며 빠르게 고치려고만 합니다. 훌륭한 개발자는 버그를 만나면 버그가 생길 수밖에 없었던 근본적 원인을 생각해보고 리팩토링하는 기회로 삼습니다.

당시 수많은 함수들이 들어있는 general.js 에서 이해할 수 없는 문제가 생겨서, console.log를 무작정 찍어가며 디버깅하던 나를 보며 말씀하셨던 걸로 기억한다. 결국 찾아낸 문제의 원인은 변수명의 오타였다. 정훈님은 오타를 고치는 게 아니라 파일명, 변수명을 비롯한 코드 구조 자체를 바꿔야 한다고 말씀하셨다. 이런 근본적인 문제가 해결되어야 나중에 비슷한 문제가 생길 가능성이 줄어든다면서.

‘버그가 생길 수밖에 없었던 근본적 원인을 찾아 개선한다’는 말은 내 머릿속에 깊이 새겨졌고, 이 말을 내 안에서 오랫동안 숙성시켜 나만의 디버깅 템플릿을 만들어봤다. 이 템플릿은 기본적으로는 유저가 리포트한 버그를 고치는 것을 가정하여 만들었지만, ‘버그’는 결국 ‘구현한 기능이 의도대로 동작하지 않는다’는 것이고, 따라서 일반적으로 개발할 때 겪는 문제에 대해서도 적용할 수 있다. 디버깅 과정을 현상문제 원인해결책환경 개선의 네 단계로 나누는 것에서부터 시작한다.


1. 현상

유저 레벨에서 버그를 바라보며 명확한 재현 조건을 찾는다. 문제에 대해 실패하는 테스트 케이스를 만들어낸다고 생각하면 된다. “이런 조건에서(Given) 이런 액션을 했을 때(When) 이런 예상 결과가 나와야 하는데 실제 결과는 이랬다(Then).”와 같은 문장을 도출해낸다.

1-1. 누가, 어디서, 어떤 문제 현상을 겪었는가?

에러 리포트를 기반으로 최초의 테스트 케이스를 만들어내기 위한 질문이다. 웹 프론트엔드 영역에서는 아래와 같이 바꿀 수 있다. 실제로 문제를 겪은 사람의 리포트 링크, 문제 상황 스크린샷, 에러 로그 등의 데이터는 따로 기록해둔다.

1-2. 현상이 발생하는 조건은 무엇인가?