테스트를 하나의 단계가 아닌 리스크 예산(Risk Budget)으로 취급하기 시작한 이유
요약
테스트를 단순한 개발 단계가 아닌 '리스크 예산(Risk Budget)'으로 바라보는 관점의 전환을 제안합니다. 코드 커버리지 수치에 집착하기보다 프로젝트의 불확실성과 위험 요소를 식별하고 우선순위를 정하는 것이 더 가치 있음을 강조합니다.
핵심 포인트
- 테스트는 일정 관리의 단계가 아닌 리스크를 관리하는 예산이다.
- 나선형 모델의 핵심인 리스크 중심 사고를 테스트 전략에 적용해야 한다.
- 단순 코드 커버리지 수치는 실제 시스템의 안전을 보장하지 못한다.
- 위험도가 높은 핵심 트랜잭션과 경계 조건에 테스트 자원을 집중해야 한다.
제 커리어 초기에 습득한 대부분의 테스트 조언들은 사실상 일정 관리(scheduling)에 관한 것이었습니다. 빌드한 후에 테스트하라, 회귀 테스트 스위트(regression suite)를 자동화하라, 쉬프트 레프트(Shift left)하라. 이 모든 것들은 유용하고 실제적인 조언들이지만, 테스트가 실제로 가치가 있는지 결정하는 유일한 질문, 즉 '애초에 무엇을 테스트할 가치가 있는가, 그리고 얼마나 많이 테스트해야 하는가'라는 질문은 교묘하게 피해 가고 있었습니다.
테스트가 거쳐 지나가는 하나의 단계(phase)가 아니라, 할당해야 하는 예산(budget)이라는 사실을 깨닫기까지 부끄러울 정도로 오랜 시간이 걸렸습니다. 그리고 실제 프로젝트를 거치며 살아남은 프레임워크들은 바로 테스트를 그런 방식으로 취급하는 것들이었습니다.
1980년대 프로세스 모델에서 빌려온 아이디어
이러한 사고방식의 전환은 뜻밖의 곳에서 찾아왔습니다. 역사적 호기심으로 Barry Boehm이 1986년에 발표한 소프트웨어 공학의 나선형 모델(spiral model)에 대해 읽고 있었습니다. 저는 먼지 쌓인 유물일 것이라 예상했습니다. 하지만 제가 발견한 것은 제가 수년간 더듬거리며 찾아 헤매던 아이디어를 가장 명확하게 표현한 것이었습니다.
나선형 모델은 프로젝트를 반복되는 루프(loop)로 구성하며, 모든 루프는 무언가를 빌드하기 전에 한 가지 질문을 던지는 것으로 시작합니다: '지금 가장 무서운 미지의 요소(unknown)는 무엇이며, 이를 어떻게 저렴하게 공략할 것인가?' 리스크(Risk)는 프로젝트 시작 시 체크해야 할 항목이 아닙니다. 그것은 다음에 무엇이 일어날지를 결정하는 엔진입니다. 여러분은 프로젝트를 침몰시킬 가능성이 가장 높은 요소를 가장 먼저 직면하며, 대개 일회용 프로토타입(throwaway prototype)을 사용하여 이를 해결합니다. 그리고 불확실성이 해소된 후에야 비로소 실제 비용을 지출합니다.
'프로젝트'를 '테스트 스위트(test suite)'로 바꾸어 생각하면 모든 것이 맞아떨어집니다. 여러분의 가장 위험한 미지의 요소들이 바로 테스트 노력이 투입되어야 할 지점이며, 그 외의 모든 것은 테스트를 덜 하더라도 감당할 수 있는 오차 범위 내의 일들입니다.
커버리지(Coverage)는 끔찍한 목표입니다
거의 모든 팀이 빠지는 함정이며, 저 또한 포함됩니다. 누군가 대시보드에 커버리지(Coverage) 퍼센트를 올려두는 순간, 팀은 안전(Safety) 대신 그 숫자를 최적화하기 시작합니다. 결국 쉬운 경로, 단순한 게터(Getters), 문서화가 잘 된 엔드포인트(Endpoints)는 아름답게 커버되지만, 정작 위험이 도사리는 곳에는 거대한 구멍이 뚫리게 됩니다. 바로 동시성(Concurrency)의 가장자리, 제3자 타임아웃(Third-party timeout), 권한 경계(Permission boundary), 그리고 돈이 움직이는 트랜잭션(Transaction) 같은 곳들 말입니다.
테스트 스위트(Suite)는 건강해 보입니다. 하지만 리스크(Risk)는 거의 움직이지 않았습니다. 대시보드는 잘못된 느낌을 제조해내는 '신뢰 제조기'가 되어버렸습니다.
커버리지 수치는 결제 엔드포인트가 아바타 업로드보다 더 중요하다는 사실을 알려줄 수 없습니다. 그 결과가 가져올 파급력을 이해하는 인간만이 그 우선순위를 매길 수 있으며, 그 순위 매기기야말로 여러분의 테스트 노력이 만들어내는 가장 가치 있는 산출물입니다.
실제로 제 작업 방식에서 바뀐 점
이것의 실질적인 버전은 도구의 문제라기보다 순서(Sequence)의 문제였습니다. 좋은 소프트웨어 테스트 전략 (Software testing strategies)은 근본적으로 할당 문서(Allocation documents)입니다. 이 전략들은 한정된 시간과 한정된 파이프라인(Pipeline) 시간을 의도적으로 사용하며, 그 안에서 가장 중요한 문구는 무엇을 테스트할 것인가가 아니라, 팀이 의식적으로 무엇을 테스트하지 않기로 선택했는가입니다.
요즘 저는 새로운 기능에 대해 단 하나의 테스트를 작성하기 전에, 쉬운 언어로 다음 네 가지 질문에 답하려고 노력합니다:
- 여기서 실패했을 때 사용자의 하루를 망치는 것은 무엇인가? 여기에 가장 깊은 커버리지를 할당합니다.
- 이전에 무엇이 고장 났었나? 과거의 모든 장애 사례는 예외 없이 영구적인 회귀 테스트(Regression test)를 갖게 됩니다.
- 무엇이 정말로 제대로 구현하기 어려운가? 동시성(Concurrency), 돈, 권한, 외부 의존성(External dependencies) 등이 해당됩니다. 여기에 깊이를 더합니다.
- 솔직히 테스트를 덜 해도 감당할 수 있는 것은 무엇인가? 이것을 소리 내어 명시하는 것이 앞선 세 가지를 위한 예산을 확보하는 방법입니다.
그 과정이 끝난 후에야 비로소 어떤 프레임워크(Framework)나 도구(Tool)가 계획을 실행할지 고민하게 됩니다. 도구는 승수(Multiplier)일 뿐입니다. 전략이 목표입니다. 전략 없이 빠른 도구만을 겨냥한다면, 생성하기 가장 쉬운 곳에서만 결과물을 만들어내게 될 것이며, 그곳은 리스크(Risk)가 존재하는 곳인 경우가 거의 없습니다.
불편한 부분
이 모든 것 속에 숨겨진 냉혹한 진실은, 훌륭한 테스트 전략을 세우려면 의도적으로 어느 정도의 리스크를 수용해야 한다는 점입니다. 이는 잘못된 것처럼 느껴집니다. 모든 본능은 모든 것을 테스트하라고 말합니다. 하지만 모든 것을 동일한 깊이로 테스트하는 것은 우선순위가 없는 테스트를 하는 것과 다름없으며, 한정된 인력을 가진 팀은 자신들의 주의력이 무한한 것처럼 가장할 여유가 없습니다.
나선형 모델(Spiral model)의 가장 오래된 교훈은 성숙함이란 리스크가 없는 상태가 아니라, 리스크를 의도적으로 관리하는 상태라는 것입니다. 가장 큰 미지의 영역에 먼저 맞서십시오. 막대한 비용을 쓰기 전에 저렴한 비용으로 정보를 확보하십시오. 무엇을 쫓지 않을 것인지 소리 내어 의도적으로 결정하십시오.
저는 제 프로젝트에 공식적인 나선형 루프(Spiral loops)를 실행하고 있지는 않으며, 여러분도 아마 그러지 않을 것입니다. 하지만 테스트를 완료해야 할 단계가 아닌 리스크 예산(Risk budget)으로 취급하는 방식은, 커버리지(Coverage)를 쫓는 행위가 그 어떤 것을 해냈던 것보다 제가 출시하는 제품의 실제 신뢰성을 높이는 데 더 큰 도움이 되었습니다. 이 프레임워크는 40년이나 되었습니다. 이 프레임워크가 바로잡고자 하는 실수는 지금 이 순간에도 수백 개의 저장소(Repo)에서 반복되고 있습니다.
여러분의 팀이 의식적으로 테스트하지 않기로 결정한 한 가지는 무엇이며, 그것이 나중에 발목을 잡은 적이 있나요? 저는 이런 이야기들을 수집하고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기