Stripe의 대규모 Ruby 시스템 운영: 개발자의 하루와 확장 전략

James Carr - A day in the life of 2,000 developers

작성자
Friendly rb
발행일
2025년 07월 01일

핵심 요약

  • 1 Stripe는 150만 개 이상의 Ruby 테스트와 2,500만 라인 이상의 코드를 가진 대규모 모노레포 환경에서 Sorbet 타입 체커, 스마트 테스트 선택, 자동화된 배포 시스템을 통해 개발 생산성과 안정성을 극대화합니다.
  • 2 테스트 시간을 99.5% 단축하는 변경 영향도 기반 테스트 선택 시스템과 통합 브랜치 전략으로 CI/CD 효율성을 높이고, Ruby 3.3 및 YJIT 도입으로 25%의 CPU 성능 향상을 달성했습니다.
  • 3 개발자의 '슬픔'을 제거하는 것을 목표로 정적 분석, QA 환경, 블루/그린 배포 및 트래픽 전환 등 다층적인 방어 체계를 구축하여 프로덕션 배포의 안정성과 속도를 확보합니다.

도입

본 발표는 Stripe의 Ruby 인프라 팀 소속 개발자인 James Carr가 Stripe에서의 개발자의 하루를 통해 대규모 Ruby 코드베이스를 어떻게 효과적으로 관리하고 확장하는지에 대한 통찰을 제공합니다. Stripe는 150만 개 이상의 Ruby 테스트와 2,500만 라인 이상의 코드를 단일 모노레포로 운영하며, 이는 단일 머신에서 실행 시 2개월이 소요될 만큼 방대한 규모입니다. 이러한 환경에서 개발 생산성과 시스템 안정성을 유지하기 위한 Stripe의 독특한 전략과 도구들이 소개됩니다.

Stripe는 결제 서비스를 넘어 다양한 금융 상품을 제공하며, 이 모든 서비스의 대부분은 Ruby로 구축되어 있습니다. 회사는 규모가 커지더라도 Ruby를 포기하지 않고 확장하기 위해 Ruby 인프라 팀을 운영하며 코드베이스의 확장성, 청결성, 버전 업그레이드 및 Gem 관리를 전담하고 있습니다.

대규모 코드베이스 디버깅 및 타입 체커 활용

개발자의 하루 시나리오를 통해 Stripe의 개발 프로세스가 설명됩니다. 루마니아 통화 추가 후 발생한 결제 금액 표시 오류를 디버깅하는 과정에서 다음 도구와 전략이 활용됩니다. * 내부 코드 검색 도구: 2,500만 라인 이상의 Ruby 코드에서 특정 문자열을 0.5초 만에 검색합니다. * Sorbet (타입 체커): VS Code의 LSP 모드에서 실행되어 ‘정의로 이동’과 같은 기능을 제공하며, 코드의 정확성을 검증합니다. 특히 enum 기능을 활용한 완전성(exhaustiveness) 검사를 통해 새로운 통화 추가 시 누락된 처리 로직을 자동으로 감지하여 프로덕션 배포 전 문제를 방지합니다. * 타입 체커의 이점: 생산성 향상, 개발자의 정신적 부담 감소, 프로덕션 버그 방지(5~10초 만에 오류 발견), 코드베이스를 더 작게 느끼게 함, 신규 입사자 온보딩 지원. * Sorbet 권장 사항: 소규모 프로젝트는 typed: true 모드로 시작하여 점진적으로 타입을 추가하는 것을 권장합니다.

CI/CD 및 모노레포 전략

