대규모 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) 및 버그 티켓 생성을 통해 팀이 플래키 테스트를 해결하도록 유도합니다.