빠른 테스트 스위트 구축 및 코드 메트릭 활용 전략

[28S04] The Necessity and Implementation of Speedy Tests

작성자
RubyKaigi
발행일
2025년 10월 05일

핵심 요약

  • 1 느린 테스트 스위트는 개발 팀의 사기를 저하시키므로, 멀티 코어 활용, I/O 최적화, GC 설정 조정을 통해 테스트 속도를 극대화해야 합니다.
  • 2 모킹/스터빙, 셋업 최적화, Factory Girl의 효율적 사용 등 테스트 작성 습관 개선을 통해 테스트의 신뢰성과 속도를 동시에 확보해야 합니다.
  • 3 Metric Fu Gem을 활용하여 코드 중복, 복잡도, 변경 빈도, 커버리지 등 다양한 메트릭을 분석하고 이를 기반으로 지속적인 코드 리팩토링을 수행해야 합니다.

도입

본 발표는 Ruby 개발자이자 기술 작가인 Jake Scrugs가 느린 테스트 스위트가 개발 팀에 미치는 부정적인 영향에 대해 논하며, 이를 해결하기 위한 다양한 전략을 제시합니다. 발표자는 테스트 속도 최적화를 위한 기술적 접근 방식과 더불어, 테스트 작성 습관 개선을 통한 근본적인 해결책, 그리고 Metric Fu Gem을 활용한 코드 품질 관리 방안을 상세히 설명합니다. 느린 테스트는 단순한 불편함을 넘어 팀의 생산성과 사기를 저하시키는 '뱀파이어'와 같다고 강조하며, 개발자들이 테스트 속도와 품질에 지속적으로 관심을 가져야 할 필요성을 역설합니다.

느린 테스트 스위트의 문제점

느린 테스트 스위트는 팀 사기 저하, 지속적인 통합(CI) 빌드 실패 시 책임 공방, 디버깅 시간 증가, 그리고 궁극적으로 테스트 자체에 대한 반감으로 이어집니다. 마치 서서히 끓는 물 속의 개구리처럼, 테스트 스위트의 속도가 점진적으로 느려지는 것을 인지하지 못하다가 큰 문제에 직면하게 됩니다. 이는 개발자들이 인터넷 서핑 등으로 시간을 보내게 만들고, 코드 품질 저하로 이어질 수 있습니다.

테스트 속도 향상 전략 (습관 변경 없이)

개발자의 테스트 작성 습관을 바꾸지 않고도 테스트 속도를 개선할 수 있는 기술적 방법들이 제시됩니다:

  • 멀티 코어 활용: 컴퓨터의 코어 수가 증가함에 따라 Parallel Tests, Hydra, Deep Test와 같은 도구를 사용하여 테스트를 병렬로 실행합니다. 이 방식은 각 코어에 개별 데이터베이스를 설정해야 하는 경우가 많으며, 테스트 실행 시간을 지능적으로 분배하는 ‘비균등 분할’ 기능이 중요합니다.

  • I/O 최적화: 개발 환경에서는 데이터베이스의 Journal Writeback 기능을 비활성화하고, 파일 시스템의 atime (접근 시간 기록) 기능을 끄면 불필요한 디스크 쓰기 작업을 줄여 I/O 성능을 개선할 수 있습니다. 단, 프로덕션 환경에서는 절대 비활성화해서는 안 됩니다.

  • Fast Context: Shoulda와 같은 프레임워크에서 setup 블록과 여러 should 블록을 한 번에 실행하여 중복되는 셋업 시간을 절약합니다. 단, 테스트의 원자성이 깨지지 않도록 주의해야 합니다.

  • Ruby Enterprise Edition GC 설정: Ruby Enterprise Edition을 사용하는 경우, 테스트 실행 중 가비지 컬렉션(GC)을 거의 수행하지 않도록 GC 설정을 극단적으로 높게 조정하면 빌드 시간을 크게 단축할 수 있습니다. 테스트는 많은 객체를 생성하고 버리므로, GC가 병목이 될 수 있습니다.

  • 시스템 프로파일링: CPU 및 I/O 성능 그래프를 모니터링하여 테스트 실행 중 발생하는 병목 현상을 식별하고 개선 기회를 찾아야 합니다.

테스트 품질 향상 전략 (습관 변경 필요)

