일반적인 개발 프로세스
지금까지 만들던 기능 개발은 기능이 어떤 것인지 추출하고, 이를 우선 구현부터 했습니다. 테스트가 필요하다면 테스트 코드는 작성했고 테스트가 통과하지 못한다면 다시 코드를 수정하거나 테스트 코드를 수정하기도 했었죠. 문제는 바로 '우선 돌아가는 코드를 작성' 하는 부분이었습니다. 분리해야 할 기능들이 한 메서드에 모여있고 강한 결합도를 가지며 점점 테스트하기 힘든 코드가 되었던 것이죠. 결과적으로 테스트 코드 작성이 어려운 수준까지 코드 품질이 떨어졌고 테스트 코드 작성보다는 직접 서버를 띄운 후 하나하나 테스트를 해야 했습니다.
이렇게 개발을 하던 도중, 인프런에서 강의하시는 백기선님의 개발 스타일을 보고 꽤 감명받은 기억이 납니다. 강의에서는 원하는 기능에 대해 테스트 코드를 먼저 작성하고 메서드가 존재하지 않아 컴파일조차 되지 않는 상황부터 개발을 시작했습니다. 어떤 메서드가 필요한지, 어떤 기능이 필요한지 테스트 코드를 먼저 작성하며 추출하였고 그래서 실제로 코드를 작성할 때는 이런 테스트를 통과하는 것을 목표로 두고 코딩을 했습니다. 코드는 짧고 간결했고 최소한의 코드만 남아있었습니다. 실제로 어떤 기능을 개발해야 하는지 명확하게 보였습니다.
테스트는 왜 만들어야 하나요?
테스트는 그 자체만으로 기능에 대한 명세가 될 수 있습니다. 프로그래머가 어떤 기능을 만들었고 이 코드가 올바르게 동작하는 것을 확인할 수 있도록 도와줍니다. 테스트가 기능을 명세하고 있기 때문에 다른 사람이 이 코드를 보고 이해하는데도 큰 도움을 줄 수 있습니다. 어떤 입력값이 있을 때 어떤 반환값을 주는지, 어떤 예외를 던지는지 등을 파악하여 코드의 활용성을 높일 수 있습니다.
만약 프로그램의 요구사항이 변경되었다면 어떻게 될까요? 변경된 기능에 관련된 코드만을 수정하고, 그 변경된 부분만 테스트하면 될까요? 아쉽게도 코드의 변경은 어떤 사이드 이펙트를 가지고 올지 바로 파악하기 어렵습니다. 코드의 변경이 있다면 다른 버그가 발생하지 않는지 파악하기 어렵다는 이야기죠. 이럴 때 미리 작성해둔 테스트코드가 있다면 변경되지 않은 기능에 대해서 빠르게 테스트할 수 있습니다. 프로그램을 켜고, 프로그램이 가지고 있는 기능을 하나하나 직접 테스트 하는 것이 아닌 이미 작성된 테스트코드를 실행시키기만 하면 됩니다.
자신이 작성하지 않은 기능을 공부할 때도 테스트코드 작성이 도움이 됩니다. 테스트 코드를 작성하는 과정에서 이 기능이 어떤 요구 조건을 가지고 있고 어떤 결과를 주는지, 어떤 동작을 가지고 있는지를 유심히 살피게 됩니다. 다른 사람이 작성한 기능, 코드를 익히고 싶을 때 테스트코드를 작성하는 것은 효과적인 방법입니다.
위 내용을 정리한 결과는 아래와 같습니다.
- 작성한 코드가 어떤 기능을 가지고 있는지 명세할 수 있습니다.
- 시스템에 작성한 코드를 문서화하여 다른 사람이 이 코드를 이해하는데 도움을 줄 수 있습니다.
- 코드 변경이 생겼을 때 변경 사항 외 기존 동작이 깨지지 않는지 확인할 수 있습니다
- .시스템의 현재 동작을 이해할 수 있습니다.서드파티 라이브러리가 정상적으로 동작하는지 확인할 수 있습니다.
TDD가 무엇일까요?
Test가 주도하는 개발입니다. TDD를 적용한 개발에서는 기능 개발 전 테스트코드를 먼저 작성합니다. 당연하게도 테스트는 실패하고 이 테스트를 성공하기 위한 코드를 작성하는 것을 목표로 합니다. 먼저 작성해둔 테스트 코드를 성공해야만 개발이 끝나기 때문에 작성한 테스트는 개발을 전반적으로 주도하게 됩니다. 그래서 이를 TDD(Test Driven Development), 테스트 주도 개발이라고 합니다.
TDD의 3단계
TDD는 세 부분의 단계를 가지고 있습니다. 이 사이클은 테스트-코드-리팩토링 과정을 가지고 있습니다. 개발한 코드에 대해 이 사이클을 점진적으로 반복하며 개선합니다.
1. 먼저 실패하는 테스트 코드를 작성해야 합니다.
테스트 코드는 꼭 실패해야합니다. 실패하는 테스트는 아직 기능이 완성되지 않았음을 의미합니다. 개발을 시작도 하지 않았는데 성공하는 테스트는 정상적이라고 보기 어렵습니다.
2. 테스트를 통과시켜야 합니다.
기능을 개발하며 테스트를 통과하는 것을 목표로 개발합니다.
3. 제작한 코드를 리팩토링합니다.
테스트를 통과시킨 코드는 최소한의 기능만 가지고 있습니다. 개선할 여지가 보인다면 이를 개선합니다. 테스트 코드 또한 리팩토링의 대상입니다.
TDD를 한다면 뭐가 좋을까요?
테스트 주도 개발이 무엇인지는 잘 알았습니다. 그렇다면 테스트를 먼저 작성하는 개발 스타일에는 어떤 장점이 있을까요?
코드에 대한 자신감
테스트 코드를 먼저 작성하였으니 작성한 코드가 예상대로 잘 돌아간다는 자신감을 얻을 수 있습니다. 작성한 코드가 자신이 설계한 대로 원하는 결과를 준다는 것을 확인할 수 있고, 실수에 대한 불안감을 줄일 수 있습니다. 만약 중간에 코드를 약간 수정한다고 해도 테스트를 통과한다면 코드가 아직 잘 돌아간다는 것을 쉽게 알 수 있게 해 줍니다.
깔끔한 코드
테스트를 먼저 작성한다면 모듈의 단위가 작아집니다. 기능 개발을 할 때에도 작은 모듈에 맞춰서 개발하게 되고 잘 분리된 모듈은 곧 유지보수의 이점으로 돌아옵니다. 기능 수정을 할 때에도 작아진 단위의 모듈을 수정하기 때문에 수정이 더 쉬워집니다.
객체지향적인 코드
결과론 적인 이야기가 될 수 있지만, 테스트 코드를 먼저 작성하고 개발하는 과정에서 나온 코드는 좀 더 객체지향적인 코드로 제작됩니다. 복잡한 스파게티 코드는 곧 테스트가 어려운 코드가 되기 때문에 이러한 코드 작성을 피하게 되고 코드의 재사용을 늘리는 방향으로 코드 스타일이 변화하곤 합니다. 이러한 과정에서 구조가 잘 잡힌 객체지향 코드가 탄생합니다.
모든 코드를 대상으로 한 테스트
작성한 모든 코드에 테스트가 생깁니다. 테스트를 먼저 작성하기 때문에 깜빡하고 테스트를 작성하지 않는다던가, 테스트하기 어려워 포기한 코드를 방치하는 경우가 생기기는 어려울 것입니다. 또한 모든 코드를 대상으로 테스트 코드가 있기 때문에 코드가 어떤 일을 하는지 테스트 코드를 보고 알 수 있습니다. 이는 협업 효율의 증가로 이어집니다.
TDD를 했을 때 뭐가 나쁠까요?
TDD라고 해서 장점만 있는 것은 아닙니다. 테스트를 어떻게 하는지, 테스트에 관련된 배경지식은 어떤 게 있는지 익혀야 합니다. 그리고 TDD를 하기 어려워하는 큰 이유는 생산성이 저하되기 때문입니다. TDD는 아래와 같은 특징 때문에 생산성을 저하시킬 수도 있습니다.
코드를 작성하는 시간이 늘어납니다.
대부분의 개발 프로세스에는 정해진 시간이 있고 대부분 넉넉하지 않습니다. TDD 적용 초기에는 잘 작성하지 않던 테스트를 만들어야 하고, 테스트를 만들기 위해 구조를 잡는 시간이 어느 정도 필요합니다. TDD에 적응한다면 전체적인 개발 속도는 다시 비슷해지지만 당장 눈 앞에 닥친 마감일을 마주한다면 TDD에 시간을 투자하기는 어렵습니다.
고민하는 시간도 늘어납니다.
TDD의 장점 중 코드가 깔끔해지고, 객체지향적인 코드가 탄생하는 것은 마법 같은 일이 아닙니다. TDD를 적용하며 많은 고민을 한 결과물이고, 이를 위해 구조와 설계에 시간을 더 투자하게 됩니다. 이 역시 눈앞의 마감일을 본다면 시간 투자를 하기란 쉽지 않은 일입니다.
테스트를 만드는 방법을 익혀야 합니다.
테스트 코드를 작성하는 방법 또한 새로 익혀야 하는 대상입니다. Mocking, JUnit 등 새로 익혀야 하는 지식이 많고 이는 곧 진입장벽을 의미합니다.
마치며
TDD는 장점도 있고 단점도 있지만 충분히 매력적인 개발 방법론입니다. 소프트웨어의 품질을 높일 수 있고 모든 기능을 테스트할 수 있다는 점은 큰 장점으로 다가옵니다. 이러한 TDD를 적용하기 위한 책을 몇 권 소개하고 이번 포스트를 마치겠습니다.
References
자바와 JUnit을 활용한 실용주의 단위 테스트 / Jeff Langer, Andy Hunt, Dave Thomas
스프링 프레임워크 입문 / 백기선
코드 품질을 높여주는 테스트 주도 개발 알아보기 / Samsung SDS
'Java > 기초' 카테고리의 다른 글
[Java] 많이 헷갈려하는 String constant pool과 Runtime Constant pool, Class file constant pool (10) | 2023.05.19 |
---|---|
[Java] Multi Thread환경에서 동시성 제어를 하는 방법 (4) | 2020.06.06 |
[Java] Java는 Call by reference가 없다 (25) | 2020.03.24 |
[Java] jConsole로 원격에서 실행중인 JVM 모니터링하기 (0) | 2020.02.14 |
[Java] Java의 GC(Garbage Collection)방법과 종류 (1) | 2019.10.17 |
댓글