많은 정보 속에서 올바른 정보를 찾기 어려운 이유에 대해
정보의 바다라는 표현이 있다. 이제는 식상한 표현이지만 과거 인터넷이 대중화되기 전에는 인터넷을 통해 방대한 정보에 접근할 수 있게 되면서 정보가 바닷물처럼 많다는 의미에서 쓰인 표현이다. 코딩을 공부할 때도 마찬가지다. 코딩을 하다 보면 마주치는 에러들, 고민들을 구글에 검색하면 나와 같은 문제를 마주쳤던 사람들, 고민을 했던 사람들의 글을 찾을 수 있다. 문제 해결에 도움이 되는 것은 물론이다.
가끔은 검색을 했을 때 전혀 상관없는 정보를 얻게 되기도 한다. 예를 들어, IOException이라는 키워드로 검색했을 때 나타나는 글은 굉장히 다양하다. 왜냐하면 Java 환경에서 코딩을 할 때면 IOException 예외를 아주 쉽게 접할 수 있기 때문이다. 이 경우 좀 더 키워드를 추가하여 내 상황과 맞는 케이스를 찾을 수 있어야 한다. 제시된 문제 해결 방법으로 해결이 되지 않는 경우도 있다. 그 사람의 개발 환경에서만 해결이 되는 방법이라던가, 내 개발 환경이 커스터마이징 되어 있어 제시된 방법으로 문제가 해결되지 않기도 한다. 또는, 아예 환경이 다른 것이 원인이 되기도 한다. CentOS 7에서의 문제를 Ubuntu 16.04에서의 해결 방법으로는 해결되지 않을 수도 있다.
나는 일단 문제 자체는 해결되는 경우에 대해 더 이야기하고 싶다. 이게 무슨 말일까? 문제 해결이 되면 그냥 좋은 것 아닌가? 나는 그렇지 않다고 생각한다. 오히려 이 경우는 위험하기까지 하다. 예를 들어, Python에서 dictionary의 데이터를 이용하여 URL을 build 하는 함수 func1을 만든다고 생각해보자. 굉장히 흔한 문제 중 하나이기 때문에 쉽게 예제 코드를 찾을 수 있을 것이다. 그 예제 코드를 갖다 썼더니 문제없이 잘 실행된다. 코드를 봤을 때도 크게 문제는 없어 보인다. 이걸로 해피 엔딩이면 정말 좋을 것이다. 그런데 몇 달 후, URL build에 사용되는 dictionary의 데이터에 boolean 타입이 추가되고 나니 어디선가 문제가 발생하기 시작한다.
소프트웨어 개발 세상에서는 이러한 문제 상황에서 작업자가 상당히 많은 시간을 쓰게 만든다. 어느 날 갑자기 버그가 생겼다는 제보를 받은 개발자는 버그라는 결과만 놓고 원인을 찾기 위해 결과에서부터 원인까지 거슬러 올라가야 한다. 이를 위해 방대한 코드를 방황해야 한다. 그리고 마침내 함수 func1이 문제였다는 것을 알게 된다. 왜냐하면 기존 요구사항에서 URL을 만드는 데이터로 문자열만 존재한다는 가정이 있었고, 함수 func1가 그 요구사항을 만족하는데 문제가 없었으며, 테스트 코드에서도 기존의 요구사항을 만족하는지만 검증하고 있었기 때문이다. 또한, dictionary의 데이터에 boolean 타입이 추가될 수 있게끔 시스템이 변경됐지만, 사용자들이 그동안 새로운 기능을 이용하고 있지 않아 실제로 그런 데이터가 서버로 들어오기 시작한 것은 일정 시간이 지난 후였다. 변경 즉시 에러가 난다면 최근의 코드 변경 사항으로 원인을 더 빠르게 파악할 수 있겠지만 나중에 발생하는 에러는 원인을 찾기 더 어렵게 만든다. 마지막으로, 슬프게도 버그를 해결하기 위해 삽질을 한 개발자는 func1을 개발했던 개발자가 아니었다.
이런 사례는 꽤나 흔한 일이다. 왜냐하면 소프트웨어 코드는 여러 사람이 함께 개발하고, 긴 시간에 걸쳐 만들어지기 때문이다. 해야 할 일이 너무 많아 바쁜 상황에서 기존 작업자의 작업물을 응용하는 개발 패턴은 매우 흔하게 일어난다. 기존에 문제없던 코드니까 앞으로도 문제가 없을 것이라는 기대를 품고 코드를 재사용한다. 만약, 그 코드에 잠재적인 위험이 도사리고 있다면 그 위험이 고스란히 함께 전파된다. 앞서 예시를 들었던 func1에 문제가 있다는 것을 알게 되어 수정했지만, func1을 따라 만들었던 func2가 어디선가 쓰이고 있을지도 모르는 일이다. 그 코드가 나중에 또다시 문제를 일으킬 가능성이 있다는 것은 명약관화하다.
추후 발생할 수 있는 요구사항 변경까지 고려하여 코드를 작성하는 것은 정말 쉽지 않은 일이다. 여기서 나는 내 작업물을 동료가 얼마든지 재사용할 수 있다는 것과, 그렇기 때문에 처음부터 최대한 잘 해야 한다는 것에 초점을 맞춰서 이야기하고 싶다. 혼자서 개발을 할 때와 다르게 내가 깨달은 문제점은 동료들에게 전파되지 않는다. 우리가 프로토스처럼 칼라로 정신을 공유할 수 있는 게 아니지 않은가.
앞선 상황과 조금은 다른 경험을 이야기해보겠다. 최근에 나는 Hive와 관련된 삽질을 하고 있었다. 검색 결과에서는 다양한 문제 해결 방법이 존재했다. 그리고 일부 문제 해결 방법에서는 공통적으로 bucketing과 관련된 설정을 수정하기를 권하고 있었다. 그러나, 계속에서 삽질하던 과정에서 읽은 매뉴얼을 따르면 해당 설정은 최근 버전의 Hive에서는 기본적으로 활성화되어있어 굳이 설정을 바꿔줄 필요가 없었다. 하지만 많은 사람들은 그 설정을 켜야 한다는 해결 방법을 끊임없이 재배포하고 있었다. 자세히는 모르지만 일단 그 설정을 켠 방법으로 해봤을 때 잘 되니까 올바른 해결 방법인 것으로 알고 살게 되는 것이다.
이 경우에는 매뉴얼을 통해 쉽게 알 수 있는 사항이었지만 그렇지 않은 경우가 많다. 요즘 쓰이는 시스템들은 내부가 너무나도 복잡하여 그 안을 자세히 알고 쓰기보다는 적당히 사용법만 알고 넘어가는 경우가 대다수이다. GCC 컴파일러가 실제로 어떻게 빌드하는지 자세히 알고 쓰는 사람은 거의 없는 것과 비슷하다. 완전히 자세히 알지 못하면서 사용하는 게 잘못됐다고 말하고 싶진 않다. 그런 식이라면 코딩을 시작조차 하기 어려워질 것이다. 하지만 그럼에도 불구하고 내가 무슨 짓을 한 건지는 최대한 알아야 한다. sudo 명령어를 쓰기 전에는 내가 뭘 하는 건지 알고 실행하라는 말과 비슷한 맥락이다. 왜냐하면 내가 결론 내린 해결 방법은 나와, 나의 동료에 의해 계속에서 재배포될 것이니까 말이다.
이것이 내가 연역적 추론의 중요성을 강조하고 다니는 이유 중 하나이다. 검색을 통해 귀납적으로 선택한 해결 방법은 완전히 올바른 게 아닌, 적당히 올바른 경우가 흔하다. 그리고 그렇게 해결한 문제는 언제 다시 부메랑이 되어 돌아올지 모른다. Javascript를 설치하기 위해 Java를 설치하라는 글 따위도 돌아다니는 인터넷 세상에서 정신 똑바로 차리고 살지 않으면 언제 나도 바보 같은 문제 해결 방법을 쓰고, 또 재배포하고 있을지는 알 수 없다.
그렇기 때문에 바쁜 일정이지만 실험, 테스트에 대한 시간 투자를 아끼지 말아야 한다고 생각한다. 그동안 학습한 전공 지식을 통해 연역으로 잘 판단할 수 있다면 괜찮지만, 실제로는 그렇지 않은 경우가 많다. 왜냐하면 앞서 언급한 바와 같이 요즘의 소프트웨어와 시스템들은 완벽히 이해하기에는 너무나도 복잡하기 때문이다. 그렇기 때문에 현실적으로 할 수 있는 최선은 실험과 테스트이다. 실험을 통해 설정을 바꿨을 때 어떻게 결과가 달라지는지 최대한 살펴보고, 왜 그렇게 됐는지 이해하기 위해 시간을 투자해야 한다. 문제 상황을 잘 재현할 수 있는 테스트를 만들고 수행하면서 올바른 해결 방법을 찾기 위해 노력해야 한다. 그렇게 노력해서 찾아낸 해결 방법이 정말로 가장 최선의 해결 방법이 아닐지도 모른다. 하지만 최선에 가까울수록 나중에 문제가 되지 않을 가능성이 높을 것이다. 그리고 그 결과 우리는 더 빠르게 나아갈 수 있을 것이다.