Persona의 Rails 아키텍처 확장 여정: 복잡성 관리와 단순성으로의 회귀

RailsConf 2025 Simplifying at Scale: 7 Years of Rails Architecture at Persona by Alex Coomans

작성자
Ruby Central
발행일
2025년 07월 24일

핵심 요약

  • 1 Persona는 Rails 애플리케이션의 초기 단순성에서 벗어나, 현대적인 프론트엔드, Kubernetes, 그리고 데이터베이스 샤딩을 통해 급격한 성장을 지원했습니다.
  • 2 Google App Engine에서 Kubernetes로의 전환은 유연성과 제어를 제공했지만, 운영 복잡성을 증가시켰으며, 대규모 MySQL 테이블 및 Active Storage와 같은 데이터베이스 스케일링 문제가 발생했습니다.
  • 3 궁극적으로 Persona는 '스택(Stacks)' 아키텍처를 도입하여 복잡성을 통합하고 재사용 가능한 단일 배포 모델로 회귀함으로써 예측 가능한 성장을 위한 단순성을 추구했습니다.

도입

본 강연은 Persona가 초기 아이디어 단계에서 완벽하게 기능하는 Rails 애플리케이션 및 제품으로 발전시키면서 겪었던 아키텍처 확장 여정을 다룹니다. Persona는 온보딩, 규정 준수, 사기 방지 등을 포함하는 올인원 신원 확인 플랫폼으로, 다양한 산업 분야의 고객 요구사항을 충족하기 위해 높은 유연성과 확장성을 필요로 합니다. 이러한 요구사항은 아키텍처의 모든 부분에 영향을 미쳤으며, Rails 애플리케이션이 어떻게 진화해왔는지에 대한 구체적인 경험을 공유합니다.

Persona는 Rails 5.2를 기반으로 Google App Engine에서 시작하여 빠른 개발 및 배포의 이점을 누렸습니다. 초기에는 자산 파이프라인(Asset Pipeline)의 도입(Rails 3.1)으로 자산 관리의 구조화가 가능해졌으나, jQuery 기반의 프론트엔드 환경에서 React 및 TypeScript와 같은 현대적인 프론트엔드 프레임워크로의 전환이 필요해졌습니다. 이는 Webpacker gem의 도입으로 이어졌지만, Webpacker는 프론트엔드 혁신의 빠른 속도를 따라가지 못하며 디버깅 난이도, 느린 피드백 루프, 그리고 새로운 도구에 대한 지연된 지원과 같은 문제점을 야기했습니다. 결국 Persona는 Webpacker에서 Vite로 전환하며, Rails의 기본 설정에 얽매이지 않고 필요한 도구를 유연하게 도입하는 접근 방식을 취했습니다.

성장함에 따라 Google App Engine의 한계에 부딪혔고, 더 큰 유연성을 위해 Kubernetes(GKE)로의 마이그레이션을 단행했습니다. App Engine이 제공했던 단순성과 추상화는 사라졌지만, Kubernetes는 컴퓨팅, 스케일링, 네트워킹, 관측 가능성(Observability)에 대한 세밀한 제어권을 제공했습니다. 특히, App Engine이 요청-응답 웹 트래픽에 중점을 두어 Sidekiq과 같은 백그라운드 작업 처리에 어려움이 있었던 반면, Kubernetes는 수평 Pod 오토스케일러(Horizontal Pod Autoscaler)를 통해 버스트(bursty) 워크로드에 대한 효율적인 스케일링을 가능하게 했습니다. 하지만 이러한 제어권은 DNS, TLS, 헬스 체크, 방화벽 규칙 등 더 많은 운영 책임을 수반했습니다.

