개발 공부를 시작한 이래로, 여러 개발 방법론을 듣고 읽고 사용해 보고자 노력했다. 최근에는 클린 코드 작성과 더불어 TDD에 대해서 공부를 진행하고 있다.
TDD에 관한 자료를 찾아보면 2014년도부터 2022년까지 꾸준히 자료가 올라옴을 확인할 수 있다. 그리고 대부분의 자료는 TDD에 관한 찬반 논쟁 자료다. 말 그대로, 사람들은 TDD가 좋다 나쁘다로 거즘 10년이 되는 시간 동안 싸우고 있다. TDD는 개발 방법론 중 하나이기 때문에, 당연히 이를 사용하고 말고는 개발자 마음이다. 나 역시 TDD를 공부해야 하는 이유에 대해 기술할 예정이지만, TDD만이 절대 지존이며, 반드시 TDD만 써야 한다라고는 말하지 않을 것이다. 다만, TDD를 공부하면서 느꼈던 'TDD를 사용한 개발 방법이 주는 이점'에 대해 이야기해보고자 한다.
TDD는 왜 공부해야할까. 당연히 TDD를 공부하고 사용하면 좋은 점이 있으니까가 그 이유다. 처음 TDD를 공부하기 전에는, '그래서 그 좋은 점이 뭔데 쓰라는 거야' 하는 생각이 나 역시 가장 먼저 들었다. 테스트를 먼저 만들고 개발을 진행하면 괜히 시간만 더 드는 것 같고. 도메인 기능과 비즈니스 로직을 짠 뒤에 테스트를 만들고 리팩토링을 진행해도 늦지 않을 것 같은 생각이 들었다. 그러나 최근 내 생각은 조금 달라졌다.
TDD로 인해 늘어나는 설계 시간은 오히려 전체 개발 시간을 줄여준다.
테스트는 도메인 기능과 다른 패키지 단위에서 실행된다. 따라서 테스트를 구현하기 위해서 우리는 'public한 기능을 구현해야 한다.' 그리고 public한 기능은 프로그램의 핵심적인 비즈니스 로직을 담당한다. 따라서, '어떤 기능이 핵심적인 비즈니스 로직을 담당하는지'를 다시 한번 더 고민해 볼 수 있는 시간이 주어진다. 테스트를 먼저 제작하지 않고, 자신이 분석한 요구 사항을 바탕으로 순차적으로 개발을 진행하다 보면 필요한 핵심 로직을 놓칠 수도 있고, 프로그램의 핵심적인 기능이 아님에도 불필요하게 public한 기능을 제작하는 일이 발생할 수도 있다. TDD 방법으로 개발을 진행하면, 이러한 실수를 줄일 수 있다.
또한 테스트를 먼저 만들면 '어떤 객체가 필요한지 판단할 수 있다.' 당연한 말이지만, 테스트는 객체의 기능을 테스트한다. 설계 단계에서 필요하다고 생각했던 객체가, 사실은 아무 기능도 하지 않을 수도 있다. 물론 데이터 클래스로서 존재하는 클래스도 있을 수 있지만, 그런 클래스들은 그냥 뚝딱 만들기만 하면 된다. 그러나 핵심적인 기능을 담당하는 클래스는 그렇지 않다. 위의 이유와 일맥상통하는데, '핵심적인 비즈니스 로직을 담당하는 것은 당연하게도 핵심 객체다'. 이전 글에서 이야기했던 사용자의 이름을 바꾸는 경우를 예시로 들어보자.
사용자의 이름을 변경하는 기능을 테스트하려는 테스트 메서드를 작성할 때, 우리는 User가 자체적으로 이름을 변경하게 테스트 로직을 짤 수도 있고, UserModifier에서 User를 받아 이름을 변경하게 테스트 로직을 짤 수도 있다. 이렇게 테스트 메서드의 로직을 짜는 과정을 거치며 우리는 불필요한 객체에 대한 생략을 할 수도 있고, 필요한 객체를 더 만들 수도 있다. 즉, 설계에 대한 고민을 다시 한번 진행해 더욱 적합한 객체 설계에 다가설 수 있다. 이렇게 설계된 프로젝트는 그렇지 않은 프로젝트보다 더 적은 수정을 요할 것이다.
다시 말해, 우리는 TDD를 통해 '결합도가 낮은 설계'를 할 수 있다. 하나의 기능을 테스트하기 위해서 필요한 객체가 여러 개가 된다고 하면, 우리는 기능을 수행하는 핵심 객체의 역할과 책임이 적절히 배분되었는지를 재차 고민해봐야 한다. 정말 특수한 경우가 아니고서야, 하나의 기능을 위해 여러 객체가 달려드는 일은 없을 것이다. 테스트를 먼저 진행하면서, 우리는 우리의 설계의 결합도를 확인해 볼 수 있다. 그리고 당연히 결합도가 줄어든 설계는 문제가 발생했을 때, 수정해야 할 부분도 줄어든다.
마지막으로, 테스트를 먼저 만들면 '리팩토링이 쉽다.' 어떤 프로젝트를 하던, 처음 제작한 프로토 타입은 부족한 부분이 있기 마련이다. 개발의 신이 직접 개발을 진행하는 것이 아닌 이상, 부족한 부분은 아마 한두 군데가 아닐 것이다. 그런 문제를 우리는 리팩토링을 통해 해결해 간다.
문제는, '리팩토링을 어디서부터 하느냐'이다. 에러는 언제 어느 곳에서 던 터질 수 있다. 부가적인 기능을 추가하다가 로직이 꼬일 수도 있고, 하다 못해 리팩토링을 진행하다가도 로직이 꼬일 수 있다. 만일 모든 시스템을 구성하고 프로그램을 돌렸을 때 에러가 발생한다면, 도대체 어디서 뭐가 잘못되었는지를 파악하기가 쉽지 않다. 그러나 테스트를 먼저 만들고 부가적인 기능을 추가하면서, 또는 리팩토링을 진행하면서 동시에 테스트를 겸한다면, '어느 단계에서 문제가 생겨서 에러가 발생하는지를 쉽게 파악할 수 있다.' 우리는 해당 단계에서 발생한 문제만 수정해 주면 된다. 이러한 이유 때문에 TDD를 '심리적 안정감을 얻을 수 있는 개발 방법'이라고 부르기도 한다. 내가 짠 로직이 잘 돌아가는지를 확인해 주는 테스트가 있기 때문에, 우리는 마음 편하게 리팩토링과 기능의 추가를 진행할 수 있다.
나는 여전히 큰 단위의 프로젝트를 진행해 본 적도 없고, 개발 경험이 많지도 않은 나부랭이다. 당연히 다른 개발 방법론들에 대해서도 공부하고 적용해봐야 할 것들이 많다. 그럼에도 최근 TDD를 중점적으로 파악해 봐야겠다고 생각한 것은 TDD가 '개발의 핵심과 관련된 문제들을 줄이는데 큰 도움이 된다'라고 생각하기 때문이다. 결국 개발자는 코딩을 하는 사람이 아니다. 물론 코딩은 당연히 하겠지만, '코딩은 누구나 다 할 줄 안다. 우리는 생각하는 개발자가 되어야 한다.' 그리고 그 생각은 어떤 코드가 좋은 코드인가, 어떻게 설계하는 것이 좋은 설계인가에 대한 생각이 되어야 한다. TDD는 그런 부분을 집어준다.
그래서 TDD는 왜 공부해야 하는가. 'TDD는 우리가 생각하게 만들기 때문이다.'
'Swimming > Think' 카테고리의 다른 글
죽이되든 밥이되든 (0) | 2023.04.20 |
---|---|
스트림은 왜 사용해야 하는가. (1) | 2023.03.08 |
생성자 오버로딩은 왜 사용하면 안되는가. (2) | 2023.02.28 |
'왜'는 왜 생각해야 하는가. (1) | 2023.02.27 |
객체에 관한 고민 - 책임과 기능은 어디까지인가. (1) | 2023.02.25 |
댓글