Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

TDD를 한달여정도 해보고 나서....

저는 애자일 방법론이 이야기하는 사상과 방법론들을 좋아하는 편인데(누군들...) 그중에서 TDD(Test-Driven Development)를 꽤 좋아합니다. 관련된 책도 보고 여러가지 정보들을 보게 되면서 많은 애자일 방법론 중에서 가장 실용적이면서도 개인이 실천하면서  그 이득을 거의 즉각적으로 얻을 수 있는 방법론이라고 생각하기 때문입니다. 그리고 개발자의 목표인 프로그램의 품질은 높이고 안정성을 확보할 수 있는 방법으로 제가 아는한 가장 현실적이지 않나 생각합니다.

하지만 그렇다고 딱히 TDD를 해보지 않았습니다. TDD라는 것은 물론 간단히는 테스트를 먼저 작성한다는 기본 개념을 가지고 있지만 제대로 이득을 취하려면 여러가지 관련된 것들이 필요합니다. 일반적으로 JUnit으로 대표되는 xUnit같은 도구들이 이런 것을 위한 것이고 그 외에도 CI서버라든가 리펙토링등에서 테스트를 만들었을 때의 이점을 지속적으로 느낄수 있는 도구들이 많이 있습니다. 물론 Java같은 경우 main 메서드로도 테스트를 작성할 수 있지만 핵심 로직을 테스트하기 위해서 main메서드를 사용하면 모를까 프로젝트를 TDD로 하면서 main메서드로만 한다는 것은 쉽지 않은 일일것입니다.

앞에서 말했듯이 TDD를 좋아하고 있음에도 이전에는 TDD를 할 수 있는 환경에 있지 않았기 때문에 따로 공부만 했었지 실제로 적용해 보지 못했습니다. 물론 환경에 대한 핑계를 대려는 것은 아니지만 어쨌든 그동안은 할 수 없었기에 좋아하는 방법론이라면서 경험이 없다는 건 저에게는 항상 신경쓰이는 부분이었습니다. 자주 쓰는 JavaScript에서 좀 적용해보려고 했지만 JavaScript같은 경우는 UI랑 밀접하게 물려있고 접근자체를 약간 다르게 해야했고 관련 자료도 많지 않았기 때문에 TDD경험이 많지 않은 저로써는 번번히 시도만하고 실패를 하였습니다.

어쨌든 현재 다시 Java를 하게 되었고 본격적인 업무를 수행하게 되면서 업무개발에 TDD를 적용했습니다. 사실 두려움은 좀 있었습니다. 정말 오랜만에 업무로 자바를 하는 것인데다가 업무파악도 안되어 있고 일정은 있는 상태에서 처음 사용하는 방법론을 적용해 보는 것이 과연 괜찮을까 하는 생각이었지만 TDD에 대한 어느정도의 신뢰가 있었고 TDD는 안정성과 품질을 위해서 별도로 투자해야 하는 것이 아닌 개발방법론이라고 생각하고 있었고  할수 있는 환경이 되었는데 안할 이유는 없었기에 그대로 TDD로 개발업무를 시작했습니다. 이제 한 한달반정도를 해보았고... 나중에는 또 다른 생각이 들지도 모르지만 중간 로그차원에서 남깁니다.





좋은 점
개발 속도가 느려진다는 느낌은 전혀 없었습니다. 앞에 말한대로 업무도 파악안된 상태에서 처음 적용을 해보는 것이지만 테스트를 만들기 때문에 개발하는 속도가 줄어든다는 느낌은 전혀 없었습니다. TDD를 포함하여 보통 유닛테스트를 잘 하지 않는 이유는 바쁘고 일정이 촉박해서 라는 이유가 가장 많은 것으로 알고 있는데 처음 해보는 거라 어느정도 삽질로 인해서 낭비되는 시간을 감안하고 있었음에도 훨씬 자연스럽게 진행이 되었습니다.

테스트를 먼저 작성하다 보니 무엇을 구현해야 하는지를 생각해야하다보니 개발하고자 하는 의도가 명확해 져서 프로젝트에 늦게 투입해서 전체에 대한 그림을 다 그리지 못했음에도 차근차근 이해하면서 개발할 수 있었습니다. 로직과 테스트를 동시에 생각하고 만들어내니 따로 유닛테스트를 작성하는 것보다는 훨씬 빠른 느낌이었습니다. 오히려 코드를 먼저 작성했다면 귀찮아서 유닛테스트는 작성하지 못했고 추후에 테스트할 로직을 다시 생각해야하기에 시간은 오히려 더 들었을 것입니다.

간간히 생각지 못한 타이밍에 나타나는 fail의 빨간 막대는 스트레스라기 보다는 안심을 하게 해주었습니다. 로직을 추가하고 작성하는 가운데 생각지 못한 테스트케이스가 깨지는 경우를 종종 보면서 제가 고려못한 부분을 테스트가 커버해주고 있다는 안도감을 느낄수 있었기에(완벽히 커버는 못해주겠지만) 더욱 테스트케이스를 열심히 만들수 있었고 로직변경에도 다른 부분에 대한 많은 걱정없이 작업할 수 있었습니다.

