Testing & Refactoring

필자는 개발을 시작하고 Testing에 대해서 꾸준히 들어왔다. 관련 세미나에 참석해서 내용을 들어 보기도 하고 실무 혹은 사이드 프로젝트를 통해서 적용해 보려고 여러번 시도했다. 시도 할때 마다 느꼈던 점은 어떤 식으로 테스트 코드 작성을 시작해야 될지 막막 했고 작성 하다가도 일정 상의 이유 혹은 관리의 어려움 등의 문제로 좌절 했던 기억이 있다. 더 이상 미루기만 할 수 없다고 생각해서 이번 기회에 테스팅과 리팩토링에 대해서 정리하고 연습 해보려고 한다.

개발자들은 항상 새로운 프로젝트를 시작할 때 여러가지 고민을 하게 된다.

  • 어떤 기술을 적용할 것인지
  • 지난 프로젝트에서 아쉬웠던 점은 어떤 것들이 있는지
  • 좀 더 좋은 설계를 하려면 어떻게 해야 되는지

하지만 실제로 프로젝트를 시작하면 내가 생각했던 이상적인 방향으로만 흘러가지는 않는다. 요구사항은 자주 변경되고 처음에 작성한 구조는 내일의 내가 봤을때 만족하지 못한다. 이건 개발자의 기량에 상관없이 계속해서 하게되는 고민일지도 모른다. 그렇다면 여기에 대한 해결책은 없을까? 해결책은 있다. 누구나 한 번에 모든 변경사항에 대처해서 코드를 작성할 수 없다면 지속적으로 개선해 나가면 된다.

즉, 리팩토링이 필요한 시점이다.

🛠 리팩토링(Refactoring)
외부 동작을 변경시키지 않으면서 내부 구조를 개선해 나가는 방법

리팩토링 이란 위 정의와 같이 동작에 영향을 주지 않으면서 내부 구조를 개선 해나가는 방법이다. 리팩토링이 어려운 이유는 "두려움" 에 영향이 있을 거라고 생각한다. 현재 잘 동작하고 있는 어플리케이션 의 구조를 바꾸고 정상적으로 동작할까? 버그가 생기진 않을까? 와 같은 생각이 들게 된다. 모든 개발자들이 해당되지 않을 수 있다. 필자의 경우 위와 같은 이유로 리팩토링 을 뒤로 미뤘던 경험이 있다.

지속적으로 개발한 코드를 개선하고 발전시켜 나가려면 리팩토링의 "두려움"을 극복할 수 있는 방법이 필요하다. 이에 대한 효율적인 방법은 "자동화된 테스트" 이다. 단위 테스트를 작성하고 이를 자동화 시켜 놓으면 코드를 개선해 나갈때 마다 내가 작성해 놓은 테스트 케이스 들이 어느 부분에 영향을 주고 있고 어떤 부분이 실패하고 있는지 피드백을 줄 것이다. 이런 피드백을 통해서 코드를 안정적으로 또 점진적으로 개선해 나갈 수 있다.

위 그래프는 시간에 따른 변경 비용을 그래프로 나타낸 것이다. 테스트 코드를 작성한 경우엔 시간이 지나도 변경에 대한 비용이 일정하게 유지가 되는데 테스트 코드가 없는 경우엔 시간이 지날수록 변경에 대한 비용이 지속적으로 증가하는 것을 알 수 있다.

테스트 주도 개발(Test-Driven-Development)

처음 TDD라는 방법론을 듣고 접했을 땐 단위 테스트를 작성하는 것이 곧 TDD를 하는 것과 동일하다고 잘못 인지를 한 적이 있다. TDD 즉 테스트 주도 개발은 말 그대로 테스트를 먼저 작성하는 것이다. 일반적으로 개발을 할땐 바로 실제 코드를 작성하게 되지만 TDD에서는 테스트 코드를 먼저 작성하는 것을 원칙으로 한다.

전체적인 사이클을 살펴보면 위 그림과 같다.

  • 실패하는 테스트 케이스를 작성
  • 최대한 빠르게 테스트 케이스를 통과
  • 리팩토링

먼저 실패하는 테스트 케이스를 작성한다. 이 테스트 케이스는 패스는 물론 컴파일도 제대로 되지 않을 것이다. 다음은 최대한 빨리 테스트 케이스를 통과시킨다. 이 과정에서는 작성이 되지 않은 클래스를 작성하고 임시로 예상 값과 동일하게 반환을 해서 통과만 시켜주면 된다. 마지막으로 모든 편법을 동원해서 통과시킨 실제 코드를 리팩토링 한다. 리팩토링을 하는 도중에 중간중간 테스트를 실행시켜주면 리팩토링 한 코드가 제대로 동작 하는지에 대한 피드백을 줄 것이다.

TDD를 해서 얻는 이점

  • 개발의 라이프 사이클을 많이 단축 시켜준다.
  • 테스트 코드를 통해 프로그램 Spec이 명확해 진다.
  • 리팩토링을 할때 안정감을 준다.

테스트 주도 개발을 하게 되면 위와 같은 이점을 얻을 수 있다. 프로그램을 좀 더 작은 단위로 분리해서 테스트를 진행하기 때문에 라이프 사이클이 단축된다. 예를 들어 백엔드를 개발하고 있다고 가정 해보면 어떤 로직을 테스트하기 위해서 웹 서버를 구동하고 브라우저를 통해 요청을 해서 확인을 하게 되는데 단위 테스틀 이용하면 이러한 과정을 거치지 않고 주요 로직을 검증 할 수 있다. 또한 테스트를 먼저 작성한다는 의미는 곧 프로그램이 어떻게 동작 하는지에 대한 Spec에 대해서 작성한다고 봐도 무방하다. 어떤 Input이 들어 왔을 때 어떤 Output이 기대 되는지 명확히 알 수 있다. 마지막으로 위에서 다뤘던 리팩토링에 대한 “두려움” 을 많이 해소 해준다. 테스트 코드를 먼저 작성하고 통과하도록 변경한 뒤 작성된 테스트에 피드백을 받으면서 점진적으로 코드를 개선해 나갈 수 있다.

TDD가 어려운 점

  • 개발 해왔던 방식을 바꾸는 과정에서 익숙 해지는데 시간이 필요
  • 테스트 코드 관리

테스트를 먼저 작성한다는 것은 기존에 개발 해왔던 방식과 많이 다르고 익숙하지 않다. 테스트 코드를 처음 작성할 땐 실제 코드보다 테스트 해야 될 항목을 인지하는 부분과 테스트 코드를 작성하는 부분에 더 많은 시간을 할애하게 될 수 있다. 그리고 테스트 코드 역시 실제 코드와 마찬가지로 리팩토링 해야 되고 관리해야 할 대상이 된다. 따라서 이런 부분은 충분한 연습을 통해서 익숙해진 뒤 실무 코드에 적용 해보는 것이 좋을 것 같다. 충분히 익숙해진 상태라면 테스트 코드를 작성하는 것 자체가 생산성을 떨어뜨릴 것 같지는 않다.

마무리

  • 리팩토링을 하기전에 반드시 테스트 코드를 작성하자
  • 처음 부터 TDD를 적용하기 어렵다면 단위 테스트를 작성하는 걸로 시작하자.
  • 단위 테스트를 작성하는 많은 연습이 필요한 것 같다.

Ref