지난 2월 17일 Udemy에서 성장하는 개발자의 학습법이라는 웨비나를 진행했다.
현재 우아한테크코스에서 코치로 계신 메이커준님과 포코님이 연사로 참여하셨다.
첫번째 세션을 진행하신 메이커준님이 테스트 주도 개발(TDD)에 대해 설명해주셔서 이번 기회에 관련 포스팅을 하기로 생각했다.
테스트 주도 개발(TDD)를 아는가. 프로그래밍을 하다보면 한 번쯤은 들어본 적이 있을 것이다. 하지만 실천해본 사람을 적을 것이다.
메이커준님은 TDD의 중요성을 강조하시면서 꼭 개발에 적용해볼 것을 당부하셨다.
특히 테스트를 먼저 만들고, 실제 코드를 작성하고, 원하는대로 동작하는지 빠르게 피드백을 받는 것이 중요하다고 강조하셨다.
❗ 핵심은 더 자주, 더 빨리 피드백을 받는 것
내가 던진 농구공이 들어갔는지 1달 뒤에 알 수 있다면..?
테스트 주도 개발(Test-Driven Development, TDD)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다. 개발자는 먼저 요구사항을 검증하는 자동화된 테스트 케이스를 작성한다. 그런 후에, 그 테스트 케이스를 통과하기 위한 최소한의 코드를 생성한다. 마지막으로 작성한 코드를 표준에 맞도록 리팩토링한다. 이 기법을 개발했거나 재발견 한 것으로 인정되는 Kent Beck은 2003년에 TDD가 단순한 설계를 장려하고 자신감을 불어넣어 준다고 말하였다. - 위키백과
왜 이런 방식을 권장하는 것일까? TDD의 장점을 알아보자.
이런 일을 미연에 방지해준다.
가장 큰 장점은 높은 퀄리티의 소프트웨어를 보장한다는 것이다. 그렇다면 높은 퀄리티의 소프트웨어는 어떻게 정의할 수 있을까? 참고한 포스트에서는 이를 아래와 같이 3가지로 정의했다.
TDD는 아래와 같은 특징 때문에 위 3가지 사항을 모두 만족시킨다.
TDD는 코드의 재사용성을 보장할 것을 명시하므로 TDD를 통한 소프트웨어 개발 시 기능별 철저한 모듈화가 이뤄진다. 이는 종속성과 의존성이 낮은 모듈로 조합된 소프트웨어 개발을 가능하게 하며 필요에 따라 모듈을 추가하거나 제거해도 소프트웨어 전체 구조에 영향을 미치지 않게된다.
TDD는 테스트 코드를 먼저 작성하기 때문에 내가 지금 무엇을 해야하는지 분명히 정의하고 개발을 시작하게 된다. 또한 테스트 시나리오를 작성하면서 다양한 예외상황에 대해 생각해 볼 수 있으며 이는 완성도 높은 설계로 이어진다. 따라서 매우 크리티컬한 예외상황이 생길 확률이 극히 낮으며, 개발진행 중 소프트웨어의 전반적인 설계가 변경되는 일을 방지할 수 있다.
이는 통합 테스팅이 아닌 유닛 테스팅을 하는 이점이기도 하다. 우리는 각각의 모듈 별로 테스트를 자동화 할 수 있는 코드가 없다면 특정 버그를 찾기 위해서 모든 레벨의 코드들을 살펴봐야 한다. 예를 들어 사용자의 데이터가 잘못 나온다면 DB의 문제인지, 비지니스 레이어의 문제인지 UI단의 문제인지 실제 모든 레이어들을 전부다 디버깅 해야하지만, TDD의 경우 자동화 된 유닛테스팅을 전재하므로 특정 버그를 손 쉽게 찾아낼 수 있다.
주로 SI 프로젝트를 진행하다 보면 어떤 요소들이 테스트 되었는지 테스트 정의서를 만들곤 한다. 이것은 단순 통합테스트 문서에 지나지 않는다. 즉, 내부적인 하나하나의 로직들이 어떻게 테스트 되었는지 제공할 수 없다. 하지만 TDD를 하게 될 경우 테스팅을 자동화 시킴과 동시에 보다 정확한 테스트 근거를 산출할 수 있다.
개발이 완료된 소프트웨어에 어떤 기능을 추가할 때 가장 우려되는 점은 해당 기능이 기존 코드들에 어떤 영향을 미칠지 모른다는 점이다. 따라서 단순한 기능이라도 추가 된 후에는 모든 기능들을 처음부터 테스트 해야한다. 하지만 TDD의 경우 자동화된 유닛 테스팅을 전재하므로 이러한 테스트 기간 역시 획기적으로 단축시킬 수 있다.
TDD는 위와같이 무수한 장점을 가지고 있지만 모두가 이 개발 프로세스를 따르지 않는다. 이유가 무엇일까?
가장 큰 단점은 바로 생산성의 저하다. 단순한 어플리케이션을 개발하는 경우 경험에 의해 어떤 예외상황이 발생할지 눈에 뻔히 보이는 경우가 많다. 특히 이러한 단발성 개발은 개발 기간이 타이트하게 잡히는 경우가 많은데, 이럴 때 TDD를 적용해 뻔한 테스트 코드를 작성하고 그에 통과하기 위한 코드를 작성한다면 마치 답을 알고 푸는 문제집처럼 따분하고 비효율 적일 것이다.
본 포스팅에 참고한 박경훈 님의 글에 의하면 아래와 같은 경우도 있다고 한다.
TDD로 프로젝트를 진행하면서 어려운 예외가 생길 수 있는데 그것 때문에 고민하는 순간이 찾아오게 된다. 원칙을 깰 수는 없고 꼼수가 있기는 한데 그 꼼수를 위해서 구조를 바꾸자니 이건 아무래도 아닌 것 같고, 테스트는 말 그대로 테스트일 뿐 실제 코드가 더 중요한 상황인데도 불구하고 테스트 원칙 때문에 쉽게 넘어가지 못하는 그런 경우다.
그럼 메이커준님이 소개한 간단한 프로그램으로 TDD를 적용하여 개발해보자
class Calculator() {
add(num1, num2) {
return num1 + num2;
}
minus(num1, num2) {
return num1 - num2;
}
multiply(num1, num2) {
return num1 * num2;
}
divide(num1, num2) {
return num1 / num2;
}
}
const calculator = new Calculator();
console.log(calculator.add(1,2);
console.log(calculator.minus(1,2);
console.log(calculator.multiply(1,2);
console.log(calculator.divide(1,2);
TDD는 이런식으로 진행된다고 설명해주셨다. 나는 개인적으로 애자일 방법론에서 설명하는 것과 비슷하다고 느꼈다.
핵심은 큰프로그램을 한번에 만들려고 하는 것이 아닌 기능을 테스트하며 점진적으로 업그레이드 하는 것이 중요하다고 생각된다.
ex) 자동차를 만들 때
처음부터 자동차의 바퀴, 문, 엔진등을 만드는 것이 아닌,
자전거 -> 오토바이 -> 자동차의 순서로 기능을 업그레이드 하면서 만드는 식
https://gmlwjd9405.github.io/2018/06/03/agile-tdd.html
https://ko.wikipedia.org/wiki/테스트_주도_개발
https://manorgass.tistory.com/63