TDD

내가 생각하는 TDD를 하는 이유

Posted by Pando on June 19, 2022

TDD는 왜 해야할까?

예전에 스터디로 <파이썬을 이용한 클린 코드를 위한 테스트 주도 개발>을 진행한 적이 있습니다.

당시에는 테스트를 짤 줄도 몰라서 책을 한 줄 한 줄씩 오랜 시간에 걸쳐 완성을 했지만 정작 그 의미가 무엇인지 모르고 일단 했었습니다.

그래서 블로그에 해당 내용을 올려보겠어!라며 Intro를 작성하고 어언 4년이 지났습니다;

그 사이에 회사를 다니면서 깊이 이해하진 않아도 TDD 대화의 맥락은 이해하는 정도로만 지냈습니다.

그러다 최근에 최범균님의 <테스트 주도 개발 시작하기>를 읽으면서 내용보다도 TDD를 왜 하는지에 대한 제 생각을 정리해보려고 합니다.

TDD는 Test-Driven Development의 약자로써, 테스트 주도 개발 방법론이라는 뜻입니다.

TDD없이 개발을 하는 경우

우리는 어떤 프로젝트를 진행할 때, 어떤 업무를 해야하는지 분담하고, 분담한 업무를 다시 우선순위를 기준으로 작업들을 나눕니다.
그리고 개발자는 받은 작업들을 순차적으로 코드로 작성합니다.

만약, 제가 어떤 기능을 만든다고 가정해보겠습니다.
기존에는 기능을 모두 구현하고, 생각하는 문제에 대해 테스트를 짜기 시작했습니다.
그러면 순서는 다음과 같습니다.

  • 기능 구현
  • 실제로 동작하는지 코드 수행
  • 테스트 케이스1 구현
  • 테스트 케이스2 구현
  • 테스트 케이스n 구현

평소에는 문제가 없을 수 있습니다. 그러나 다음과 같은 상황을 가정해보겠습니다.
맡은 작업의 사이즈가 커서 기능 구현이 영업일 기준 하루 이상으로 넘어갑니다.
그러다보면 머릿속으로 이리저리 생각해서 코드를 구현합니다. 그리고 하루 안에 완료가 되지않은 경우 다음 날로 넘기기도 합니다.
그렇게 기능이 어느정도까지 구현되었는지는 보장할 수 없습니다.

다음 날 회의에서 그렇게 말합니다.
“(아직 기능을 동작시켜보진 않았지만 작성한 정도를 보면..) 약 반 정도 했습니다.”

오늘은 해당 기능을 마무리해야겠다고 생각하고 작업에 들어가려는 찰나, 다음과 같은 상황이 발생합니다. “버그가 발생했습니다. 해결해주세요.”

해당 작업을 완료하기 전에 우선순위가 높은 작업이 치고 들어옵니다.
오늘 완성하기로 했던 작업은 일단 커밋을 해두고, 급한 불부터 끕니다.

어찌저찌 그날 해결을 하고 다시 작업을 하러 돌아왔습니다.
작업했던 코드를 보며, 분명 어떤 코드를 작성해야했는데 잘 떠오르진 않고 이전에 작업한 코드를 읽으면서 다시 작성을 합니다.

그리고 작업은 불행히도 지연됩니다.
급한 작업이 들어온게 문제이긴 합니다. 하지만 실무에서는 그런 일이 다반사이긴 합니다.

그 부분은 예측할 수도 안할 수도 없는 작업입니다.
그러나, 다시 돌아와서 작업을 할 때에는 내 머릿속에 저장됐던 기억은 휘발되어 다시 몰입을 하기가 어렵습니다.

TDD로 개발을 하는 경우

이 경우에 TDD를 적용하면 어떨까요?
원래 구현하기로 한 기능의 기댓값 부터 테스트로 작성합니다.

그리고 그 테스트가 동작할 수 있는 빈 껍데기 클래스를 구현합니다.
그리고 거기서 예상할 수 있는 에지 케이스부터 테스트를 구현합니다.
그리고 에지 케이스가 나올 수 있도록 코드를 구현합니다.
그러다 중복되는 로직은 리팩토링을 합니다.
하나씩 케이스를 정복해나가며 기능 구현을 완성합니다.
이러면 순서는 다음과 같습니다.

  • 원하는 케이스1에 대한 테스트 구현
  • 해당 테스트가 통과할 수 있도록 케이스1에 대한 로직을 구현
  • 원하는 케이스2에 대한 테스트 구현
  • 해당 테스트가 통과할 수 있도록 케이스2에 대한 로직을 구현
  • 중복되는 로직을 발견하여 리팩토링
  • 원하는 케이스n에 대한 테스트 구현
  • 해당 테스트가 통과할 수 있도록 케이스n에 대한 로직을 구현 (완성)
  • 실제로 동작하는지 코드 수행

