ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TDD/BDD의 허와 실
    해외취업이야기 2013. 12. 12. 20:15

    사실 한국에서 작은 개발팀에서 일할 당시에는, TDD (Test Driven Development)와 BDD (Behaviour Driven Development)라는 용어 조차 알지 못했다. 엄청나게 복잡한 서비스 로직이 녹아들어있는 콜서버를 C++로 개발하면서도, 테스트단계는 항상 매우 단촐했다. 몇가지 주요 시나리오를 구동해주는 시뮬레이터를 돌려보는것이 전부였다. 물론 마치 시계 장인이 현미경과 핀셋으로 숨죽이고 작업을 하듯이, 코드 한줄한줄에 굉장한 신경을 쏟으면서 작업하였던 기억이 난다. 왜냐하면, 시뮬레이터가 모든 케이스를 테스트해준다는 생각은 애초에 해본적이 없기 때문이다. 그저, 대형사고 날만한문제를 예방하며, 마음의 위안을위해서 한번정도 돌려보는 수준이랄까. 지금 생각하면, 어떻게 그렇게 일을 했는지 놀라울따름이다.


    한번은 굉장히 확신을 가지고 아주 단순한 코드 변경을 하고, 당연히되겠지 라는 생각으로 KT의 상용 통신망에 그냥 적용해버렸다가 호처리가 불통되고 난리난적이 있었다. 그때는 정말 당혹스러웠고, 개발자는 스스로를 50% 이상 믿으면 안된다는 생각이 들었다. 하지만 팀장님도, 동료도 TDD/BDD라는걸 언급한적은 없었던 걸로 기억한다. 나 스스로도 그런 대규모 서버개발의 경험이 처음이었고, 그냥 다들 그렇게 하는줄 알았었다.


    영국에와서, TDD/BDD/Agile을 사용하는 개발팀에서 완전 새로 시작하는 프로젝트에 참여하게 되었다. 언어나 문화적으로 익숙치 않은 환경에서 시작하는것 자체로도 상당한 어려움이 있었는데, 거기다가 개발 방법까지도 익숙하지 않다보니 처음에는 상당히 귀찮았다. 특히 내가 개발한 파트에 대한 유닛테스트 코드를 작성하는게 너무 귀찮았다. 특히나 서버쪽이다보니 다양한 상황에 맞는 유닛테스트 코드를 작성하기가 너무 복잡한 경우가 많았다.


    유닛테스트 작성은 그 자체가 귀찮은것 뿐 만 아니라, 코드의 아키덱쳐에까지 영향을 미쳤다. 여러가지 복잡한 클래스들을 Mocking 해야하다보니, 실제로는 꼭 필요하지 않은 부분에도 interface를 사용해야 하는 경우가 많았다. 유닛테스트를 염두에 둔 개발에 익숙하지 않던 초기에는, 아주 깔끔하게 완성된 모듈을 보며 감상에 젖어있다가 유닛코드를 작성하면서 구조를 뒤집게 된 경우도 많았다. 테스트가 불가능한 구조로 짰기 때문이다.


    우리 팀은 유닛테스트 외에, 컴포넌트 테스트도 동시에 작성한다. 서버를 가상으로 구동한 후, 실제로 인풋과 아웃풋을 발생시켜서 예상치를 검사하는 방식이다. 이를 위해서 자체의 validator engine과 script를 개발했고, 각각의 개발자가 새로운 기능 추가나 오류 수정시에 스크립트를 작성하여 추가한다. 테스트하고자하는 부분을 지원하기에 engine이나 script가 미흡할 경우, 각각의 개발자가 그때 그때 업그레이드를 하는 식으로 진행 하였고, 지금은 상당히 훌륭한 기능을 하게 되었다.


    이 두가지 테스트 프레임웍을 훌륭하게 유지하면, 개발자의 아주 뻔한 실수부터, 완전 뒷통수 때리는 황당한 side effect까지 예방할 수 있다. 실제로도 그런 경험을 다수 했기 때문에, 나는 이 방법론에 대해서는 백프로 긍정적이다. 현 시점에서부터, 앞으로 30년동안 이보다 더 완벽한 개발 방법론이 있을까 싶을 정도로 신봉한다. 


    여기서 가장 큰 변수는, 개발자 각자가 유닛테스트와 발리데이터를 얼마나 성실하게 수행하느냐인데, 이게 생각보다 어렵다. 귀찮거나, 시간이 없거나, 부주의함으로 인해서 분명히 테스트가 놓치는 부분이 존재하게 된다. 개발자가 자신의 유닛테스트의 완벽함을 확인하는데 최선을 다하지 않는 이상 이렇게 놓친 부분은 오랫동안 공백으로 남게 되고, 언젠가는 문제의 소지가 발견 될 수도 있는 것이다. 또 한가지 위험성은, 양립할 수 없는 착각을 할 수 있다는 것이다. "내 코드는 어차피 유닛테스트와 발리데이터로 검사될거야" 라는 생각으로, 코드 한줄한줄에 심혈을 기울이기보다는 전체적인 구현에 촛점을 맞추게 된다. 이로써 개발속도가 더 빨라지기는 하지만, 부주의한 코딩의 습관이 점차 늘어간다. 즉 실수를 더 많이 하게 된다. 이는 완벽한 유닛테스트 코드의 작성이 밑바탕에 깔려 있어야 하는데, 유닛테스트 코드도 결국 사람이 작성하는것이기에 완벽할 수 는 없는것이다. 그래서 또다른 헛점이 노출된다.


    이런 문제를 일부 완화하기 위해서 우리 팀은 GCOV 같은 code test coverage 툴을 이용한다. 이 analyser를 이용하면 코드의 어느 부분이 테스트가 안되었고, 어느부분은 불완전하게 테스트 되었는지 상당히 훌륭한 수준으로 파악 할 수 있다. 프로젝트 중반즈음, 빌드시마다 자동으로 code coverage report를 퍼블리시 하도록 구성해 놓아서, 일일히 수동으로 검사 할 필요가 없게 하였다.


    하지만 아직까지는 아쉬운 점이 많다. 팀에 조금 더 여유가 있다면, 테스트 부분을 전담하여 관리하는 엔지니어가 있으면 좋을것 같다. code coverage report를 확인하여, 부족한 부분에 대한 유닛테스트 코드 추가를 각 개발자들에게 요구하거나 직접 작성하는 것으로 시작하여, 프로젝트 전반에 대한 이해를 가지고, 발리데이터에 추가되어야 하는 서비스 시나리오들을 간파해 내는 일들을 누군가 전담할 필요가 있다고 본다. 안타깝게도 남의 코드를 꿰뚫어보는 수준의 C++ 프로그래머는 결코 싸지 않고, 하고싶은 일의 수준이 높기때문에 아마도 이런 일만 전담해서 하려고 하는 프로그래머는 구할수 없을것이다. 그래서 development lead가 개발일을 줄이고 이런 일을 주도적으로 수행하는 경우가 많은것 같다.


    결론적으로, TDD/BDD는 각자의 롤을 확실히 세우고, 장치를 잘 설계하면 정말 훌륭하지만, 이부분이 소흘해지면 구멍이 숭숭 난 빵처럼 실속이 없어져 버린다. 더불어 개발 팀장의 역량이 여실히 들어나는 개발 방법론이다.

    댓글

Designed by Tistory.