걸리버 개발기
돌다리도 두들겨보고 건너라. 돌다리가 아무리 튼튼하고 안전하더라도 다리를 건널 때에는 돌다리를 시험해보고 건너는 조심스러운 태도를 가지라는 말이다. 조심스러운 태도를 견지해야하는 것은 소프트웨어 개발에 있어서도 마찬가지이다. 개발자에게 건너야할 강은 새로운 기능이고, 돌은 코드 조각, 다리는 기능을 상용 환경에 선보이는 배포이다. 그렇다면 두드림은? 바로 테스트이다. 이 테스트라는 단어는 개발에 있어서는 단순한 시험이라는 뜻 이상의 뜻을 가진다. 이 글에서는 개발에 있어서의 테스트의 다양한 의미와 중요성을 적어보고자 한다.
첫번째로 개발에 있어서의 테스트는 개발한 기능이 잘 동작하는지 확인하는 의미를 가진다. 개발이 완료된 코드를 바로 상용 환경에 내보내는 것은 위험하다. 사용자는 웹사이트나 앱이 조금이라도 이상하거나 버벅이면 금새 흥미를 잃고 다른 웹사이트나 앱으로 이탈하곤 한다. 이는 서비스의 성패를 가르는 이탈율, 재진입율과 같은 지표들에 바로 반영된다. 소중한 사용자가 이탈하는 수준을 넘어서 서비스의 보안이나 정합성과 관련된 문제가 발생한다면 더 큰 문제이다. 소중한 사용자를 테스터로 사용할 수는 없다. 따라서 개발자는 개발한 기능이 잘 동작하는지 여러 층위의 테스트를 작성하여 확인해보곤 한다. 가장 작게는 클래스나 메서드와 같은 코드의 조각을 테스트하는 단위 테스트를 작성하여 테스트한다. 이후 빌드 과정에서 단위 테스트들을 묶어 모두 통과하는지 확인한다. 어플리케이션을 실제로 작동시킨 뒤에 직접 요청을 보내고 응답값을 통하여 정상 작동을 확인하는 통합 테스트 스위트(integration test suite)를 작성하기도 한다. 준상용(staging) 환경에서 전문 테스터와 QA가 진행하는 기능 테스트들도 테스트의 과정이다. 쇠는 두드릴수록 단단해진다고 하던가. 작게는 코드 조각에서 크게는 준상용 환경에서의 테스트를 거칠수록 서비스는 단단해진다.
두번째로 테스트는 개발하지 않은 기능이 잘 동작하는지 확인하는 의미를 가진다. 개발자가 아닌 사람들에게는 조금 어색하게 들릴지 모르겠지만, 버그는 내가 개발하지 않은 부분에서 발생하는 경우가 많다. 이는 소프트웨어 개발을 하얀 백지에서 시작하는 경우가 많지 않기 때문이다. 보통의 경우 개발자는 수만 줄의 코드 사이에 자신의 코드를 끼워넣는 방식으로 개발을 진행한다. 물론 개발자는 자기 나름대로 수만 줄의 코드의 맥락을 파악한 후 자신의 코드를 더할 그럴싸한 계획을 가지고 있을 것이다. 버그에 얻어맞기 전까지는 말이다. 이 경우 도움이 되는 것이 작성해둔 테스트들이다. 개발자는 남이 작성해둔 코드를 이어받아 작업을 진행하는 일이 잦은데, 복잡한 핵심 기능에 테스트가 작성되어 있는 경우 안도의 한숨을 내쉬곤 한다. 매일매일 코드를 작성하다 보면 심지어 내가 작성한 코드도 남이 작성한 코드처럼 생경해지는 것이 사람이다. 그럴 때 작성된 테스트들은 내가 개발하지 않은 기능들에 대해서도 안정성을 측정할 수 있는 최후의 보루가 된다.
세번째로 테스트는 개발 요건을 명확히 정리하는 의미를 가진다. 글을 쓰다보면 단어와 문장 자체가 생명력을 가지는 경험을 하곤 한다. 내가 적으려고 했던 어렴풋한 뇌 속의 생각들이 펼쳐지다 보면 글의 주제를 벗어나 버린다. 신기하게도 혹은 당연하게도, 코드도 작성하다 보면 유사한 경험을 하곤 한다. 필요 이상의 코드를 작성하고, 필요 이상의 코드들은 부작용을 일으키는 일이 생각보다 흔하다. 이 때 코드의 테스트들을 작성하면 개발의 핵심적인 요건들에 집중할 수 있다. 테스트를 작성하는 과정에서, 기능에 필요한 조건들과 상황 그리고 산출되는 결과물이 정의되기 때문이다. 그래서 좋은 테스트 코드는 그 자체로 코드의 기능에 대한 가장 간결한 설명이 되기도 한다. 테스트로 정의된 조건과 결과물을 바탕으로 개발 요건을 정리하다보면, 기능에 핵심적인 코드 조각과 부차적인 코드 조각이 정리됨은 물론이다.
마지막으로 테스트는 코드를 아름답게 하는 의미를 가진다. 앞서 이야기한 것처럼 테스트 코드를 작성하는 과정에서 개발 요건이 명확하게 정리 될 수 있다. 명확히 정리된 개발 요건은 자연스럽게 아름답고 간결한 코드로 이어진다. 요즘에는 테스트 주도 개발(Test Driven Development; TDD) 이라고 하여 코드를 작성하기에 앞서서 테스트 코드를 작성하자는 프로그래밍 방법이 주목받고 있다. 개발하고자 하는 기능을 테스트하는 코드를 먼저 작성하고, 그 테스트를 성공시키는 코드만을 작성하자는 것이다. 테스트 주도 개발은 단순히 코드를 작성하고 테스트하는 프로그래밍의 순서를 뒤집는 것 이상의 의미를 가지고 있다. 테스트 주도 개발은 테스트가 코드의 확인을 넘어서 코드 자체의 질적인 향상에 기여할 수 있다는 관점의 전환을 제시하고 있다. 쇠는 뜨거울 때에 두들겨야 하는 것처럼, 테스트는 아직 구현에 대한 고민과 열정이 식지 않았을 때에 작성하는 것이 좋다. 물론 개발에 대한 열정은 기능을 완성하기 전에 가장 뜨겁다.
회사에 입사하고 선배 개발자에게 어떤 개발자가 좋은 개발자인지에 대해 설익은 질문을 던졌을 때가 있었다. 선배 개발자는 잠시 고민하더니 다음과 같이 답했다. "테스트를 잘하는 개발자가 좋은 개발자다." 테스트의 다양한 의미와 중요성을 생각해 보았을 때에 정답에 가까운 대답이 아니였나 싶다.