Doctolib의 대규모 Rails 앱 테스트 최적화 및 스케일링 전략

Optimizing Rails Tests at Doctolib Scale – On Rails

작성자
HackerNews
발행일
2025년 10월 07일

핵심 요약

  • 1 Doctolib은 8만 4천 개 이상의 테스트와 130+ CPU 시간을 소모하는 대규모 Rails 모놀리스의 테스트 성능을 최적화하기 위해 Rails 기본 설정으로 회귀하는 전략을 채택했습니다.
  • 2 테스트 속도 저하의 주요 원인으로 데이터베이스 리셋, 비효율적인 팩토리 사용, 그리고 수년간 축적된 불필요한 코드들이 지목되었으며, 이를 개선하기 위한 구체적인 방법론이 적용되었습니다.
  • 3 트랜잭션 테스트와 픽스처(fixtures) 사용을 통해 특정 엔진의 테스트 시간을 7분에서 1분 미만으로 단축하여 개발자 경험을 개선하고 인프라 비용을 절감하는 데 성공했습니다.

도입

Robby Russell과 Doctolib의 Rails 엔지니어 Florent Beaurain은 유럽 최대 규모의 Rails 모놀리스 중 하나인 Doctolib의 기술적 도전과 해결책에 대해 논의합니다. 특히, 8만 4천 개 이상의 테스트와 130+ CPU 시간을 소모하는 방대한 CI/CD 환경에서 개발자 경험 개선과 인프라 비용 절감을 위한 테스트 최적화 전략에 초점을 맞춥니다. Florent는 C/C++ 학습의 어려움 끝에 Rails를 만나 개발에 대한 흥미를 되찾았다고 밝히며, Doctolib의 성장 과정에서 발생한 대규모 시스템의 문제점과 이를 극복하기 위한 노력을 공유합니다.

대규모 Rails 환경의 도전과 테스트 성능 문제

  • Doctolib은 약 400-500명의 엔지니어가 매일 기여하는 300만 라인 이상의 Rails 코드를 가진 유럽 최대 규모의 Rails 모놀리스를 운영합니다.

  • 초기 5천 개였던 테스트는 현재 8만 4천 개 이상으로 증가했으며, 전체 CI 실행에 130+ CPU 시간이 소요됩니다.

  • 풀 리퀘스트(PR)당 파이프라인 시간이 40-45분에 달하여 개발 속도 저하와 병목 현상을 초래합니다.

  • 로컬 환경에서 전체 테스트 스위트 실행은 사실상 불가능하며, 메인 브랜치의 안정성이 매우 중요합니다.

테스트 성능 저하의 주요 원인 분석

Florent의 연구 결과, 주요 병목 현상은 다음 세 가지로 나타났습니다:

  • 데이터베이스 리셋: 각 테스트마다 여러 데이터베이스(최대 10개)를 리셋하는 과정이 전체 테스트 시간의 약 30%를 차지했습니다.

  • 팩토리(Factories) 사용: Factory Bot을 통한 객체 생성은 데이터베이스 상호작용 및 복잡한 콜백 등으로 인해 테스트 시간의 약 50%를 소모했습니다.

  • 수년간 축적된 코드(bloat): 좋은 의도로 추가되었지만 전체적인 성능 저하를 야기하는 작은 코드 조각들이 누적되었습니다.

Rails 기본으로의 회귀를 통한 최적화

Florent 팀은 Rails의 기본 설정으로 돌아가는 ‘하드 리부트’ 전략을 채택했습니다.

  • 트랜잭션 테스트(Transactional Testing): 각 테스트를 데이터베이스 트랜잭션 내에서 실행하고 테스트 종료 시 롤백하여 데이터베이스 리셋보다 훨씬 빠르게 데이터를 초기화합니다.

  • 픽스처(Fixtures) 활용: Factory Bot 대신 YAML 기반의 픽스처 사용을 재도입하여 객체 생성 오버헤드를 줄였습니다. Shopify의 Factory Fixture와 같은 하이브리드 접근 방식도 언급되었습니다.

  • 이러한 변화를 통해 특정 엔진의 테스트 시간을 7분에서 1분 미만으로 단축하는 데 성공하여, 전체 코드베이스에 대한 마이그레이션 추진력을 얻었습니다.