그렇게 달라진 건, 없는 것 같습니다. 테스트가 먼저 짜여졌고 로직을 짜는 순서만 바뀐 것 같습니다.
그러나 TDD에서는 다음과 같은 말을 합니다.

테스트가 있는 부분만 프로그램이 완성되었다고 보장할 수 있다.

테스트가 없는 프로그램은 그 기능이 동작한다는 것을 보장하기 위해서 취할 수 있는 방법은 직접 실행해보는 수 밖에 없습니다.

그러나 매번 사람이 일일이 그 기능을 수행해보는 것을 매우 비효율적인 작업입니다.
테스트는 그 기능이 동작함을 보장해주는 코드로써 논리적으로 기능이 동작하는 것을 보장할 수 있는 유일한 수단입니다.
이렇게 되면 어떨까요?

다시 상황을 가정해보겠습니다.

TDD를 하는 것만 다르고, 작업 도중 똑같이 버그가 터졌고 그 버그를 해결하고 난 뒤에 다시 돌아와서 작업을 하는 상황입니다.
기존에는 어디까지 작업이 되었는지 아직 동작시켜보지못한 코드와 자신의 기억이었지만
테스트 코드를 수행해보고, 어떤 케이스까지 테스트가 작성되어있는지 확인하고 그 다음 케이스부터 작성을 할 수 있습니다.
와우 놀랍지 않나요?

이제는 어디까지 코드가 완료되었는지 보장할 수 있는게 내 기억이 아닌 테스트 코드입니다.
그러므로 현실에 코드 작성도 이어하기가 조금 더 수월할 수 있죠.

TDD를 하는 이유: 사람보단 코드로 기억하기

코드를 짜는 건, 개발자 즉 사람이기때문에 버그 이외에도 삶에 대한 다양한 일로 인해 생각의 흐름이 끊길 수 있고 그 경우 완료되지않은 작업은 보장하지 못하는 경우가 자주 발생합니다.

이는 많은 게임에서 하는 Save 기능과 비슷합니다.

일정량의 기능에 대해서 구현을 끝내고 테스트를 짜는 건, 켠김에 왕까지와 같이 한번도 게임을 종료하지않은 채로 프로그래밍을 하는 것과 동일 합니다.

그렇게 내 기억에 의존하다 까먹기라도 하면 그 기억을 다시 살리는 건 매우 어려운 일이죠.

그러나 일정 기능에 대해서 테스트를 짜는 건, 게임 도중 저장을 하는 것과 같을 수 있습니다.

save.png

이렇게 저장을 하면, 다른 일을 하고 와도 다시 돌아올 수 있죠.

to_be_continue.jpeg

지금까지의 작업을 저장을 할 수 있다는 것은 생각의 크기를 줄일 수 있다는 말이기도 합니다.

우리는 한번에 많은 생각을 하고 있으면 실수를 할 확률이 높아집니다.
그래서 한번에 하나씩 해결하는게 효율적인 방법입니다.

그러면 기능을 한번에 구현하는 것이 아닌 일정 부분만 동작하는 걸 보장하면서 짜는게 훨씬 생각의 크기를 줄일 수 있습니다.

정리하자면 TDD를 하는 큰 이유는

  1. 실제 삶에서 프로그래밍 도중 기능이 완성되지않더라도 Save를 내 기억이 아닌 테스트 코드로 할 수 있다.
  2. 큰 기능을 작게 쪼개서 몰입하며 진행할 수 있다.

그러면 다음과 같이 당당히 말할 수 있습니다.
“얼마나 구현되었나요?”
“(생각한 케이스가 10개인데 그 중 5개 정도 되었으니..) 50% 정도 완료되었습니다!”

이 외에도 TDD의 장점은 다양합니다.
TDD를 하는 본인이 왜 사용해야하는지에 대한 동기가 있으면 자연스럽게 적용하지않을까요?