근본적인 테스트 속도 및 신뢰성 개선을 위해서는 테스트 작성 습관을 개선해야 합니다:

  • 모킹(Mocking) 및 스터빙(Stubbing): 외부 API 호출, 데이터베이스 접근, 파일 시스템 조작 등 비용이 많이 들거나 불안정한 작업을 모킹/스터빙하여 테스트를 격리하고 속도를 높입니다. 외부 API 테스트는 별도의 스위트로 분리하여 실행하는 것이 좋습니다.

  • 셋업(Setup) 최적화: 중첩된 describe/context 블록 내의 setup 남용을 경계해야 합니다. 불필요하게 많은 객체를 생성하는 셋업은 테스트 시간을 늘리고 가독성을 해칩니다. 많은 셋업이 필요한 테스트는 객체 설계에 문제가 있다는 신호일 수 있습니다.

  • Factory Girl 활용: Factory Girl 사용 시 create 대신 build를 사용하여 데이터베이스 저장 시간을 절약합니다. 또한, 최소한의 유효성 검사만 통과하는 ‘작은’ 객체 팩토리를 우선적으로 사용하고, 필요한 경우에만 ‘중간’ 또는 ‘큰’ 객체를 사용하도록 합니다.

  • 테스트를 통한 설계 개선: 많은 셋업이 필요한 테스트는 객체 설계에 문제가 있다는 신호이므로, 이를 통해 더 나은 객체 설계를 고민해야 합니다.

  • ‘테스트 입양’ 프로그램: 프로파일링을 통해 가장 느리거나 문제가 많은 테스트를 식별하고, 여유 시간에 점진적으로 개선해 나가는 ‘테스트 입양’ 프로그램을 운영합니다. 이는 한 번에 모든 것을 고치려다 좌절하는 것을 방지합니다.

Metric Fu를 활용한 코드 품질 관리

Metric Fu는 코드 품질을 분석하는 다양한 도구(Flog, Fle, Cycro, Reek, Rails stats, Archov, Churn 등)를 통합한 Gem입니다. 이를 통해 코드의 문제점을 시각적으로 파악하고 개선할 수 있습니다:

  • Fle (구조적 중복 탐지): 코드 내의 중복된 구조를 찾아내 DRY(Don’t Repeat Yourself) 원칙 위반을 감지합니다. 이는 메서드 추출이나 모듈화를 통해 해결할 수 있습니다.

  • Churn (코드 변경 빈도): 자주 변경되는 ‘휘발성 코드’를 식별합니다. 휘발성 코드는 테스트가 잘 되어 있고 복잡도가 낮아야 합니다. 또한, 시스템 전반에 걸쳐 변경되는 ‘God Object’를 감지하는 데 유용합니다.

  • Archov (코드 커버리지): 코드 커버리지를 측정하지만, 높은 커버리지가 항상 좋은 테스트를 의미하지는 않습니다. 복잡하고 변경이 잦은 코드 영역에 커버리지를 집중하는 것이 중요합니다.

  • Flog & Cycro (복잡도 측정): Cycro는 순환 복잡도를, Flog는 분기, 할당, 호출 등을 종합하여 메서드 복잡도를 측정합니다. Flog 점수 60 이상은 메서드를 분리하여 리팩토링할 필요가 있음을 시사합니다.

  • Reek, Raudy, Rails Best Practices (디자인 문제 감지): Feature Envy, Long Method 등 일반적인 디자인 스멜을 감지합니다. 단, 이러한 도구의 조언은 기계적인 판단이므로 항상 옳지는 않으며, 팀의 상황과 맥락에 맞게 해석해야 합니다.

  • Metric Fu의 미래: 여러 메트릭을 조합하여 코드의 ‘핫스팟’ (예: 변경이 잦고, 복잡하며, 커버리지가 낮은 메서드)을 식별하는 ‘메타메트릭스’ 기능 개발을 목표로 합니다.

결론

결론적으로, 빠르고 신뢰할 수 있는 테스트 스위트는 개발 팀의 생산성과 사기를 유지하는 데 필수적입니다. 테스트 속도 최적화를 위한 기술적 접근과 더불어, 모킹/스터빙, 셋업 및 팩토리 사용 최적화와 같은 테스트 작성 습관 개선이 동반되어야 합니다. 또한, Metric Fu와 같은 코드 메트릭 도구를 적극적으로 활용하여 코드의 중복, 복잡도, 변경 빈도, 커버리지 등을 지속적으로 모니터링하고, 이를 리팩토링의 지표로 삼아야 합니다. 코드 메트릭은 절대적인 규칙이 아닌 '조언'임을 인지하고, 팀의 상황에 맞춰 현명하게 적용하여 지속적인 코드 품질 향상을 추구하는 것이 중요합니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!