BookSalon 개발기 - Firebase에서 Supabase로, 그리고 70% 번들 최적화까지
독서 커뮤니티 플랫폼 BookSalon의 Firebase에서 Supabase 마이그레이션 과정, RLS 정책 설계의 시행착오, 그리고 번들 최적화 이야기를 공유합니다.
BookSalon은 SidequestLab에서 가장 많은 교훈을 남긴 프로젝트입니다. 책을 중심으로 지식을 공유하고 토론하는 커뮤니티 플랫폼을 만드는 과정에서, 인프라 마이그레이션부터 번들 최적화까지 수많은 도전을 겪었습니다. 이 글에서는 그 여정을 솔직하게 공유합니다.
BookSalon이란?
BookSalon은 독서를 혼자가 아닌 함께 하는 경험으로 바꾸는 플랫폼입니다. 책에 대한 리뷰를 작성하고, 다른 독자들과 토론하며, 독서 기록을 관리할 수 있습니다. 단순한 독서 기록 앱이 아니라, 책을 매개로 한 지식 공유 커뮤니티를 지향합니다.
주요 기능은 다음과 같습니다.
- 도서 검색 및 상세 정보 조회
- 리뷰 작성 및 별점 평가
- 사용자 프로필 및 독서 통계
- 독서 모임 관리
Firebase에서 시작한 이유
프로젝트 초기에는 Firebase를 선택했습니다. 이유는 명확했습니다.
- 빠른 프로토타이핑: Firebase의 실시간 데이터베이스와 인증은 MVP를 빠르게 만들기에 최적이었습니다
- 서버리스 아키텍처: 별도의 백엔드 서버 없이도 핵심 기능을 구현할 수 있었습니다
- 익숙한 도구: 팀에서 이미 다른 프로젝트에서 Firebase를 사용한 경험이 있었습니다
Firebase로 첫 버전을 빠르게 완성했고, 핵심 기능이 동작하는 것을 확인했습니다.
Supabase로 전환한 이유
하지만 기능이 확장되면서 Firebase의 한계가 드러나기 시작했습니다.
비용 구조의 문제: Firebase의 읽기/쓰기 과금 모델은 커뮤니티 플랫폼에 불리했습니다. 사용자가 늘어날수록 비용이 예측하기 어려운 방식으로 증가했습니다.
RLS(Row Level Security)의 부재: Firebase Security Rules도 강력하지만, PostgreSQL 기반의 RLS는 데이터베이스 레벨에서 보안을 보장하여 더 견고한 접근 제어가 가능했습니다.
확장성: SQL 기반의 관계형 데이터베이스는 복잡한 쿼리와 데이터 관계를 다루기에 훨씬 유연했습니다. 리뷰, 사용자, 도서 간의 관계를 자연스럽게 표현할 수 있었습니다.
마이그레이션의 도전
Supabase로의 전환은 단순한 데이터 이동이 아니었습니다. 12개의 서비스를 전면 교체하는 대규모 작업이었습니다.
12개 서비스 전면 전환
Firebase SDK에 의존하던 인증, 데이터 조회, 파일 저장 등 모든 서비스를 Supabase 클라이언트 기반으로 재작성했습니다. 기존 서비스를 "유지"하면서 새 서비스를 "추가"하는 방식이 아니라, 완전한 교체를 원칙으로 삼았습니다. 이는 나중에 두 시스템이 공존하며 생기는 혼란을 방지하기 위한 결정이었습니다.
RLS 정책 설계 - 3차 수정까지
RLS 정책 설계는 예상보다 훨씬 까다로웠습니다. 무려 3차에 걸친 수정을 거쳐야 했습니다.
1차 설계: 기본적인 CRUD 권한을 설정했지만, 실제 사용 시나리오를 충분히 고려하지 못했습니다. 자신의 리뷰만 수정할 수 있어야 하는데, 다른 사용자의 리뷰도 수정 가능한 상태였습니다.
2차 수정: 권한 체계를 세분화했지만, auth_id와 users.id의 불일치 문제를 발견했습니다. Supabase Auth에서 제공하는 auth.uid()와 애플리케이션 레벨의 사용자 ID가 달라서, RLS 정책이 의도대로 동작하지 않는 경우가 발생했습니다.
3차 수정: auth_id를 기준으로 모든 RLS 정책을 재설계하고, users 테이블에 auth_id 컬럼을 추가하여 명확한 매핑을 구현했습니다. 이 과정에서 E2E 테스트를 도입하여 모든 권한 시나리오를 자동으로 검증할 수 있게 되었고, 최종적으로 E2E 테스트 100% 통과를 달성했습니다.
auth_id vs users.id 문제
이 문제는 특별히 언급할 가치가 있습니다. Supabase에서 auth.users 테이블의 ID와 애플리케이션의 public.users 테이블의 ID는 별개입니다. 처음에는 이 두 ID를 혼용하면서 "로그인은 되는데 데이터가 안 보이는" 현상이 발생했습니다.
해결책은 public.users 테이블에 auth_id 필드를 추가하고, 모든 RLS 정책에서 auth.uid() = users.auth_id 조건을 사용하는 것이었습니다. 단순해 보이지만, 이 깨달음에 도달하기까지 상당한 디버깅 시간이 소요되었습니다.
React Router v7과 번들 최적화
Supabase 마이그레이션과 함께 프론트엔드 아키텍처도 개선했습니다.
SPA에서 진정한 웹앱으로
React Router v7을 도입하면서 단순한 SPA(Single Page Application)에서 서버 사이드 렌더링이 가능한 웹 애플리케이션으로 전환했습니다. 이를 통해 SEO 개선, 초기 로딩 속도 향상, 그리고 더 나은 사용자 경험을 달성할 수 있었습니다.
927KB에서 278KB로
번들 분석을 통해 불필요한 의존성과 중복 코드를 식별하고 제거했습니다. 그 결과, 초기 번들 크기를 927KB에서 278KB로 약 70% 감소시켰습니다.
주요 최적화 항목은 다음과 같습니다.
- 사용하지 않는 Firebase SDK 완전 제거
- 코드 스플리팅을 통한 라우트별 지연 로딩
- 트리 쉐이킹 최적화
- 이미지 및 에셋 최적화
얻은 교훈
BookSalon 프로젝트를 통해 SidequestLab 전체에 적용되는 중요한 원칙들이 탄생했습니다.
인프라 우선 원칙: 코드를 작성하기 전에 인프라(데이터베이스, 환경변수, 배포 환경)를 먼저 구성해야 합니다. 인프라 없이 코드만 작성하면 나중에 통합 과정에서 더 큰 비용이 발생합니다.
QA 필수 규칙: 코드 리뷰만으로는 충분하지 않습니다. 실환경에서의 QA 검증을 거치지 않고 배포하면 예상치 못한 장애가 발생합니다. BookSalon의 RLS 문제는 이 교훈을 뼈저리게 가르쳐 주었습니다.
완전 교체 원칙: 마이그레이션 시 신규 시스템을 "추가"하는 것이 아니라 기존 시스템을 "교체"해야 합니다. 두 시스템의 공존은 혼란과 버그의 원인이 됩니다.
향후 계획
BookSalon은 현재 v0.4 단계이며, v1.0 정식 출시를 향해 나아가고 있습니다.
- 커뮤니티 기능 강화: 독서 모임 생성 및 관리, 토론 스레드
- OAuth 연동: Google, Kakao 소셜 로그인 지원
- v1.0 정식 출시: 안정성 검증 후 공개 론칭
BookSalon이 책을 좋아하는 분들에게 의미 있는 공간이 되기를 바랍니다.
SidequestLab 드림