단위 테스트가 프로젝트에 가져온 긍정적 효과
0. 글을 시작하며
현재 회사에서 진행 중인 프로젝트를 인수인계 받으면서 눈에 띄었던 부분 중 하나는 테스트 코드가 존재하지 않는다는 점이었습니다. 해당 프로젝트를 통해 개발 중인 시스템은 실시간으로 어떤 대상의 값을 보고 내부에서 설정한 정책에 따라 작업 생성여부를 판단하는 로직을 타게 되는 데 판정대상이 단순히 하나의 어떤 대상을 보는 것이 아니라 과거의 값까지도 봐야하는 등 꽤 복잡한 형태로 구성되어 있었습니다. 시스템에서 작업을 생성할 것인지, 말 것인지를 판정하는 로직인 만큼 굉장히 중요한 부분이고, 그렇기에 작성한 로직에 대한 검증이 대단히 중요한 부분이기도 했습니다.
하지만 문제는 이 부분을 포함해서 프로젝트 전체에서 테스트 코드를 통한 검증 자동화가 존재하지 않다보니 기존에는 개발 서버에 배포 후 작업을 돌려보는 등의 방식으로 테스트를 해왔다 점입니다. 이러한 테스트 방식은 어떤 변경사항이 생길 때마다 프로젝트를 다시 배포하고, 실제로 작업을 돌려보는 등 검증에 많은 시간이 필요하고, 직접 눈으로 그 결과를 보면서 테스트 해야하기에 작업 생성 여부 판단 로직에 대한 테스트, 검증 과정이 많은 비용이 든다는 문제점이 존재했습니다.
물론 이러한 문제는 단위/통합 테스트 등의 테스트 코드를 작성하여 테스트를 자동화 하는 방식으로 개선할 수 있습니다. 하지만 이미 오랜기간동안 테스트 코드가 고려되지 않고 개발된 시스템에 테스트 코드를 도입하는 것은 쉽게 접근할 수 있는 문제가 아닙니다. 테스트 하기 어려운 구조로 이미 애플리케이션이 개발된 상황이라면 이를 모두 테스트할 수 있는 구조로 변경하면서 도입해야하기 때문입니다. 이러한 이유들로 시스템 전체에 테스트 코드를 도입하는 시도는 너무나 막연하고, 먼 길처럼 느껴져 시작할 엄두가 나지 않았습니다. 그래서 가장 중요한 작업생성 여부 판단 로직에 대해서만이라도 단위 테스트를 도입해서 이에 대한 테스트, 검증을 자동화 하고 싶었습니다.
서론이 다소 길었습니다. 결과적으로 저는 단위 테스트를 도입하기 위한 기존 작업 생성여부 로직의 구조 변화의 과정을 통해서 작업 생성여부 로직을 테스트 하는 단위 테스트를 성공적으로 도입하였습니다. 비록 굉장히 제한적인 도입이었고, 테스트 중에서도 가장 기초적인 테스트인 단위 테스트에 대한 도입이지만 이 과정에서 배우게 된 긍정적 영향에 어떤 점들이 있었는 지를 기록으로 남겨보고자 합니다.
1. 테스트를 빠르고, 정확하게, 자주 할 수 있다.
단위 테스트를 도입하기 전까지는 테스트를 해야한다고하면 한번 한숨을 내쉬고 시작해야 했습니다. 아무리 개발서버라도 테스트에 필요한 데이터를 세팅해야하고, 데이터 적재 및 로직이 동작하는 데 필요한 시간을 기다려야하고, 결과를 확인하고... 이 과정을 지속적으로 반복해야하기 때문에 굉장히 귀찮고, 개발시간을 많이 빼앗아가는 주범이기도 하였습니다. 그렇기 때문에 자주 테스트 하지 못하고, 여러 개발내용을 한번에 모아서 테스트해야하고, 그렇다보니 버그가 많아져 원인 파악 및 수정이 어려운 등의 악순환이 반복되는 구조였습니다.
하지만 단위 테스트를 도입하자 이러한 어려움이 완전히 없어지고, 테스트를 빠르고, 정확하게 수행할 수 있게 되었습니다. 그리고 그렇다보니 오히려 작은 변경이 생기더라도 테스트를 "자주" 수행할 수 있게 되어 개발 시 실수로 인하여 발생하게 되는 오류들을 획기적으로 줄이고, 개발한 프로그램을 더 신뢰할 수 있게 되는 긍정적인 효과를 가져오게 되었습니다.
결과적으로 더 빠르고, 정확하고, 자주 테스트를 수행하게 됨으로써 기존보다 더 정확하고 편리하게 작성한 코드를 검증할 수 있게되었고, 이를 통해 절약한 시간을 개발에 더 집중하여 사용할 수 있는 선순환을 가져오게 되었습니다.
2. 리펙터링을 자신있게 할 수 있게 되었다.
기존에 테스트 코드가 없는 환경에서 리펙터링을 진행할 때에는 항상 다음과 같은 생각이 저를 괴롭혔습니다.
현재의 리펙터링이 수정 전의 코드와 동일하게 동작함을 보장할 수 있는 가?
이러한 부분을 충분히 확인하지 못하고 잘못된 리펙터링을 하게 되는 경우 시스템의 오류나 잘못된 작업처리로 이어질 수 있기 때문에 리펙터링에 대해 느끼는 부담이 크고, 그렇다보니 다음과 같은 잘못된 결론에 도달하게 되는 경우가 많았습니다.
에이... 지금 잘 동작하잖아? 굳이 리펙터링 해야 해?
결국 이러한 의사결정이 누적되면서 프로젝트 코드가 점점 이해하기 난해해지고, 유지보수하기 어려워지는 구조가 되는 등의 부정적인 결과롤 이어지게 되는 경우가 많았습니다. 하지만 특히 테스트 중에서도 굉장히 빠르게 수행할 수 있는 단위 테스트를 도입하니 리펙터링을 통해 코드의 특정 부분들이 수정되더라도 단위 테스트를 빠르게 수행해봄으로써 이것이 올바른 리펙터링인지 빠르게 확인할 수 있었습니다.
이렇게 리펙터링 작업에 대한 검증을 빠르고 정확하게 수행할 수 있게 되니 리펙터링에 자신감이 붙게되고, 과감하게 리펙터링을 시도해볼 수 있는 환경이 되었습니다. 이전과 반대로 이것이 누적되면서 이해하기 좋은, 유지보수하기 좋은 구조의 코드를 작성할 수 있게 되는 효과로 이어짐을 느낄 수 있었습니다.
3. 테스트 그 자체가 더 좋은 문서가 된다.
물론 이는 조금 부가적인 내용이지만 JUnit을 이용해서 테스트할 때 아래와 같이 @DisplayName 어노테이션을 사용해서 해당 테스트 메서드에 대한 더 자세한 설명을 기술할 수 있습니다.
@Test
@DisplayName("밤 10시가 넘으면 주문을 받을 수 없다.")
public void orderFailedWhenTimeOverRange() {
...
}
테스트 메서드의 이름을 잘 짓는 것으로 그 테스트에 대해 잘 표현할 수 있는 것은 맞지만, @DisplayName 어노테이션에 테스트에 대한 상세정보를 작성함으로서 해당 메서드가 어떤 기능에 대한 테스트인지를 더욱 명확하게 드러낼 수 있게 됩니다. 그리고 이것이 누적되면 이것은 단순히 문서에 애플리케이션의 기능에 대해서 길게 작성한 문서보다 오히려 더 좋은 문서의 역할을 하게 됩니다. 후임자가 코드를 보게 되었을 때 단순히 문서를 읽으며 텍스트 기반으로 애플리케이션의 동작을 이해하는 것보다 실제로 테스트를 빠르게 돌려보면서 애플리케이션의 동작을 이해할 수 있기 때문입니다.
그리고 테스트 코드를 잘 작성해두면 개발하는 당사자도 추후 문서작업에 대한 부담을 덜 수 있다는 점도 큰 장점입니다.
4. 복잡한 외부요인에 대한 고려없이 순수 자바코드로 테스트 한다.
테스트 도입을 가장 어렵게 만드는 이유 중 하나는 대부분의 시스템이 외부 요소와의 다양한 상호작용을 통해 동작하기 때문에 이를 어떻게 테스트할 것인지에 대한 막연함이 크기 때문입니다. DB, 외부 API 서버, AWS S3와 같은 미디어 서버 등 애플리케이션은 다양한 외부 요인들과 함께 연동되어 동작되는 경우들이 대부분이고, 테스트를 막 공부하는 입장에서 이러한 요소들은 어떻게 테스트 해야할 지, 테스트 순서는 어떻게 구성해야할 지 등 고민해야할 내용들이 많고 어렵습니다. 이러한 외부 요인으로부터의 종속성을 제어하기 위해 테스트 코드 작성 시 Mocking을 사용하지만, Mocking은 단위 테스트를 작성하는 것에 비해서 난이도도 더 높고, 공부해야할 내용도 더 많다보니 바로 도입하는 것이 어렵습니다.
하지만 단위테스트의 경우 외부요인에 대한 고려없이 순수자바코드로만 테스트할 수 있도록 구성해야만하고, 그렇게 동작합니다. 즉, 내가 자바로 작성한 소스에 초점을 두고 이 코드가 의도한 대로 동작하는 지 빠르고 정확하게 검증할 수 있기 때문에 DB, 외부 API, 미디어 서버와 같은 외부 연동에 대한 고민으로부터 독립적으로 코드에 만 집중한 테스트할 수 있다는 장점이 있습니다. 또한 순수 자바코드로 테스트하기 때문에 Spring을 직접 띄울 필요도 없고, 각각 독립, 병렬로 빠른 시간 내에 테스트를 수행할 수 있습니다.
물론, 그렇기 때문에 단위 테스트만으로 검증을 수행하면 작은 기능들이 모여 구성된 하나의 통합된 기능을 검증 하는 것에는 한계가 있습니다. 이것이 단위 테스트의 단점이자 한계점이기도 합니다.
5. 글을 마치며
단위 테스트만으로 온전히 애플리케이션을 테스트할 수는 없습니다. 그렇기 때문에 단위 테스트를 통해 검증된 코드들이 모여 하나의 큰 통합된 기능들을 테스트하는 통합 테스트도 존재하고, 직접 사람에 의해 수행되는 테스트도 존재합니다. 하지만 저는 이러한 테스트를 공부하고 적용하는 첫 걸음으로서 "단위 테스트"를 도입했을 때 단위 테스트만으로도 프로젝트에 굉장히 많은 이점을 가져올 수 있음을 나누고 싶었습니다.
처음부터 단위/통합/기능 테스트의 개념들을 완벽히 학습하여 프로젝트, 실무에 적용하기에는 다소 무리가 있습니다. 그만큼 많은 학습시간과 연습의 시간도 필요하고, 충분한 학습과 연습을 마쳤다고 해도 여러 복잡한 상황들이 얽혀있는 실무에서는 이를 즉각적으로 도입하는 것은 단순히 기술적 문제만이 아닌 여러 어려움이 있을 수 있습니다. 그렇기 때문에 테스트에 대해서 완벽하게 학습한 뒤 적용하게 나가겠다는 자세보다는 시작하기 부담이 적고 쉬운 단위 테스트를 작은 부분 부터 적용해보는 것이 오히려 학습동기나 연습의 측면에서 더 좋은 것 같다는 생각이 들었습니다. 테스트는 일정에 쫓기고, 여러 복잡한 상황이 얽혀 복잡해진 실무코드를 다뤄야하는 우리에게 상당히 다가가기 어려운 개념입니다. 하지만 완벽히 테스트를 학습할 때까지 기다리는 것이 아니라 이렇게 단위 테스트부터 차근차근 도입해가면서 실무 코드와 함께 나를 성장시켜나가는 것이 오히려 우리가 테스트와 더 빠르게 친숙해지고, 익숙해지는 좋은 경험이 될 것이라고 생각합니다.
저 또한 현재는 특정 파트에 대해서 제한적인 단위 테스트를 적용하였지만, 이후 통합 테스트, Mocking과 같은 다양한 개념들을 학습하고 적용하면서 더욱 더 신뢰할 수 있는 코드로 구성된 프로젝트를 구성하고 싶고, 이 과정들을 통해 더욱 성장하는 개발자가 되고 싶습니다.
'ETC > 회고' 카테고리의 다른 글
동적 쿼리와의 전쟁... Querydsl을 도입할 수 없었던 이유 (4) | 2024.03.17 |
---|---|
프로젝트 인수인계 시 꼭 기억할 5가지 (0) | 2024.02.04 |
자기개발 미루지 않는 개발자 되기 (1) | 2024.01.21 |
댓글
이 글 공유하기
다른 글
-
동적 쿼리와의 전쟁... Querydsl을 도입할 수 없었던 이유
동적 쿼리와의 전쟁... Querydsl을 도입할 수 없었던 이유
2024.03.17 -
프로젝트 인수인계 시 꼭 기억할 5가지
프로젝트 인수인계 시 꼭 기억할 5가지
2024.02.04 -
자기개발 미루지 않는 개발자 되기
자기개발 미루지 않는 개발자 되기
2024.01.21