
발자국 3주차: 테스트에서 가장 중요한 것
테스트의 중요성?
사실 테스트가 [중요하다]는 것은 주입당한(?) 사상으로 어렴풋하게 알고 있었던 것 같다.
다들 중요하다고 하니까. 내가 보기에 그렇게 보이기도 하니까.
하지만 실무를 실제로 진행하면서 테스트를 작성할 수 있는 일 자체는 [일정]을 이유로 불가능했고,
이는 테스트 코드 없이 실제 운영되는 코드를 작성하는 결과만을 낳았다.
돌이켜보면 이는 오히려 비효율적인 수동 반복 테스트를 낳기도 했다.
음, 그 시간에 차라리 테스트 코드를 짰다면... 나는 더 빠르게 테스트를 진행했을 수도 있다.
하지만 그동안은 테스트 코드가 [학습]의 영역이 아니어야 실무에 적용이 가능하다고 여겼다.
(특히 일정이 이슈가 된다면 더더욱)
따라서 이번 기회에 [학습]의 꼭지점을 잡고, 프로젝트에 적용하는 것을 연습해 보기로 했다.
이번 프로그램을 신청한 목적은 [테스트] 였기 때문에 가장 초점을 두고 공부하고자 한 영역이기도 했다.
무엇을 테스트하는가?
우리는 프로그램을 만든다. 프로그램은 잘 돌아가야 한다.
잘 돌아간다는 것은 곧 정확하게 돌아가는 것과 같다
테스트는 프로그램이 [정확하게] 돌아가는 것을 보장하는 일이다..
그렇다면 무엇을 기준으로 정확성을 판단할 수 있을까?
음... 내가 가장 어려워 하는 것은 이런 부분인 것 같았다. 테스트를 삼을 기준.
따라서 이번에는 어떤 것이 기준이어야 하는지 생각해 보았고, 그 결과는 다음 4가지로 귀결되었다.
기능적 요구사항 – 프로그램이 제공해야 하는 기능이 기대한 대로 동작하는가?
데이터 저장 후 올바르게 조회할 수 있는가?
성능적 요구사항 – 성능, 보안, 확장성과 같은 요소들이 기대치를 충족하는가?
API 응답 속도가 1초 이내로 유지되는가? 동시 접속자가 많아도 서비스가 정상적으로 운영되는가?
TPS는 원하는 대로 나오는가?
경계 조건 및 예외 처리 - 비정상적인 IO가 발생하는가?
입력 값이 비어 있거나, 허용 범위를 벗어난 경우에도 오류 없이 처리되는가?
예외 시에 적절한 예외 반환을 보장하는가? 정확한 유효성 검사를 수행하는가?
데이터 무결성의 검증 - 트랜잭션은 작동했는가?
하나의 작업에 대해 의도한 모든 트랜잭션이 적절하게 적용되었는가? 실패한 프로세스는 없는가?
실패 시 Fallback이 정확하게 이뤄졌는가?
사실 무엇을 테스트할까 알아보면서 내리게 된 결론은.. 한 가지의 방향점을 가리키고 있었다.
지금 내가 세운 기준은 그동안 세워 왔던 단위 테스트 명세서에 들어 있던 것들이라는 걸.
왜 이것들을 진작 연결지어 보지 못했었는지.
테스트가 개발의 병목이 되지 않으려면
좋다. 이제 어떤 걸 테스트할지에 대해서는 명확해졌다.
강의에서는 [어떻게] 테스트를 하는지에 대한 방법론과 그걸 [할 때]의 효율성에 대해 다룬다.
강의를 보게 됨으로써 내 테스트는 한 층 더 정교해지고 빨라졌을 것이다.
그런데 나는 한 가지를 더 생각해 보고 싶었다.
[무엇을 우선순위로 삼을 것인가.]
서두에도 언급한 만큼, 테스트 코드를 작성하는 것은 중요하지만,
현실적으로 일정이 촉박한 상황에서 테스트가 개발 속도를 저해하는 요소가 되어서는 안 된다.
따라서, 테스트를 작성하면서도 일정을 맞추는 방법을 고민해야 한다.
이럴 때 필요한 것이 우선순위가 아닐까 한다.
나는 이번에 다음과 같은 우선 순위를 결정해 보았다.
1. 핵심 로직만을 검증하는 Smoke Test(기본 동작 여부 확인)
모든 코드에 대해 100% 테스트 커버리지를 목표로 하면 개발은 지연될 수 있다.
따라서 일정이 촉박한 상황에서는 가장 중요한 핵심 로직과 장애 발생 가능성이 높은 부분만을 테스트 코드로 먼저 작성해야 한다는 생각이 들었다.
이를 비롯해 내가 처음에 이야기했던 [반복 수행될 수밖에 없는 케이스] 같은 곳.
어떠한 케이스 테스트를 코드를 수정할 때마다 돌려 봐야 한다면, 그 케이스들을 먼저 작성하고, 코드로 옮길 수 있는 부분들을 먼저 고려해 볼 것 같다.
2. 테스트 전략을 역할별로 나누어 적용하기
테스트를 작성할 때 모든 것을 하나의 방식으로 해결하려고 하면 시간이 오래 걸릴 수 있다.
이에 따라 역할별로 최소한의 전략을 선택하면 효율적인 테스트 작성이 가능할 것 같다.
단위 테스트(Unit Test)
메서드 단위로 검증하여 빠르게 문제를 찾는다.
검증이 잦게 필요한 곳이나 오류가 나기 쉬운 테스트에 우선 적용한다.
통합 테스트(Integration Test)
여러 모듈 간의 연동을 검증한다. 실행 속도가 느리므로 꼭 필요한 부분에만 적용한다.
UI 테스트
사용자 흐름을 검증하는 테스트로, 작성 및 유지보수에 시간이 많이 걸린다.
핵심 기준을 명확하게 세우고, 비즈니스 로직과 분리해서 테스트한다.
즉, 단위 테스트를 우선 작성하여 빠르게 피드백을 받고, 일정에 여유가 있다면 통합 테스트를 추가하는 방식으로 접근하면 효율적일 것 같다.
또한 업무 단위로 테스트가 이뤄지는 곳은 UI 테스트가 통합 테스트로 여겨지기도 하기 때문에, 이러한 분리를 거친다면 검증에 대한 효율이 올라갈 듯하다.
테스트는 ##이다.
이번에 내리게 된 결론.
테스트는 결국 시간의 효율을 위한 것이다.
코드는 사람이 작성하는 것이며, 사람은 실수를 한다. 테스트는 이러한 실수를 빠르게 발견하고 수정할 수 있도록 도와준다.
좋은 테스트는 단순히 버그를 잡는 것이 아니라, 개발자와 사용자 모두에게 소프트웨어가 신뢰할 수 있는지에 대한 확신을 제공한다. 그 고민의 시간을 줄여 줄 것이다.
코드가 변경될 때마다 테스트를 통해 예상치 못한 문제가 발생하지 않는다는 것을 확인할 수 있으며, 이는 코드의 품질을 유지하는 가장 확실한 방법이다.
결국, 테스트는 단순한 검증 과정이 아니라, 코드의 신뢰성을 보장하는 핵심 요소이다.
이를 통해 개발자는 더 빠르고 안정적으로 코드를 수정할 수 있으며, 사용자 역시 신뢰할 수 있는 소프트웨어를 사용할 수 있다.
이 모든 것을 알면서도 주저할 수밖에 없는 것은 당장의 시간, 당장의 일정, 당장의 촉박함.
하지만 이번 회고를 토대로 나는 그러한 상황에서도 어떤 의사결정을 내릴지 결정할 수 있었다.
강사님은 말씀하셨다. 고용된 우리는 프로이며, 프로의 첫 의무는 일정 준수라고.
뼈에 오늘도 새겨 두면서 ..... 이제 앞으로 남은 강의와, 네 번째 발자국이 남아 있다.
3월의 마지막 주도 잘 갈무리해 보고자 한다.
댓글을 작성해보세요.