코드를 테스트 주도로 개발하고 싶은데, 막상 시작이 잘 안 될 때가 있습니다. 객체의 인터페이스가 어떤 모습이어야 할지 확신이 서지 않을 수도 있고, 화창한 여름날에 집중력이 흐트러질 수도 있죠. 생각하고 있는 것을 정말 테스트할 수 있을지 불확실할 수도 있고, 그냥 지금 당장 테스트 코드를 작성하고 싶지 않아 미루고 있을 수도 있습니다. TDD를 하고 싶지 않은 순간에도 TDD의 이점을 누리려면 어떻게 해야 할까요?
프로토타이핑, 버릴 목적으로 만들기
"새로운 시스템 컨셉이나 새로운 기술을 사용할 때는 버릴 시스템 하나를 반드시 만들어야 한다. 아무리 완벽한 계획이라도 처음부터 옳게 할 수 없기 때문이다. 그러니 하나를 버릴 계획을 세워라. 어차피 버리게 될 것이다."
– 프레드 브룩스(Fred Brooks)
새로운 기능을 개발하기 시작할 때, 첫 테스트 작성을 앞두고 마비될 듯한 느낌이 드는 경우가 있습니다. API는 어떤 모습이어야 하는지, 어떤 패턴과 관행이 가장 적합한지, 전체적으로 어떻게 조화를 이뤄야 하는지 등 결정해야 할 사항이 너무 많기 때문입니다.
이런 막힘을 뚫는 방법 중 하나가 바로 프로토타입을 만드는 것입니다.
프로토타입을 작성할 때는 스케치를 그린다고 생각하세요. 브레인스토밍을 하고 여러 가지를 자유롭게 시험해 보세요. 아직 테스트나 TDD는 신경 쓰지 않아도 됩니다. 대신 결정하기 어려웠던 부분들을 코드로 직접 탐색해 보세요. 몇 가지 패턴을 시도해 보고, 설계하려는 기능에 어떤 것이 잘 맞는지 살펴보세요. 이 과정에서 앱의 불안정한 부분이 어디인지, 더 깊은 고민이 필요한 곳은 무엇인지, 그리고 사실 걱정할 필요가 없었던 것은 무엇인지 자연스럽게 파악하게 됩니다.
그다음, 프로토타입을 과감히 버리고 다시 만드세요. 이번에는 TDD를 활용하고, 기능을 최선으로 구축하는 방법에 대해 새롭게 얻은 통찰을 바탕으로 진행하면 됩니다.
뭔가 작성한 후 실수로 git checkout -f를 실행해서 배가 얻어맞은 듯한 느낌을 받아본 적이 있나요? 하지만 어차피 만들어야 하는 코드니까 다시 작업했더니, 결과적으로 처음보다 훨씬 나아진 경험이요. 프로토타입 재구축에서도 마찬가지입니다. 이제 기능이 어떤 모습일 수 있는지에 대한 확실한 그림이 있으므로, TDD로 해당 기능을 개발하는 일이 훨씬 수월해집니다.
역방향 TDD(Reverse TDD)
무언가를 구현하는 방법은 알지만, 테스트를 먼저 작성하는 방법을 모르겠는 상황에 부딪혀 본 적이 있나요? 메서드의 한 줄 변경인데 약간의 조정이 필요할 수 있어서, 변경 결과가 어떻게 될지 알기 전까지는 테스트로 굳어지고 싶지 않은 경우도 있죠.
이런 상황에 큰 도움이 되는 간단한 과정이 있습니다.
- 테스트 없이 코드를 먼저 작성합니다.
- 해당 코드에 대한 테스트를 작성합니다. 반드시 통과하는지 확인하세요.
- 작성한 코드를 주석 처리하거나, 코드를 변경한 파일을 되돌립니다.
- 테스트를 다시 실행합니다. 반드시 실패하는지 확인하세요.
- 코드를 다시 작성하고 테스트를 실행합니다. 통과하는지 확인합니다.
- 새로 만든 테스트를 활용해 코드를 리팩토링합니다.
저는 이 방법을 역방향 TDD, 혹은 Green-Red-Green-Refactor라고 부릅니다. TDD의 "테스트 주도 설계"라는 요소는 얻을 수 없지만, 코드가 깨졌을 때 테스트가 실패하는 모습은 확인할 수 있습니다. 이것이 중요한 이유는, 한 번이라도 실패하지 않은 테스트는 신뢰할 수 없기 때문입니다.
역방향 TDD는 설계가 크게 필요하지 않은 아주 작은 변경에 가장 잘 어울립니다. 버그 수정이나 한 줄, 한 메서드, 한 클래스 범위의 국소적인 변경이 대표적이죠. 하지만 저는 특히 아직 다듬고 있는 소규모 변경 작업에서 이 기법을 자주 활용합니다.
페어링 게임(The Pairing Game)
함께할 개발자가 옆에 있다면, 페어링 게임은 익숙한 Red-Green-Refactor 루틴에서 벗어나기 좋은 훌륭한 방법입니다. 진행 방식은 다음과 같습니다.
- 파트너를 찾습니다.
- 실패할 테스트를 작성합니다.
- 파트너가 가장 단순한 코드로 그 테스트를 통과하게 만듭니다.
- 그 코드에서 실패할 새 테스트를 작성합니다.
- 파트너가 그 테스트를 통과하는 코드를 작성합니다.
- 반복합니다…
- 테스트가 모두 통과된 시점에는, 테스트 작성 대신 리팩토링을 선택할 수 있습니다.
- 리팩토링 후에는 테스트 작성자와 코더 역할을 서로 바꿉니다.
비밀 하나를 공개하자면, 제가 처음 TDD를 배운 방법이 바로 볼링 점수 계산기로 페어링 게임을 하는 것이었습니다. 실패하는 테스트를 기반으로 단순한 코드를 작성하는 연습과, 애초에 그러한 테스트를 작성하는 연습에 모두 도움이 됩니다. 두 개발자 모두 상대방의 코딩 스타일과 선호하는 기법에 대해 많이 배우게 되죠. 그리고 거의 즉시 몰입(flow) 상태에 들어서기 때문에, 마칠 때쯤이면 기분이 아주 좋아집니다.
코드 작성 속도는 다소 느려질 수 있지만, 훨씬 재미있고 값진 학습 경험이 된다는 점이 매력입니다.
장난기를 잃지 말고 실험하세요
TDD는 교조가 되어서는 안 됩니다. 핵심 TDD 개념을 유연하게 응용하다 보면 의외로 흥미로운 지점에 도달할 수 있고, 때로는 더 나은 코드로 이어지기도 합니다.
그러니 실험해 보세요. 코드 작성과 TDD에 대한 새로운 접근법을 시도하고, 그것들이 어떤 느낌을 주는지, 어떤 코드가 결과물로 나오는지 살펴보세요. 그리고 매일 하는 일 속에서 즐거움과 몰입의 순간을 계속해서 다시 발견하시기 바랍니다.