마이루틴의 Firebase 마이그레이션 이야기 - 왜 했는가?
— 개발, Firebase, 마이루틴, 회고 — 13 min read
약 1년 전 마이루틴의 모바일 앱을 런칭하는 과정에서 서비스의 백엔드를 전부 Firebase로 마이그레이션했다. 기억이 낡기 전에 그 과정을 기록하고 회고하고자 한다.
이 회고는 두 편의 글로 나눌 예정이다. 첫번째 글인 이 글에서는 왜 Firebase로 마이그레이션을 했는지에 대해서 이야기한다. 두번째 글에서는 어떻게 Firebase 마이그레이션이 이루어졌는지를 이야기한다.
두번째 글을 언제 쓰게 될 지는 모르겠다.
소프트웨어는 유연해야한다. 소프트웨어가 유연하기 때문에 우리는 시장 요구사항과 배움을 제품에 빠르게 적용할 수 있다. 특히 스타트업은 빠르기가 사실상 유일한 강점이기 때문에, 스타트업 제품으로써 소프트웨어에 기대되는 유연함은 일반적인 환경에서 요구되는 수준보다 더 높아야 한다고 생각한다.
소프트웨어가 유연하다는 것은 어떤 것일까? 가장 먼저 떠올릴 수 있는 것은 구현이 유연하다는 것이다. 구현이 유연하면 코드를 고치기 쉽다. 소프트웨어에 새 기능을 추가할 때, 기존 기능을 수정할 때, 버그를 고칠 때 얼마나 적은 비용으로 그 일을 해낼 수 있는지를 의미한다. 또 다른 관점에서, 자원이 유연하다는 것을 생각해볼 수 있다. 자원이 유연하다는 것은 환경의 변화에 따라 시스템의 특정 부분에 비용과 관심을 집중하는 일이 얼마나 자유롭게 이루어질 수 있는지를 나타낸다. 서비스에 트래픽이 몰릴 때, 시스템의 처리량을 쉽게 늘릴 수 있으면 자원이 유연하다는 것이다.
사람들은 종종 소프트웨어의 유연함이 기술 스택과 아키텍처 혹은 디자인 패턴으로 결정된다고 오해한다. 물론 좋은 기술 스택과 좋은 아키텍처는 소프트웨어를 유연하게 만드는데 도움을 준다. 하지만 소프트웨어의 유연함을 결정하는 가장 큰 요소는 바로 그 소프트웨어를 만드는 개발자, 개발팀이다. 사람과 기술 스택, 아키텍처를 함께 볼 때 소프트웨어의 유연함을 확인할 수 있다.
우리 팀은 언제나 유연한 소프트웨어를 만들기 위해 최선을 다했다. 마이루틴의 첫 MVP는 React 웹 앱과 node.js API 서버로 구성되었고 AWS 인프라로 서비스했다. 이 때의 유연함은 사실 거의 대부분 이 소프트웨어를 만드는 개발팀 그 자체에서 나왔다고 해도 과언이 아니었다. 3년동안 함께 일해온, 3명의 풀스택 개발자로 구성된 개발팀은 모두가 팀에서 사용하는 스택을 기본적인 수준 이상으로 이해하고 사용할 수 있었고, 덕분에 프로젝트의 진행과정에서 병목이 발생할 때 빠르게 해소할 수 있었다.
탁월하게 설계했다고 자신할 수는 없지만, 주어진 환경에서 필요한 요구조건들을 달성할 수 있고, 무엇보다도 지속적으로 발전가능하다는 믿음이 있었다. 미래에 문제가 생긴다면 그 때에 충분히 빠르게 고칠 수 있다고 생각했다.
그리고 시간이 지나 (거의) 모든것이 바뀌고, 비즈니스가 새로운 환경에 놓이게 되었다. 기존 개발자 중 두 명이 나가고 세 명의 주니어가 들어왔다. 이 중 두 명은 이제 커리어를 시작하는 단계였다. 비즈니스의 발전 속도만큼 개발팀이 빠르게 성장해야했다.
새로운 조건 속에서 기존 구조는 유연하지 못함이 드러나게 되었다. 매 스프린트 주기마다 API와 클라이언트 중 어느 한 쪽에 예측하지 못하는 병목이 발생하고, 그 병목에 유연하게 대응하기가 어려웠다. 양 스택을 자유롭게 오가며 작업할 수 있는 사람이 없었기 때문이다. 그리고 이로인해 개발자가 다른 누군가에게 자신의 배움을 나누기 어려웠다. 예컨대 API를 개발하는 주니어 개발자와 페어 프로그래밍을 할 수 있는 사람이 나밖에 없었다. 또 사용자 수가 늘어나면서 종종 인프라를 다듬을 필요가 있었는데, 이 역시도 예상치 못한 병목이었다. 여기다 더해서 모바일 앱을 런칭한다면, 설령 크로스 플랫폼 앱이라고 할지라도 전체 시스템 관리 비용이 더욱 크게 늘어날 것이 뻔했다. 자원의 유연함이 극도로 떨어진 셈이다.
우리가 기존에 선택한 기술이나 아키텍처에 문제가 있었던 것은 아니다. 오히려 우리의 아키텍처는 뒤에 이어질 백엔드 마이그레이션의 비용을 효과적으로 억제하는 좋은 역할을 수행했다. 단지 소프트웨어를 만드는 사람이 바뀌었기 때문에 그에 맞춰 적응할 필요가 있었을 뿐인 것이다. 새로운 환경에서 클라이언트-API 구조 자체가 가장 큰 병목지점이라고 느꼈고, 자연스럽게 백엔드 코드를 대체할 수 있는 Firebase에 대해 생각해보기 시작했다.
Firebase를 도입하는 것은 굉장히 어려운 결정이었다. Firebase는 몇 번의 사이드프로젝트에서 요긴하게 써먹어본 적이 있었다. 하지만 프로덕션 환경에서 일부 기능이 아닌 시스템 전체를 Firebase로 구현해본 적은 없었고, 주변에서 사례를 찾기도 어려웠다. 특히나 이렇게 기존에 존재하던 시스템을 파이어베이스로 마이그레이션 한 사례는 정말 찾기 어려웠다. 꽤 오랜 기간에 걸쳐서 숙고하고 결정했다.
Firebase를 선택하면 다음과 같은 위험에 노출될 것이다:
- 커뮤니티의 사례를 풍부하게 얻기 어려울 것이다.
- 락인되기 쉽고, 락인에서 벗어날 때 큰 비용이 발생할 것이다.
- 우리 시스템이 Firebase SDK의 제약사항에 직접적인 영향을 받을 것이다.
- NoSQL(Firestore)에 대한 경험이 부족하여 데이터베이스의 완성도가 낮을 것이다.
- API의 비즈니스 로직을 클라이언트로 포팅하는 과정에서 큰 비용이 발생할 것이다.
Firebase를 선택하면 다음과 같은 이득을 얻을 것이다:
- API/클라이언트/인프라 병목이 원천적으로 없어진다.
- Firebase SDK의 완성도가 높아서 기초적인 구현을 쉽게 위임할 수 있다.
- 모두가 같은 코드베이스에서 작업함으로써 배움의 기회가 늘어난다.
위험은 다음과 같이 회피할 수 있을 것이다:
- 커뮤니티의 사례가 부족하지만, 공식 문서의 퀄리티가 훌륭하다.
- 락인이 되기 쉽지만, 일단 비즈니스가 그 수준으로 성장하는 것이 먼저이고 좋은 아키텍처로 락인 비용을 잘 억제할 수 있을 것이다.
- 극복 불가능한 Firebase 제약사항이 생긴다면, 그 부분만 마이크로서비스로 구현하면 될 것이다.
- 당장 데이터베이스의 완성도가 낮더라도, NoSQL의 특성상 미래에 개선하기 쉬울 것이다.
- API 로직을 포팅해야하지만, 서 비스 전체의 비즈니스 로직에서 API가 차지하는 비중이 크지 않아서 그 양이 많지 않다.
다시한번 강조하지만 클라이언트-API 구조에 문제가 있다거나, node.js나 RDBMS보다 Firebase가 좋아서 이주한 것이 아니다. 단지 개발팀이 새롭게 처한 환경에 적응을 하는 과정에서, 현재의 문제를 해결하는 최적의 기술이 Firebase라는 것을 인지한 것이다.
결국 Firebase를 도입함으로써 얻을 수 있는 이득이 리스크보다 크다고 생각했고, 마이그레이션을 하기로 결정했다. 이 결정 과정 내내 팀의 다른 동료들에게 내 생각이 어떻게 흘러가는지 최대한 투명하게 드러내려고 노력했다. 이 마이그레이션에 들어갈 막대한 비용을 생각하면 전사적인 이해와 납득이 필요하다고 생각했고, 그러기위해 개발팀이 언제나 제품과 비즈니스의 지속적인 발전을 위해 최선의 의사결정을 하고 있다는 믿음을 줘야했기 때문이다.
Firebase 마이그레이션 이후 1년이 흐른 지금, 결과적으로 아주 만족스러운 상황을 보내고 있다. 4명의 개발자가 모두 같은 코드베이스에서 작업하며 이야기하고 있다. 작업 스프린트에서 특정 레이어/도메인의 병목이 전혀 존재하지 않는다. Firebase SDK를 통해 실시간 데이터 스트림을 쉽게 구현했다. DAU가 주마다 10%-20%씩 늘고 있지만 인프라에 대해 아무런 걱정을 안 하고 있다. 아직까지 Firebase의 제약사항으로 구현에 문제가 된 적은 없다.
또 다시한번 강조하지만, 이 글은 Firebase를 설명하고 도입을 권유하는 글이 아니다. 마이루틴이 Firebase를 도입할 때 처했던 상황을 설명하는 글이다. 제품의 기술 스택을 고민하는 사람과 팀에게 참고가 될 수 있으면 좋겠다.