TDD
TDD (Test Driven Development)
TDD란, Test Driven Development의 약자로 켄트 벡(Kent Beck)이 1999년 익스트림 프로그래밍의 일부로 제안한 소프트웨어 개발 방법론이다.
TDD는 우리 말로 테스트 주도 개발이라고 하며 의미 그대로 개발하려는 기능에 대한 테스트 코드를 먼저 작성하고 해당 테스트를 통과하는 코드를 작성하므로써 테스트가 개발을 주도하는 방식의 소프트웨어 개발 방법론이다.
켄트 벡은 이 개발 방법론을 초기에는 Test First Development(TFD, 테스트 우선 개발) 이라고 불렀으나 First의 경우 Last라는 명백한 반의어가 있었기 때문에 반의어가 모호한 TDD라고 부르기로 했다.
그러면서 “테스트 주도 개발을 하지 않을 거라면 추측 주도 개발을 할 거냐” 는 농담을 했다고 한다.
때문에 간혹 테스트를 작성하는 행위 자체를 TDD라는 용어로 사용하는 경우도 있는데, 켄트 벡이 TDD로 명명한 이유가 그 자체로 테스트를 나중에 작성하는 것과는 차별을 두려고 한 것이므로 테스트를 먼저 작성하지 않고 그저 테스트를 작성하는 행위 자체를 TDD라고 하는 것은 적절하지 않다.
TDD Cycle
위 그림은 TDD 개발 싸이클을 표현한 것이다.
RED(Fail) → GREEN(Pass) → REFACTOR(Refactoring) 순으로 계속 반복 순환하는 것을 확인할 수 있다.
RED(Fail): 처음에는 실패하는 아주 작은 테스트를 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
GREEN(Pass): 테스트를 통과만 하는 아주 간단한 코드를 작성한다.(실제 동작하는 로직 없이 하드코딩으로 통과만 하도록 작성)
REFACTOR(Refactoring): GREEN에서 작성한 테스트를 통과만 하는 아주 간단한 코드를 해당 논리 흐름 그대로 실제 동작하도록 작성한다. 이 때 작성하는 코드는 잘 작동하고 테스트를 통과하며 보기 좋고 이해하기 쉬운 코드로 리팩토링 한다. 만약 이 과정 중 실패 케이스를 추가로 발견한 경우 다시 RED로 돌아가서 동일한 순서로 반복하여 잘 작동하는 깨끗한 코드를 작성한다. 테스트를 먼저 작성해야 하는 이유 결론 부터 이야기 하면 TDD를 하는 궁극적인 목적은 테스트가 아니라 “Clean Code That Works” 즉, “작동하는 깨끗한 코드”이다.
잘 작동하는 깨끗한 코드로 개발하기 위해 TDD를 하는 것이다.
켄트 벡은 자신의 저서 테스트 주도 개발(TDD)에서 “TDD의 아이러니 중 하나는 TDD가 테스트 기술이 아니라는 점이다. TDD는 분석 기술이며, 설계 기술이기도 하다. 사실 개발의 모든 활동을 구조화하는 기술이다.”라고 이야기 하고 있다.
즉, TDD는 단순히 내가 개발한 기능을 테스트하는 것이 아니라 내가 개발할 기능의 요구사항을 분석하고 설계하고 구조화하여 잘 작동하는 깨끗한 코드로 개발할 수 있도록 한다는 것이다.
1. 작동하는 깨끗한 코드로 이끈다.
계속 이야기하고 있는 “작동하는 깨끗한 코드”란 보기 좋은 코드, 이해하기 쉬운 코드 등 여러 의미가 있겠지만 가장 핵심적인 의미 한 가지만 고르자면 “변경이 쉬운 코드”일 것이다.
TDD로 개발하면 작동하는 깨끗한 코드, 즉 변경이 쉬운 코드를 작성할 수 있다.
테스트 주도 개발이라는 것은 어쩌면 테스트가 변경이 용이한 작동하는 깨끗한 코드로 이끌도록 주도한다는 것이기도 하다.
2. 느슨한 결합을 만든다.
앞에서 테스트 주도 개발은 작동하는 깨끗한 코드로 이끈다고 했다.
소프트웨어 설계에서 말하는 느슨한 결합(Loose Coupling)이라는 것이 있는데, 그 의미를 요약하면 다음과 같다.
“시스템의 구성 요소(Component)가 서로 약하게 연관되어 관계를 떼어낼 수 있고, 그 때문에 한 구성요소에 변화가 생겼을 때 다른 구성 요소의 성능이나 존재에 최소한의 영향을 끼치는 상태”라고 요약할 수 있다.
“시스템의 구성요소가 다른 구성요소의 정의에 대해 많은 지식이 없어도 사용할 수 있는 상황”을 말한다.
전자에서는 구성 요소 간에 서로 의존하는 정도를 이야기 하는 것이라면, 후자는 약간 비슷한 맥락일 수도 있지만 서로 동립적인 개체로써 결합도가 낮은 상황을 의미한다.
위에서 설명한 느슨한 결합이 의미하는 것은 시스템의 구성 요소간의 결합도가 낮아 코드를 변경하려고 할 때 너무 복잡하거나 어렵지 않은 유연한 상태이다.
결론적으로 소프트웨어를 설계할 때에는 시스템의 구성 요소간의 결합의 정도가 낮게 설계해서 코드의 변경이 쉽도록 하는 것이 좋은데, 테스트를 먼저 작성하면 요구사항을 분석하고 설계하는 과정에서 구성 요소간 결합이 강한지 약한지 알 수 있고 테스트 과정에서도 REFACTOR 단계에서 결합이 강한 부분을 발견하고 수정할 수 있기 때문에 결합도가 낮은 유연한 소프트웨어를 설계하고 개발할 수 있도록 해준다.
테스트를 작성하기 쉽지 않다면, 그것은 테스트가 아니라 설계에 문제가 있다는 신호이다.결합도가 낮고 응집성이 높은 코드는 테스트하기 쉽다.< "익스트림 프로그래밍" >
3. 잘못을 일찍 깨달을 수 있다.
먼저 전형적인 폭포수(Water Fall) 방식인 분석→ 설계 → 구현→ 테스트→ 오픈 순으로 개발하는 방식은 긴 시간에 대한 추정이 기저에 깔려 있다.
예를 들면 분석에 몇 개월을 쓰고 또 설계에 몇 개월을 쓰며 구현 및 테스트에는 또 몇 개월을 쓴다.
정해진 기한 안에 제한되고 부족한 정보만을 가지고 소요될 시간을 추정한다는 것인데, 만약 개발 기한이 정해져 있는 상황인데 분석과 설계가 모두 끝나고 구현 또는 테스트를 진행하다가 분석/설계가 잘못되었다는 것을 알게 되었다면 다시 뒤로 돌아갈 수 있을까?
가능은 하겠지만 이 것은 매우 큰 비용이 들것이고 지금까지 한 수고가 헛수고가 될 가능성이 높아진다.
우리는 분석/설계를 하면서 이미 발생할 문제들을 알고 있고, 이해하고 있다고 착각한다.
하지만 실제로는 아는 것이 없을 때, 이런식으로 문제가 없고 모두 이해했다고 확신을 갖을 가능성이 높다.
위 그림은 더닝 크루거 효과를 설명하는 우매함의 봉우리 그래프이다.
더닝 크루거 효과는 기존에 알고 있는 것이 사실과 다르거나 틀리다는 것을 인지하게 되면서 겸손한 자세로 배우려는 태도를 갖게 된다는 것을 설명하는 심리학 용어로서 인지 편향 중 하나이다.
더닝 크루거 효과를 보며 알 수 있는 것은 진짜 문제는 잘못을 늦게 깨닫는 데서 생긴다는 것이다. 아는 것이 없을 때, 문제가 없다고 확신할 확률이 높으며 그러면 잘못하게 될 가능성이 높아진다.
누구나 잘못을 할 수는 있지만 잘못을 늦게 깨닫는다면 돌이킬 수 없게 된다.
테스트 코드를 먼저 작성하는 것은 분석/설계를 한 직후 바로 테스트 코드를 실행해서 RED - GREEN - REFACTOR순서의 TDD Cycle을 짧은 주기로 반복하여 인지하지 못하고 있던 것들을 알게 되기 때문에 무엇이 잘못되었는 지 빨리 깨달을 수 있게 해준다.
즉, 내가 개발하는 것은 사용하기 위함이고 테스트가 바로 개발한 것을 사용하는 것이기 때문에 테스트를 먼저 작성하는 것은 사용한다는 것을 먼저 정의하는 것이고 부담이 작은 테스트를 반복적으로 실행해가며 개발한 것을 바로 사용하도록 해서 잘못을 일찍 깨닫게 되는 것이다.
4. 과업을 정의하고 필요 없는 일을 제거한다.
HBR(Harvard Business Review)에 실린 <지식 근로자의 생산성을 어떻게 끌어올릴 것인가>에서 피터 드러커는 지식 근로자의 생산성을 가장 크게 높일 수 있는 방법으로 과업을 정의하고 해야 할 필요가 없는 일들을 제거하는 것이라고 말했다.
이와 비슷한 것이 요즘 많은 조직에서 도입하는 OKR 역시 낭비를 줄이고 조직의 성과를 달성하기 위해 목표를 명확히 하는 것이다.
프로그램을 만들 때도 마찬가지로 목표를 명확히 해야 시간을 낭비하지 않을 수 있다.
테스트를 먼저 작성한다는 것은 프로그램이 무엇을 해야 하는 지 명시적으로 정의하는 것이다.
이런 행위는 내가 무엇을 아는 지, 모르는 지 인지하게 만든다.
이렇게 모르는 것을 인지하고 집중하므로써 위에서 이야기한 더닝-크루거 효과 처럼 ‘무지함’을 벗아나 ‘깨달음’으로 갈 수 있다.
'아는 것에서 모르는 것으로'는 우리가 어느 정도의 지식과 경험을 가지고 개발을 시작한다는 점, 개발 하는 와중에 새로운 것을 배우게 될 것임을 예상한다는 점 등을 암시한다.이 두 가지를 합쳐보자.우리는 아는 것에서 모르는 것으로 성장하는 프로그램을 갖게 된다.< "테스트 주도 개발" >
위에서 테스트 주도 개발의 저자 켄트 벡은 ‘성장하는 프로그램’이라는 은유를 했다.
성장의 핵심은 학습이며 효과적인 학습의 방법 중 시험 효과(Testing Effect) 라는 것이 있다.
즉, 같은 것을 반복해서 여러 번 공부하는 것 보다 한 번 공부하고 여러 번 반복해서 시험을 보는 것이 더 효과적이라는 것이다.
그 이유는 뇌와 관련이 있는데 연구에 따르면 우리의 뇌는 어떤 것을 계속 집어 넣으려고 하는 것 보다는 집어 넣은 것을 반복해서 떠올리려고 할수록 기억이 오래 간다고 한다.
이렇듯 테스트는 학습에 매우 효과적인 도구이다.
모든 시스템을 다 만들고 나서 오픈 전에 테스트하는 것이 아니라 부담이 적은 작은 테스트를 여러 번 반복해서 실행하다 보면 잘 모르고 있거나 잘못 알고 있는 점을 발견하고 그 것을 초기에 바로잡는 방향으로 시간을 사용할 수 있게 하고 이는 나중에 생기는 문제에 대한 불안을 낮출 수 있다.