아무리 공들여 만든 프로그램에도 버그는 있기 마련입니다. 그래서 개발자의 일상에는 늘 버그가 따라다니고, 적지 않은 시간이 그 버그를 잡는 데 쓰입니다. 버그를 잡는 이 일을, 개발자들은 ‘디버깅(debugging)‘이라 부릅니다. 벌레(bug)를 없앤다(de-)는 뜻 그대로이지요.
그런데 이 벌레 잡기가 좀 별납니다. 한밤중 방에 나타난 벌레 한 마리를 떠올려 보면 알 수 있습니다. 신문지든 살충제든 손에 들고 잡을 준비는 끝났는데, 정작 그 녀석이 어디로 숨었는지 보이지 않습니다. 불을 켜고, 가구 밑을 들여다보고, 커튼 뒤를 살피며 한참을 헤맵니다. 막상 찾기만 하면 잡는 건 한순간입니다. 정작 애를 먹이는 것은 잡는 일이 아니라, 그 작은 것이 _어디에 숨었는지_를 찾아내는 일입니다. 코드 속 버그도 꼭 그렇습니다.
디버깅은 수리가 아니라 이해다
이 사실은 개발자가 디버깅에 쏟는 시간을 들여다보면 드러납니다. 그 시간의 대부분은 _고치는 데_가 아니라, 무엇이 어디서 왜 잘못됐는지를 찾는 데 들어갑니다. 원인만 정확히 짚어내면, 정작 고치는 일은 한 줄로 끝나는 경우가 허다합니다. 며칠을 헤매다 찾아낸 문제의 해결책이 고작 글자 하나였던 경험을, 개발자라면 누구나 갖고 있습니다.
그러니까 디버깅의 본질은 수리가 아니라 이해입니다. 어떻게 고칠지를 아는 기술이라기보다, 무엇이 진짜 문제인지를 알아내는 기술이지요. 그리고 이 사실은 우리가 삶의 문제를 대하는 방식에 대해 곧바로 무언가를 묻습니다. 우리는 문제 앞에서, 이해하는 데 충분한 시간을 쓰고 있을까요. 아니면 이해하기도 전에 고치려 들고 있을까요.
코드는 거짓말을 하지 않는다
문제를 이해하려는 개발자가 가장 먼저 기대는 믿음이 하나 있습니다. 코드는 거짓말을 하지 않는다는 것입니다. 바꿔 말하면, 이유 없이 일어난 일은 없다는 것입니다. 까닭 없이 잘못되는 코드는 없습니다. 이상하게 보이는 모든 동작 뒤에는, 아직 내가 찾지 못했을 뿐인 까닭이 어김없이 자리하고 있습니다.
우리는 무언가 잘못되면 흔히 기계를 의심합니다. “컴퓨터가 왜 이러지”, “이게 갑자기 왜 이래” 하고요. 그러나 컴퓨터는 변덕을 부리지도, 기분 따라 다르게 굴지도 않습니다. 그러니 결과가 잘못되었다면, 거기에는 반드시 그렇게 될 만한 이유가 있습니다. 우연도 심술도 아닙니다. 단지 그 이유를 내가 아직 찾지 못했을 뿐입니다. 디버깅은 바로 이 믿음 위에서 시작됩니다. 어딘가에 반드시 있는 그 까닭을, 끝까지 따라가 기어이 밝혀내겠다는 믿음 말입니다.
그 까닭은 어디에 있을까요. 물론 늘 내 쪽에 있는 것은 아닙니다. 때로는 남이 만든 부품에, 때로는 예상 못 한 환경에 원인이 숨어 있기도 합니다. 그러나 노련한 개발자일수록 “컴퓨터가 이상하네”라고 말하기 전에 “내가 어디서 틀렸지”부터 들여다봅니다. 원인을 바깥에서 찾는 동안에는 아무것도 고쳐지지 않기 때문입니다. 코드는 거짓말을 하지 않는다는 믿음은 결국 문제의 책임을 바깥으로 미루지 않고 자기 쪽으로 끌어당기는 태도이기도 합니다.
여기에는 한 가지 까다로운 함정이 있습니다. 가장 잡기 힘든 버그는, 흔히 내가 “이건 당연히 맞겠지” 하고 확인조차 하지 않은 자리에 숨어 있다는 것입니다. 너무 굳게 믿은 나머지 들여다볼 생각조차 하지 못한 곳 말입니다. 코드가 거짓말을 하지 않는다면, 정작 거짓말을 하는 것은 “그럴 리 없다”는 나의 확신인 셈입니다.
막연한 불안을 구체적인 사실로
원인이 우연이 아니라 어딘가에 반드시 있다면, 거기서 한 가지가 따라 나옵니다. 같은 조건이라면, 같은 문제가 다시 일어난다는 것입니다.
그래서 디버깅의 첫걸음은 의외로 ‘문제를 다시 일으켜보는 일’입니다. 개발자들은 이것을 ‘재현한다’고 말합니다. 언제, 어떤 상황에서 그 문제가 생기는지를 정확히 짚어, 같은 일을 마음대로 다시 일으킬 수 있게 만드는 것입니다. 어쩌다 한 번 일어나고 다시는 붙잡히지 않는 문제는, 사실상 고칠 수가 없습니다. 무엇을 고쳐야 할지 손에 잡히지 않으니까요.
우리는 종종 “뭔가 잘못됐다”는 막연한 불안에 사로잡힙니다. 일이 잘 안 풀리는 것 같고, 관계가 어긋나는 것 같고, 어딘가 문제가 있는 것 같은데, 그게 정확히 무엇인지는 흐릿합니다. 그리고 흐릿한 문제는 결코 풀리지 않습니다. 손댈 자리가 없기 때문입니다. 막연한 불안을 “정확히 이런 상황에서, 이런 일이 벌어진다”는 선명한 사실로 바꿔내는 것. 거기에 이르러야 비로소 우리는 앞으로 나아갈 수 있습니다. 문제를 정확히 아는 것은, 문제를 푸는 일의 절반이 아니라 거의 전부입니다.
좁혀 들어간다
이렇게 손에 잡힌 문제를 앞에 두고, 개발자는 이제 원인을 향해 좁혀 들어갑니다. 그런데 그 방식이 뜻밖입니다. 범인을 찾겠다고 거대한 코드 전체를 처음부터 끝까지 노려보지 않습니다. 오히려 반대로 합니다. 문제가 없는 곳을 하나씩 지워나갑니다.
“여기까지는 멀쩡하다.” “이 부분도 정상이다.” 이렇게 이상이 없는 영역을 하나씩 확인해 지워가다 보면, 헤매야 할 범위가 점점 줄어듭니다. 절반을 확인해 멀쩡하면 나머지 절반으로, 그 절반의 절반으로. 그렇게 좁히고 또 좁히다 보면, 결국 문제는 더 이상 숨을 곳이 없는 한 점에 몰립니다. 막막하던 전체가, 다룰 수 있는 작은 조각이 되는 순간입니다.
이것은 거대한 문제 앞에서 압도당하지 않는 법이기도 합니다. 우리는 큰 문제를 만나면 그 크기에 질려 손도 못 댑니다. 그러나 한 번에 전부를 풀려 하지 않고 “적어도 여기는 괜찮다”를 하나씩 확인해 나가면, 막막함은 조금씩 줄어듭니다. 문제의 크기는 그대로여도, 내가 들여다봐야 할 자리는 점점 작아집니다. 결국 손쓸 수 없을 것 같던 일도, 손쓸 수 있는 한 점으로 좁혀집니다.
이해한 다음에야
이렇게 원인을 정확히 짚어내고 나서야, 개발자는 비로소 코드에 손을 댑니다. 순서가 중요합니다. 이해가 먼저고, 수정은 그다음입니다.
이 순서를 거스르면 어떻게 될까요. 원인을 모른 채 “이렇게 하면 되려나” 하고 손대는 모든 수정은, 사실 추측에 불과합니다. 운 좋게 증상이 사라질 수는 있어도, 진짜 원인은 그대로 남아 다른 자리에서 다시 터져 나옵니다. 더 나쁜 경우, 성급한 처방이 멀쩡하던 다른 곳까지 망가뜨립니다. 이해 없이 서두른 수리는 문제를 풀지 못합니다. 그저 다른 자리로 옮기거나, 더 키울 뿐입니다.
좋은 의사를 떠올려보면 이 순서가 어떤 것인지 알 수 있습니다. 환자가 아프다고 해서 곧장 약부터 건네는 의사는 없습니다. 먼저 무엇이 잘못됐는지를 진단하고, 그다음에 처방합니다. 열을 내리는 약은 열을 잠재울 뿐, 열을 일으킨 병까지 낫게 하지는 못하니까요. 원인을 짚지 않고 증상만 덮는 처방은 잠시 편하게 할 뿐, 정작 병은 그대로 두거나 키웁니다.
우리가 삶의 문제 앞에서 자주 저지르는 일이 바로 이것입니다. 무엇이 진짜 문제인지 알기도 전에, 불편한 상태를 빨리 벗어나고 싶어 황급히 무언가를 합니다. 갈등의 원인을 이해하기 전에 사과하거나 화를 내고, 일이 안 풀리는 까닭을 짚기 전에 방법부터 바꿉니다. 그렇게 증상은 잠시 가라앉지만, 같은 문제는 모습을 바꿔 되돌아옵니다.
문제를 직면한다는 것은, 덮고 싶은 충동을 누르고 그 앞에 오래 머무는 일입니다. 무엇이 진짜 문제인지 알아낼 때까지, 섣불리 고치려는 손을 거두고 견디는 일이지요.
그렇게 끝까지 이해된 문제만이, 두 번 다시 같은 자리에서 우리를 무너뜨리지 않습니다.
댓글 (0)
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요.