Packwerk와 모듈화의 한계

  • 코드베이스 모듈화를 위해 Packwerk를 도입했으나, ‘제로 의존성(zero dependency)’ 패키지 달성은 비현실적이며, 디버깅 및 추적성 측면에서 복잡성을 증가시킬 수 있다는 한계를 경험했습니다.

  • Packwerk는 의존성 문제를 식별하지만 해결 방법을 제시하지 않아, 해결 과정에서 코드 가독성이 저하될 가능성도 있습니다.

데이터베이스 스케일링 및 Rails 업그레이드

  • Doctolib은 Aurora Postgres를 사용하며, 10개의 라이터(writer)와 각 라이터당 최대 15개의 리더(reader)를 운영합니다.

  • 읽기(read) 트래픽을 리더로 자동 분산하는 시스템을 구축하여 라이터의 부하를 줄였습니다.

  • 데이터베이스 샤딩(sharding)을 통해 여러 라이터로 데이터를 분산하며, 안전한 데이터 마이그레이션 및 자동화된 툴링이 필수적입니다.

  • Rails 업그레이드는 초기에는 개인의 책임이었으나 현재는 전담 팀이 담당하며, 변경 사항을 깊이 이해하고, 불필요한 패치 제거 및 Rails 기본 설정 준수를 통해 업그레이드 프로세스를 효율화했습니다.

개발자 경험 및 AI 활용

  • dctl이라는 Go 기반의 CLI 툴을 통해 개발 환경 설정, 스테이징(staging) 데이터베이스 연결 등을 자동화하여 온보딩(onboarding) 효율성을 높였습니다.

  • 기능 스위치(feature switches)를 광범위하게 사용하여 코드 배포의 안정성을 확보하고, 프로덕션 환경에서의 성능 테스트 및 롤백(rollback) 관리에 활용합니다.

  • AI(LLM)는 코드 생성보다는 페어 프로그래밍, 문서 작성, 그리고 기능 스위치 자동 정리와 같은 워크플로우 자동화에 유용하게 활용됩니다.

플래키 테스트(Flaky Tests) 관리

  • 약 2만 개의 엔드-투-엔드(end-to-end) 테스트에서 발생하는 플래키니스(flakiness)는 주요 문제이며, React 프론트엔드와 Capybara 간의 동기화 문제가 주원인입니다.

  • Capybara Lockstep과 같은 도구를 사용하여 프론트엔드 상태와 Capybara의 동기화를 개선하고, 자동 스킵(skip) 및 버그 티켓 생성을 통해 팀이 플래키 테스트를 해결하도록 유도합니다.

결론

Doctolib의 사례는 대규모 Rails 애플리케이션에서 발생하는 성능 및 개발자 경험 문제를 해결하기 위한 실용적인 접근 방식을 제시합니다. 특히, Rails의 기본 철학으로 회귀하여 테스트 스위트의 효율성을 극대화하고, 데이터베이스 스케일링, 기능 스위치, 그리고 자동화된 개발자 도구를 통해 복잡성을 관리하는 전략은 인상적입니다. 비록 Packwerk와 같은 모듈화 도구의 한계도 인정했지만, Rails의 단순성과 일관된 구조가 대규모 팀의 빠른 온보딩과 생산성 유지에 핵심적인 역할을 하고 있음을 강조합니다. 이는 Rails가 여전히 대규모 엔터프라이즈 환경에서도 강력한 '비밀 병기'로 기능할 수 있음을 시사합니다.

댓글 0

댓글 작성

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

아직 댓글이 없습니다

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