> 서론
최근 “테스트의 사실과 오해”라는 주제로 사내 컨퍼런스에서 발표하는 기회가 있었습니다. 컨퍼런스 참여 후기는 이전 스토리👈🏻링크클릭🔗에 남겨두었습니다.
제 발표 들어주신 분들의 긍정적인 피드백에 용기를 얻어서 실제 발표 내용도 공유해봅니다.
내용은 Community Edition 으로 서비스 특정 내용들은 변경하고 필터 처리했습니다. ✌ ️
> 목차
► 소프트웨어의 테스트란?
► 내가 경험한 테스트의 사실과 오해
-▷ 1 오해 : 우리는 인력/시간이 부족해서 테스트를 작성하지 못한다
-▷ 1 사실 : 우리는 인력/시간이 부족하기 때문에 테스트를 활용해야 한다
-▷ 2 오해 : 테스트는 빌드시점에만 동작해야한다
-▷ 2 사실 : 테스트는 제품의 생명주기 전반에 걸쳐 적용 가능하다
-▷ 3 오해 : 우리는 “테스트-커버리지-100%”, “TDD”, “자동화테스트” 등을 위해 투쟁해야 한다
-▷ 3 사실 : 오늘 테스트코드 1, 2개 먼저 작성하는 것을 시작해야 한다
-▷ 4 오해 : 변경이 많은 상황에서는 테스트 작성이 무의미하다. (기획, 기능, 요구사항 etc…)
-▷ 4 사실 : 그럼에도 불구하고… 테스트 작성은 유의미하다
-▷ 5 오해 : 테스트는 어렵다
-▷ 5 사실 : 테스트는 쉽다
-▷ 6 오해 : 지금은 변경의 여지가 많으니까 나중에 테스트를 작성하면 된다 (현재와 미래의 테스트 작성비용이 같다)
-▷ 6 사실 : 지금과 미래의 테스트 작성 비용은 많이 다르다
► 페르소 백엔드의 테스트 도입 현황
-▷ 코드 라인 수와 테스트케이스 수의 변화
-▷ 테스트는 확신과 용기를 심어준다
-▷ 버전증가에 따른 코드라인 수정 규모 추이
► 서비스 자랑하기
> 소프트웨어의 테스트란?
테스트란, 검증과 확인을 통해 소프트웨어의 결과물 및 동작을 검사하는 행위입니다.
테스트 방법은 사람이 직접 수동으로 하거나, 코드로 작성하거나 툴을 활용할 수가 있습니다.
오늘 발표에서 제가 이야기하는 테스트는 코드로 작성하거나 툴을 활용해서, 궁극적으로 자동화가 가능한, 그런 테스트로 한정지어서 이야기하겠습니다.
시작하겠습니다. 제가 경험한 테스트의 사실과 오해입니다.
첫번째 오해는 우리는 인력과 시간이 부족해서 테스트를 작성하지 못한다 인데요.
사실 우리는 인력과 시간이 부족하기 때문에 테스트를 활용해야 합니다.
우리는 시간이 없어서 테스트코드 작성을 못한다고 이야기합니다. 하지만 사실 지금없는 시간은 나중에도 생기지 않습니다.
신규 기능을 개발하거나 버그를 수정할 땐 시간이 없어서 못한다고 하지 않는데 왜 테스트 작성할 시간은 항상 없을까요?
그 이유를 저는 근본적인 개발 비용 계산의 오류라고 생각합니다.
기능 개발은 일시적 것이지만 품질을 보장하는 테스트 규모는 누적해서 증가하기 때문입니다.
시간 흐름에 따른 개발비용 변동 그래프입니다.
기능 A를 개발합니다. 테스트 A를 진행합니다.
기능 B를 개발합니다. 테스트 A와 테스트 B를 진행합니다.
기능 C를 개발합니다. 테스트 A, B, C를 진행합니다.
서비스가 성장할 수록 기능은 많아지고 테스트 규모도 커져만 갑니다.
시간이 부족해집니다.
이럴때 우리는 테스트를 활용할 수 있습니다.
자동화가 가능한 테스트들은 자동화를 시켜서 누적되는 테스트 대상 규모를 최소화합니다.
자동으로라도 테스트하지 않는다면 언젠가는 확인없이 배포하는 기능들이 많아질 것입니다.
그래서 우리는 테스트를 활용해 부족한 인력과 시간 문제를 해결 할 수 있습니다.
두번째 오해는 테스트는 빌드 시점에만 동작해야한다 인데요.
사실 테스트는 제품의 생명주기 전반에 걸쳐 적용이 가능합니다.
테스트를 조금 일반화 시켜 “무엇인가를 확인하는 행위”로 생각하면 우리는 테스트를 더 광범위하게 활용할 수 있습니다.
계획, 코딩, 빌드, 테스트, 배포, 운영 등 어플리케이션 생명주기 전반에 걸쳐 테스트를 활용 할 수 있게됩니다.
솔루션으로 개발되는 서비스들은 대부분 구축사 별로 운영 서버가 존재합니다.
각 서버에 서비스를 배포하는 과정에는 다양한 가정들을 존재합니다.
- 데이터베이스에 대한 가정
- 의존 리소스에 대한 가정
- 운영체제에 대한 가정
- 성능에 대한 가정
등 수많은 가정들이 있고 우리는 이 모든 가정들 위에서 서비스 품질을 보장해야합니다.
저는 이런 가정들을 확인하는 행위 또한 테스트라고 정의 합니다. 이 테스트들은 굉장히 수동적이고 시간도 오래걸립니다.
사람에 의해 동작하니 정확도도 떨어지죠.
심지어 구축사마다 버전이 branch-out 되면 복잡도와 소요시간은 제곱으로 늘어납니다.
우리는 이런 수동적인 테스트들의 규모를 최소화하는 노력을 지속해야합니다. 시간이 곧 자원이고 자원이 고갈나면 아무것도 할 수 없기 때문이지요.
다음 슬라이드에서 소개할 예제 테스트는 배포시 운영환경에 대한 여러 가정들을 확인하는 테스트로 자바/스프링을 활용합니다.
이 테스트는 파일을 읽어서 서버가 필요로 하는 어떤 라이브러리의 라이센스 정보를 검증합니다. (혹시나 예제가 부실하다면 양해 부탁드립니다 😆)
코드를 설명드려보겠습니다.
- A : 이 테스트는 서버가 프로덕션 환경에서 실제로 시작될 때 동작하는 테스트입니다. 동작 시점은 ApplicationReadEvent 로서버 (Application)가 클라이언트의 요청들을 처리할 수 있는 시점(ReadyEvent) 에 동작하는 테스트입니다.
- B : SomeLicenseFileReader 컴포넌트로 라이센스 파일 내부의 라이센스 정보를 읽어와서
- C : 만약 실제 값(actual)이 의도한 값(expected)과 일치하지 않으면 런타임 익셉션을 발생시키고 서버 실행을 중단합니다.
- D : 또한 실제 값(actual) 자체가 존재하지 않아도 런타임 익셉션을 발생시키고 서버 실행을 중단합니다.
C 또는 D 구간이 실행되는 경우, 서버는 시점에 에러를 뱉어내며 즉시 종료됩니다.
너무 대놓고 실패하니 문제 존재여부를 사전에 미리 파악할 수 있고 방법론적으로 이것을 FAILING FAST, FAILING LOUD 라고도 합니다.
다양한 환경에서 배포되는 불안정 위에서 품질을 보장하기 위해 우리는 이런 테스트들도 작성할 수 있습니다.
세번째 오해는 우리는 “테스트-커버리지-100%”, “TDD”, “자동화테스트” 등을 위해 투쟁해야 한다 인데요.
그보다 우리는 오늘 테스트코드 한개 두개 작성하는 것부터 시작하면 됩니다.
위 그림은 테스트 작성을 방해하는 우리 상상 속 테스트의 모습입니다.
걸리버 여행기라는 소설의 표지인데요, 주인공이 표류되었고 정신을 차려보니 소인들에 의해 단단히 묶여있는 장면입니다.
저런 테스트 관련 엄청 근사하고 강력한 것들을 이루고자하는 과정에서 것들은 되려 부담으로 다가올 것 입니다.
NO 부담.
우리는 오늘부터 테스트에 대한 모든 부담을 내려놓습니다.
우리가 꿈꾸고 기대하는 자동화된 테스트, 테스트 커버리지 100%, 기타 등등의 것들은 지속된 노력에 따라오는 당연한 결과입니다.
누구나 오늘 바로 테스트를 시작 할 수 있도록 제가 한번 우리의 현재와 목표를 조심스레 제안해보겠습니다.
우리의 첫 목표는 테스트 케이스 하나 작성하는 것 입니다.
어차피 빌드하고 배포하는 거, 무엇인가를 검증하는 테스트 코드 하나를 포함시켜봅니다.
어려운 기능을 테스트 할 필요도 없습니다.
30분 전에 작성해서 이해도가 최대치인 기능을 테스트로 작성해 봅니다.
복리의 누적입니다. 여러분 🙌 🙌
오늘 테스트 코드 하나 작성하면 매일 자동화된 테스트 하나가 돌아가고
내일도 테스트 코드 하나만 작성하면 매일 자동화된 테스트 두개가 동작합니다.
이런게 쌓이고 쌓여 Best Practice 와 노하우들이 생겨나고 언젠가 우리도 걸리버를 단단히 묶어둘 수 있게될것입니다.
네번째 오해입니다. 변경의 여지가 많은 상황에서는 테스트 작성이 무의미하다 인데요.
사실은 그럼에도 불구하고… 테스트 작성은 유의미 합니다.
이에 대한 논리 두가지를 들이밀어보겠습니다.
첫번째 논리, 변경은 당연하기 때문입니다.
소프트웨어는 건축물이 아니니까요.
변경에 인해 너무 많은 테스트가 무의미해진다면 어떠한 개선의 신호 일 수도 있습니다.
이 논리를 고도화시켜보면
오히려 테스트가 도움이 된다는 주장까지 펼칠 수 있습니다.
다음은 개발자 필독서인 실용주의 프로그래머에서 발쵀한 내용입니다.
테스트 가능성이 높은 코드는 디자인이 좋다. 재미있게도, 디자인을 잘 만들려고 할 때보다 테스트 가능성을 높이려고 했을 때 결과 코드의 디자인이 더 나은 경우가 많다. — The Pragmatic Programmer, by Andrew Hunt
앤드류 헌트의 말대로 라면 테스트 가능성을 높이면 코드의 디자인이 좋아지고 디자인이 좋으면 유지보수성이 높아집니다.
결과적으로 우리는 필요한 변경 사항들을 더 쉽게 처리해낼 수 있겠죠?
다섯번째 오해는 “테스트는 어렵다” 인데요.
사실, 테스트는 쉽습니다.
이미 만들어진 툴을 사용하면 되거든요.
베스트 코드리뷰어인 SonarLint 라는 툴을 소개합니다.
SonarLint는 정적 분석 툴로 코드의 품질을 테스트합니다.
코드 품질이 기준치를 넘지못하면 실패 처리해서 저품질의 코드 배포를 막을 수도 있습니다.
SonarLint 가 얼마나 쓸모 있는가 하면 구글, 마이크로소프트, IBM 등에서도 사용합니다.
지원하는 언어도 엄청 많습니다. Java, Python, JavaScript, TypeScript 등 우리가 알만한 프로그래밍 언어는 전부 지원합니다.
접근성도 뛰어난데요, Java 기준 IntelliJ 플러그인 마켓플레이스에서 무료로 설치해서 버튼 클릭만으로도 동작 시킬 수 있습니다.
제가 경험한 SonarLint 장점들입니다.
첫째는 코드 리뷰 시간과 에너지 소모의 극적인 감소입니다.
SonarLint 덕분에 리소스 투자없이 풀타임 전문 코드리뷰어가 붙는 셈이 됩니다.
두번째 장점은 리뷰 퀄리티의 향상입니다.
SonarLint 덕분에 리뷰 할때 단순 클린코드를 넘어서 코드의 설계와 디자인까지 리뷰 할 시간적, 정신적 리소스가 생겨납니다.
우리는 이미 만들어진 툴 들을 최대한 활용해서 테스트를 쉽게 만들 수 있습니다.
마지막 오해는 지금은 변경의 여지가 많으니까 나중에 테스트를 작성하면 된다는 오해인데요.
그 내면에는 현재와 미래의 테스트 작성비용이 동일하다는 오해가 숨어있습니다.
사실 테스트 작성 비용은 현재인지, 미래인지 그 시점에 따라 정말 많이 달라집니다.
그 비용 차이를 계산해보면요.
새로운 기능 개발을 예로들어 보겠습니다.
현재는 개발을 하는 과정에 있기때문에 이해도가 높아서 테스트 작성 시간 외의 비용은 거의 발생하지 않습니다.
미래로 가면 여러 비용이 발생하기 시작합니다.
- 기본적으로 테스트 작성시간이 소모됩니다.
- 가장 비용이 큰 이해하는 단계도 포함됩니다. 테스트 할 기능과 기능의 구조를 다시 이해하는 시간이 필요합니다.
- 좋은 코드 디자인과 같이 누리지 못한 테스트의 혜택 또한 비용입니다.
- 추가로 테스트가 없어서 발생가능한 잠재적 버그들도 비용계산에 넣어야됩니다.
이처럼 테스트 작성비용은 그 시간적 시점에 따라 비용 차이가 발생하기 때문에 프로젝트의 상황에 따른 적합한 비용 계산이 필요합니다.
네. 지금까지 제가 경험한 테스트에 대한 사실과 오해를 소개해드렸습니다.
이번에는 저희 서비스 백엔드의 테스트 도입 현황을 가볍게 살펴보겠습니다.
지난 6개월간 달라진 라인 단위 코드 베이스 규모와 테스트케이스 규모 추이입니다. (정확한 수치는 필터 처리했습니다)
소스코드는 약 2배 정도 성장했고 테스트 규모는 5배 정도로 더 가파르게 성장했습니다.
이러한 변화를 저는 “테스트의 밀도가 높아지고 있다.” 라고 표현해봅니다.
테스트의 밀도가 높아지면 우리는 무엇을 얻을 수 있을까요?
저는 확신과 용기라고 생각합니다.
자동화된 테스트는 하루에 수십번, 수백번 동작하기 때문에
품질에 대한 더 큰 확신을 가지게됩니다. 그러면 우리는
용기를 가지고
더 적극적으로 코드개선을 하게되고
다시 테스트 가능성을 높이는 선순환을 발생시킬 수 있습니다.
실제 저희 페르소 백엔드의 버전 증가에 따른 소스코드 수정 규모 추이입니다 (정확한 수치 대신 규모의 변화로 마스킹 처리했습니다)
단위는 코드 라인 수이고, 추가 규모는 파란색, 삭제 규모는 빨간색으로 표현되었습니다.
보시면 코드베이스 변경규모가 처음 버전 업대비 3배 정도 커진 것을 확인 할 수 있는데요.
테스트 밀도가 높아지면서 저는 확신과 용기를 가지고 적극적인 코드개선을 하는 경험을 하게 되었습니다.
지금까지 제가 “테스트의 사실과 오해”라는 주제로 발표한 내용입니다.
읽어주셔서 감사합니다!
지금까지 많이 도와주신 팀원분들, 그리고 테크센터 구성원들 모두 감사드립니다
'개발상식' 카테고리의 다른 글
버그 발생 비용의 세부 요소 분석 (1) | 2023.11.24 |
---|---|
더 빨리 움직이면서 느려지는 우리를 위해서, 클린코더의 교훈 (0) | 2023.11.11 |
성능과 메모리 효율 관점에서 JDK 의 ArrayDeque, Stack, LinkedList 비교 (0) | 2023.07.15 |
백엔드 개발 기초 배경 부분 모음집 (2) | 2022.06.04 |
[Scratch.md] 웹소켓 WebSocket 에 대해서 알아보자 (0) | 2022.02.17 |