Stripe의 CI/CD 파이프라인은 다음과 같은 특징을 가집니다. * 일관성 검사: Rust로 작성된 고속 Ruby 포맷터와 RuboCop을 사용하여 코드 일관성을 유지합니다. * 모노레포의 이점: 모든 코드를 단일 저장소에 두어 생산성을 극대화합니다. 이는 규모에 따른 문제를 전담 팀이 해결하고, 서비스 간의 원자적 변경을 용이하게 합니다. Ruby 코드의 99.9%가 단일 코드베이스에 있습니다. * 스마트 테스트 선택: 150만 개의 테스트를 매번 실행하는 비효율성을 해결하기 위해 변경된 파일에 영향을 받는 테스트만 선별적으로 실행합니다. Linux의 open syscall을 가로채 테스트 실행 중 접근한 파일을 추적하고, 이를 역추적하여 변경된 코드와 관련된 테스트만 실행합니다. 이를 통해 기능 브랜치에서 테스트 실행 시간을 99.5% 단축합니다 (150만 개 → 1,000~2,000개). * 탄력적인 CI 워커: Amazon Spot 인스턴스를 활용한 탄력적인 워커 풀을 통해 개발자 부하에 따라 CI 자원을 동적으로 조절합니다.

배포 자동화 및 안정성

Stripe는 높은 배포 빈도와 안정성을 위해 다음과 같은 시스템을 구축했습니다. * 통합 브랜치: 병합 전 최신 메인 브랜치 위에 변경 사항을 쌓아 CI를 재실행하여 병합 충돌 및 잠재적 오류를 방지합니다. 이로써 경쟁 상태(race window)를 2주에서 5분으로 단축합니다. * 자동 병합: CI 통과 및 동료 승인 시 PR이 자동으로 병합 및 배포되는 기능을 제공하여 개발자의 편의성을 높입니다. * 컨테이너 최적화: Kubernetes 기반으로 전환하며 서비스별 컨테이너를 구축하고, 도달 불가능한 코드를 제거하여 압축 이미지 크기를 수백 MB 절감하여 빌드 및 배포 속도를 향상시킵니다. * QA 환경: 고위험 변경 사항이나 로컬 테스트가 어려운 경우를 위해 QA 환경을 제공합니다. 과거 공유 환경에서 개별 개발자 전용 QA 환경으로 전환하여 병목 현상을 해소합니다. * 프로덕션 배포: 결제 서비스는 하루 20~25회 배포되며, 대부분의 시간은 트래픽 전환에 사용됩니다. 자동화된 헬스 체크를 통해 이상 감지 시 즉시 트래픽을 이전 버전으로 되돌리는 블루/그린 배포 및 점진적 트래픽 전환 전략을 사용합니다. * 다층 방어 체계: 타입 검사, CI, 사전 프로덕션 테스트, 트래픽 전환 등 여러 단계의 방어 체계를 통해 개발자의 두려움을 줄이고 5-9 수준의 가용성을 목표로 합니다.

프로덕션 환경 최적화

  • 상수 사전 로딩: 서비스 시작 시 모든 참조 가능한 Ruby 상수를 미리 로드하여 첫 요청의 지연 시간을 없앱니다.
  • Copy-on-Write (CoW): 워커 포크 시 Linux의 CoW 페이지 공유 기능을 활용하여 메모리 사용량을 최적화합니다.
  • Ruby 3.3 및 YJIT: 최근 Ruby 3.3으로 업그레이드했으며, Shopify의 YJIT 덕분에 CPU 사용량이 약 25% 감소하는 효과를 보았습니다.
  • 커뮤니티 참여: Ruby 버그를 조기에 발견하고 수정하기 위해 커뮤니티에 적극적으로 참여하고 있습니다.

결론

Stripe는 대규모 Ruby 코드베이스를 운영하면서 발생하는 복잡한 문제들을 혁신적인 접근 방식으로 해결하고 있습니다. Sorbet을 통한 정적 타입 검사, 변경 영향도 기반의 스마트 테스트 선택, 고도로 자동화된 CI/CD 파이프라인, 그리고 YJIT 도입을 통한 성능 최적화는 개발 생산성 향상과 시스템 안정성 확보에 크게 기여합니다. 특히, 개발자의 '슬픔'을 제거하고 온보딩 과정을 용이하게 하는 데 중점을 둔 Stripe의 문화는 기술적 해결책과 더불어 모범적인 대규모 개발 사례를 제시합니다. 이러한 경험은 Ruby 커뮤니티에도 긍정적인 영향을 미치며, Ruby가 대규모 서비스에서도 충분히 강력한 선택지가 될 수 있음을 입증합니다.

댓글 0

댓글 작성

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

아직 댓글이 없습니다

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