GraphQL Rails 앱의 데이터베이스 부하 분산: 월간 3천만 사용자 서비스를 무중단으로

GraphQL×Railsアプリのデータベース負荷分散 - 月間3,000万人利用サービスを無停止で / Koya Masuda - Kaigi on Rails 2025

작성자
Kaigi on Rails
발행일
2025년 11월 25일

핵심 요약

  • 1 GraphQL Rails 애플리케이션에서 데이터베이스 부하 분산은 쿼리와 뮤테이션의 책임 분리를 통해 효과적으로 구현할 수 있습니다.
  • 2 기술 도입 시에는 조직의 역량과 변화에 대한 저항성을 고려하여 지속 가능한 개발 속도를 유지하는 관점이 중요합니다.
  • 3 무중단 서비스 릴리스를 위해 화이트리스트 방식으로 작은 범위부터 적용하고 성공 경험을 바탕으로 점진적 확장을 통해 위험을 관리해야 합니다.

도입

MyBest는 TV 방송으로 인한 트래픽 급증으로 서비스 중단 사태를 겪은 후, 월간 3천만 명의 사용자에게 안정적인 서비스를 제공하기 위해 데이터베이스 부하 분산 도입을 결정했습니다. 기존 캐싱 전략의 한계를 인식하고 데이터베이스 스케일 아웃의 필요성을 강조하며, GraphQL Rails 애플리케이션 환경에서 Read/Write Splitting을 구현하는 과정과 고려사항을 다룹니다. 본 발표는 기술적 도전과 조직적 관점을 아우르는 실질적인 해결 방안을 제시합니다.

데이터베이스 부하 분산 기술 선택

MyBest는 미들웨어 방식과 애플리케이션 방식 중 애플리케이션 방식(Rails 6.0 이상 다중 데이터베이스 지원)을 선택했습니다. 이는 Rails와 Ruby에 대한 내부 엔지니어들의 숙련도를 활용하여 초기 구현 및 장기적인 유지보수 용이성을 확보하기 위함이었습니다. 조직의 케이퍼빌리티를 고려한 선택으로, 새로운 의존성 추가 없이 Rails/Ruby 환경 내에서 제어 가능한 이점을 강조했습니다.

GraphQL과 Rails 통합 시 문제점 및 해결책

  1. HTTP 메서드 기반 DB 분기 불가: Rails의 기본 Read/Write Splitting 기능은 HTTP GET/HEAD 요청을 Reader로, POST/PUT/DELETE/PATCH 요청을 Writer로 자동 분기합니다. 그러나 GraphQL은 모든 요청을 HTTP POST로 처리하므로 Rails의 기본 기능 활용이 불가능했습니다.
    • 해결: CQRS(Command Query Responsibility Segregation) 패턴을 참고하여 GraphQL Query는 Reader, Mutation은 Writer로 명확하게 분리하는 전략을 채택했습니다. graphql-ruby gem의 execute_multiplex 메서드를 후킹하여, 요청 내 모든 오퍼레이션이 Query일 경우에만 Reader DB에 연결하도록 구현했습니다.
  2. Query 내 데이터 업데이트 가능성: GraphQL Query는 원칙적으로 데이터를 조회해야 하지만, 실제 구현에서는 Query 내에서 데이터 업데이트(예: find_or_create_by)가 발생할 수 있어 Read-only 보장이 어려웠습니다.
    • 해결: 화이트리스트 방식의 Read-Write Splitting을 도입했습니다. Read-only가 확실한 Query만 화이트리스트에 등록하고, 해당 Query만 Reader DB에 연결하도록 했습니다. 또한, ActiveSupport::Instrumentation을 활용하여 개발 및 스테이징 환경에서 sql.active_record 이벤트를 구독, Query 실행 중 업데이트 SQL이 발생하면 예외를 발생시켜 부작용 있는 Query를 사전에 방지했습니다.

무중단 릴리스 및 점진적 도입 전략

  • 초기 적용: 세 가지 GraphQL 스키마(내부 관리 화면, 웹, 모바일 앱) 중 사용 시간대가 제한적인 내부 관리 화면 스키마부터 Read-Write Splitting을 적용했습니다.

  • 점진적 확장: 화이트리스트 방식 덕분에 관리 화면 내에서도 특정 액션에 연결된 Query부터 소규모로 도입하여 성공 경험을 쌓았습니다. 이후 Datadog의 텔레메트리 데이터를 분석하여 총 실행 시간이 긴 Query부터 우선순위를 정해 화이트리스트에 추가하며 점진적으로 확장했습니다.

  • 결과: 이러한 노력을 통해 데이터베이스 CPU 사용률이 기존 70%에서 20% 전후로 안정화되었으며, 데이터베이스 스케일 아웃 문제도 성공적으로 해결되었습니다.

결론

MyBest는 GraphQL Rails 애플리케이션에 데이터베이스 Read-Write Splitting을 성공적으로 도입하여 월간 3천만 사용자의 트래픽을 안정적으로 처리할 수 있게 되었습니다. 쿼리와 뮤테이션의 책임 분리, `graphql-ruby` gem의 활용, `ActiveSupport::Instrumentation`을 통한 개발 시점의 검증, 화이트리스트 기반의 점진적 적용, 그리고 조직의 역량을 고려한 기술 선택이 핵심 성공 요인으로 작용했습니다. 이 사례는 고성능 서비스 운영을 위한 기술적 도전과 안정적인 릴리스 프로세스의 중요성을 시사하며, 유사한 환경에 처한 개발자들에게 실질적인 가이드라인을 제공합니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

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