데이터베이스 계층에서도 상당한 압력을 받았습니다. MySQL 클러스터의 부하를 분산하기 위해 2022년 중반부터 데이터베이스 샤딩을 시작했습니다. Rails 6.0은 connects_toconnected_to API를 통해 모델별 데이터베이스 연결을 지원하여 수직 샤딩(Vertical Sharding)의 기반을 마련했고, Rails 6.1에서는 네이티브 수평 샤딩(Horizontal Sharding)을 도입했습니다. 그러나 기존의 대규모 Rails 애플리케이션에 샤딩을 적용하는 것은 코드베이스 전반에 걸쳐 샤드 컨텍스트를 주입해야 하는 복잡한 작업이었습니다. 특히 웹 요청의 경우, API 키와 같은 라우팅 키를 기반으로 올바른 샤드를 찾아야 했으며, 이를 위해 MongoDB를 기반으로 하는 고가용성의 중앙 집중식 조회 테이블을 구축하여 전 세계적으로 분산된 읽기 복제본을 통해 낮은 지연 시간으로 샤드 라우팅을 수행했습니다.

대규모 MySQL 테이블은 느린 쿼리, 고통스러운 마이그레이션, 예측 불가능한 쿼리 계획, 그리고 단순한 스키마 변경에서도 발생하는 운영 위험을 초래했습니다. 샤딩이 수평 확장에 도움이 되지만, 이러한 문제는 여전히 존재하며, 오히려 여러 샤드에 걸쳐 관리해야 하는 복잡성을 더합니다. MySQL 8.0의 인스턴트 DDL 작업이 일부 개선을 가져왔지만, 인덱스 생성과 같은 작업은 여전히 전체 테이블 재구축을 필요로 합니다. 따라서 데이터의 형태뿐만 아니라 데이터가 어떻게 쿼리되고 필터링되며 조인될지에 대한 ‘액세스 패턴 설계’가 중요해졌습니다. 또한, Active Storage와 같이 Rails의 핵심 컴포넌트조차도 대규모 테이블에서 성능 문제를 일으킬 수 있어, Persona는 Active Storage에서 Shrine으로 마이그레이션을 진행했습니다. MongoDB, Elastic Search, Redis와 같은 다른 데이터 스토어 또한 규모가 커지면서 각자의 도전 과제를 안고 있었습니다.

이러한 복잡성 속에서 Persona는 ‘스택(Stacks)’이라는 프로젝트를 통해 의도적인 단순성으로의 회귀를 시도했습니다. 이는 여러 클러스터와 데이터베이스에 분산된 복잡성을 단일하고 자체 포함된 배포 모델로 통합하고 이를 반복적으로 복제하는 방식입니다. 각 스택은 여러 테넌트를 호스팅할 수 있지만, 스택 간에는 강력한 경계가 존재하여 단일 테넌시의 이점(격리, 영향 범위 축소, 운영 유연성)을 유지하면서도 각 고객마다 새로운 환경을 구축하는 오버헤드를 줄였습니다. 요청 라우팅은 데이터베이스 샤딩과 유사하게, 에지(edge) 위치에서 요청을 검사하고 로컬 캐시 및 MongoDB 조회 테이블을 통해 올바른 스택으로 즉시 라우팅하여 고객 구현에 변경 없이 백엔드 인프라의 확장성과 안정성을 확보했습니다.

결론

Persona의 7년간의 Rails 확장 여정은 복잡성이 불가피하지만, 어디에 그 복잡성을 둘지 선택할 수 있다는 중요한 교훈을 주었습니다. Rails는 훌륭한 기본값을 제공하지만, 이를 제약으로 간주하지 않고 필요할 때 과감히 외부 솔루션을 도입하는 유연성이 중요했습니다. 가능한 곳에서는 단순화하고, 반드시 확장해야 하는 곳에서는 확장하며, 필요할 때 기존의 틀에서 벗어나는 용기가 성공적인 아키텍처 진화의 핵심이었습니다. 이러한 경험들은 Rails를 확장하는 모든 개발자에게 유용한 통찰력을 제공합니다.

댓글 0

댓글 작성

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

아직 댓글이 없습니다

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