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_to
및 connected_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 조회 테이블을 통해 올바른 스택으로 즉시 라우팅하여 고객 구현에 변경 없이 백엔드 인프라의 확장성과 안정성을 확보했습니다.