kidk.kr 강그루 블로그

내가 아는 것과 안다고 생각하는 것

내가 아는 것과 안다고 생각하는 것 - 이미지: 마추피추

일화 1.

얼마전 AWS Lambda로 Node.js의 child_process 모듈을 다룰 일이 있었다. 예전에 토이프로젝트로 Slack 알림 챗봇을 만들면서 Lambda를 다루어 본 경험이 있었기에, 프로세스만 잘 다루면 수월할 거라고 생각했다. 분명 로컬에서 테스트코드를 꼼꼼이 작성하고, serverless offline으로 invoke 테스트까지 해서 ‘잘 되겠지’ 확신했다.

막상 기존 REST API와 Lambda를 연동 테스트를 하려고 하니 원하는대로 동작하지 않았다. 디버깅을 아무리 해봐도 알 수가 없어서, child process를 다루는 데 미숙한 내가 실수를 저지른 게 아닌가 싶었다. 며칠내내 child_process 모듈과 vm2 라이브러리를 사용하는 로직과 씨름했다. 아무리 해도 답이 보이지 않았다. 계속해서 일어나는 timeout과 찍히지 않는 로그… :sweat_smile:

디버깅은 힘들다...

디버깅은 벌레 잡는 것처럼 힘들다…

결론부터 말하자면 첫 번째 원인은 child process를 종료시키고도 한동안 process가 돌고 있는 것이 문제였는데, 이 때문에 사실상 Lambda의 콜백함수는 호출이 되었으나 AWS Lambda에서 timeout이 일어났다.

다른 원인은 child process의 vm 상에서 외부 모듈을 불러오는 데 시간이 많이 걸려서 정말로 timeout이 일어나고 있었다! (이 두 가지 문제가 함께 작용하고있었다.)

디버깅에 지쳐서 결국은 포기하는 마음으로, child process의 모든 리스너에 로그를 찍어보기로 했다. console.logconsole.time을 사용해 어느 구간에서 시간이 얼마나 걸리는지 일일이 확인했다. console.time 은 라벨을 지정해 특정 시간을 측정할 수 있어서, 콜백함수든 Promise든 어렵지 않게 수행 시간을 측정할 수 있었다. 그러니 점점 실마리가 보이기 시작했다. child process와 vm2는 잘 동작하고 있었다.

마침 그때 동료 개발자의 도움으로 AWS Lambda context의 callback 호출 옵션값을 설정해줄 수 있다는 것을 알게 되었고, 이것으로 일단 가장 근본적인 timeout의 원인을 해결했다.

다른 한 가지 문제는 각 package를 require하는 시점에 걸리는 시간을 측정함으로써 어느 패키지가 많은 시간을 잡아먹는지 파악할 수 있었다. 이것으로 불필요한 패키지는 제외시킬 수 있었다.

일화 2.

그리고 나는 다시 작은 마이크로서비스 (https 응답을 받아서 https 요청을 하는 간단한 인터페이스 서비스)의 프로토타입을 개발하게 되었는데, Node.js의 https모듈만 사용해서 작성하려고 했다. 평소에는 request 라이브러리나 axios같은 걸 써서 했기에 이것과 같은 방식으로 하면 되겠지, 했지만 쉽게 되지 않았다. StackOverflow에도 라이브러리를 사용해 ‘쉬운’ 해결책만 제시하는 답변들이 많았는데, 상대적으로 low-level에서 Node.js를 사용하는 방법을 익혀야 했다. 그러면서 문득 며칠전 골머리를 싸매던 상황과 오버랩되며 이런 생각이 들었다. ‘내가 안다고 생각하는 게 정말 아는 것인가?’

문제점

  1. AWS Lambda를 ‘안다’고 생각했지만 사실은 잘 몰랐다. Handler함수가 간단하다고 해서 AWS Lambda 클라우드 플랫폼마저 간단한 건 아니니까 말이다.
  2. child_process를 ‘모른다’고 생각했지만 생각보다 잘 알고 있었다. 프로세스의 리스너는 잘 동작하고 있었다.
  3. Promisebluebird를 deferred해서 사용하는 패턴(사실 anti-pattern이지만) 에 익숙하지 않아서 잘 모르는 것 같았고 이곳을 계속 의심했다.
  4. Node.js의 https모듈을 ‘안다’고 생각했다.(심지어 사용해본 적이 없는데도!) 마치 request 라이브러리처럼 쓰면 될 것이라고 생각했다. 이는 논리적으로도 틀리다. 왜냐하면 그 라이브러리는 https를 편하게 쓰려고 만든 라이브러리니까 말이다.

내가 아는 것이 정말로 아는 것인가?

당신이 아는 것과 당신이 안다고 생각하는 것 - 사이언스 팩토리

사람들은 자신의 생각을 평가하는데 ‘너무’ 뛰어난 나머지 스스로의 생각을 과대평가 하는 경향이 있다고 한다. 이것은 심리학자들의 실험에서도 드러났는데, 가짜 철학, 문학, 생물학 용어를 만들어 낸 다음 (예를들자면 ultra-lipids, bio-sexual, retroplex 같은 식의 이상한 합성어들) 이것에 대해 사람들에게 알고 있는지, 익숙한 용어인지 물어보았다고 한다. 무려 124명중 110명의 사람이 이 가짜 용어에 익숙하다고 했다. 자신이 아는 것을 확대해석하기 때문에, 처음보는 가짜 용어이지만 안다고 생각한 것이다.

위 영상에 소개된 재미있는 실험이 한 가지 더 있는데, 실험자들에게 논리추론 문제를 풀게한 다음 답에 대해 논증을 쓰게 했다고 한다. 그리고 같은 문제에 대해서 다른 사람의 논증을 평가하도록 했는데, 사실 그 논증은 다른 사람의 논증에 자신이 쓴 논증이 섞여있는 것이었다. 놀라운 사실은 이들 중 절반은 섞인 논증을 알아차렸지만 알아차리지 못한 사람들도 있었고, 이들 중 일부는 극도로 비판적으로 자기 자신의 논증을 비판했다.

Self-serving biases와 코드리뷰

프로그래밍도 논리추론과 마찬가지로 논리를 전개해나가는 과정이니 소스코드는 논증과 유사하다고 할 수 있다. 비슷한 프레임워크, 비슷한 라이브러리, 비슷한 프로그래밍 언어는 때때로 개발자가 ‘안다’고 생각한 나머지 정작 자신이 모르는 것을 보지 못하게 만들 수 있다.

이런 자신에 대한 과대평가가 어느 순간 버그를 만들어내고, 디버깅을 힘들게하는 요소로 작용한다. 그리고 자신이 작성한 코드와 소프트웨어는 대개 ‘너무 잘’ 평가한다. (그렇지 않다면 프로덕션에서 버그리포팅이 올 리가 없지…) 그렇기 때문에 이런 심리적인 문제를 극복하려면 동료들의 도움이 필요하다. 코드리뷰를 통해서 다른 사람이 쓴 코드를 볼 때, 자신이 쓴 코드를 보는 것 보다 비판적으로 볼 수 있고 어떤 경우에는 자신이 쓴 코드마저 비판적으로 보이는 순간이 있기 때문이다.