리팩토링을 무척 맘편히 할 수 있었습니다. 이전의 경험을 생각하면 간단한 코드 수정은 물론이고 구조자체를 바꿔버리는 리펙토링을 하게 되면 과연 제대로 돌아갈 것인가하는 걱정이 있었고 간단한 테스트를 해보아서 잘 돌아가서 왠지 다른데서 문제가 생길 것 같아서 더 걱정이 되기 때문에 많은 량의 리펙토링에 좀 소극적이 될 수 밖에 없었는데 전체 로직에 걸쳐서 테스트케이스가 만들어져 있다보니 (전혀는 아니지만) 훨씬 안심하고 리펙토링을 할 수 있었습니다. 구조가 맘에 안들때 좋은 구조가 생각나면 언제든지 리팩토링을 할 수 있었습니다.

Given / When / Then 템플릿은 무척이나 유용했습니다. 그냥 막 짜다가 중간부터 쓰기 시작했는데 doortts님의 추천대로 given - when - then으로 테스트를 작성하는 것은 아무런 기능은 없는 주석으로 구분해 주는 템플릿에 불과하지만 깜짝 놀랄정도로 테스트가 구조화 되고 읽기 쉬워졌습니다. 아주 좋더군요.





어려운 점
TDD를 한다고 나쁜 냄새가 잘 맡아지거나 디자인이 좋아지는 것은 아닙니다. 물론 당연한 애기지만 TDD를 한다고 이런 것들도 자동으로 얻어지지는 않았습니다. 역시 많은 경험과 지식이 필요할 것 같습니다. TDD에서 테스트가 어렵다는 것은 디자인이 좋지 않다는 징조로 볼 수 있는데 저 같은 경우는 이것이 디자인이 좋지 않은 것인지 제가 테스트케이스 작성이 미숙한 것인지 판단하기가 어려웠습니다.

테스트한다는 것은 생각보다 어려웠습니다. 처음에 시작할때는 어디서 부터 시작할지도 막막했는데 약간씩 해보다 보니 조금씩 감이 오는것 같았습니다. 아무래도 UI가 물리기 시작하면 테스트가 더 어렵기 때문에 처음에는 비즈니스로직을 작성하는 부분부터 하는 것이 좋은 것 같습니다. 어떻게 테스트해야하는 지가 꽤 어려운 부분인데 어떤 동작을 만들것인가를 생각하니까 그나마 나은 것 같습니다.

의존성이 있는 부분에 대한 테스트는 역시나 어려웠습니다. 가장 큰게 디비였는데 Mock을 연결해야 더 깔끔한 테스트를 할 수 있는데 테스트도 익숙치 않은데 Mock에 대한 부분까지 익혀서 하는 것은 시간상으로도 여유가 없었기 때문에 그냥 테스트후 트랜잭션 롤백을 하는 것으로 해서 디비를 직접 물고 테스트를 작성하였습니다.

갈수록 엉망이 되어가는 픽스쳐. 픽스쳐는 테스트를 위한 기본적 데이터인데 연습할때는 괜찮았지만 실제 업무에서 사용하다 보니 엄청나게 다양한 픽스쳐가 필요했습니다. 일반적으로 픽스쳐는 @Before에서 처리를 하는데 테스트를 만들다 보니 전체적으로 공통인 픽스쳐가 그리 많이 존재하지 않았고(제가 그렇게 구조화 하기가 어려운 것일수도) 결국 거의 모든 테스트 케이스마다 픽스처를 잡아주면서 테스트를 작성해야 했습니다.일부 공통적인 부분은 따로 메서드로 추출했지만 @Before는 시간이 지나면서 픽스처설정 용으로는 거의 쓰지 않게 되었고 픽스쳐 설정으로 인해서 테스트는 길어지고 복잡해 졌습니다.

테스트에 대한 관리는 고민을 해봐야 될 것 같습니다. 클래스에는 여러가지 메서드들이 있고 한 메서드에 대해서 어려가지 조건으로 테스트를 하다보니 시간이 지나면서 테스트 클래스에는 엄청나게 많은 테스트케이스들이 생기게 되었는데 이렇게 되다보니 어느 테스트가 어느 메서드와 관련되었는지도 찾기가 힘들었고 기능추가나 로직의 변경을 하면서 어느 부분이 테스트되고 안되었는지 정확히 파악하기가 어려웠습니다.(간단히는 커버리지툴 돌리면 되기는 하지만 논리적인 부분에서...)

리팩토링중에 테스트케이스가 만들어져야 하는 상황이 되었을때 약간 난감합니다. 예를 들어 테스트를 작성하고 로직을 작성한 후 특정 부분을 메서드 추출을 해서 private로 메서드로 만들면 보통 테스트를 작성하지 않습니다. 근데 여기서 작성을 하다보니 이 private메서드가 이 클래스가 담당해야 할 부분이 아닌 것으로 판단되어서 다른 클래스를 만들어서 위치를 이동하면서 public으로 만들어졌습니다. 그럼 여전히 이전 테스트가 옮겨진 public메서드의 테스트까지 포함하고 있지만 public메서드인데 별도의 테스트가 존재하지 않으니 뭔가 좀 찜찜합니다. doortts님의 조언으로는 public으로 나올때는 테스트를 만드는게 원칙이이라고 했는데 중복테스트를 복사해서 만들게 되는것 같아서 실용성에 대해서 고민을 해봐야 할것 같습니다.





어쨌든 처음부터 호감이 있어서 그런지는 몰라도 TDD에 생각보다 순조롭게 적응을 하고 있어서 만족하고 있고 생각하던 이점들을 실제로 느끼게 되서 더욱 맘에 들었는데 이제 TDD를 습관화 해야겠습니다. ㅎ
2011/07/20 01:59 2011/07/20 01:59