tech

Firebase를 대체할 오픈소스 프로젝트, Supabase

Supabase는 구글 Firebase를 엔터프라이즈 레벨에서도 사용 가능하도록 만든 오픈소스 프로젝트이다. 현재는 베타 서비스이다.

컴퓨터에 직접 설치하는 방식은 아니고, Firebase처럼 클라우드로 제공되는 서비스이다.

내가 Firebase를 사용하면서 아쉬웠던 건, 데이터가 많아졌을 때 인덱싱을 쉽게 적용시켜서 빠르게 레코드를 읽어오는 기능이 강력하진 않아서 데이터가 많은 앱에서는 사용하기 힘들다는 점이다.

Supabase?

Supabase 팀은 이 프로젝트를 이렇게 소개한다.

Create a backend in less than 2 minutes. Start your project with a Postgres Database, Authentication, instant APIs, and realtime subscriptions.

2분 안에 Postgres, 인증과 API, 실시간 구독을 구현할 수 있다는 얘기이다. 사실이라면 엄청나게 편리할 것 같다.

현재 회원가입은 Github OAuth로만 가능하고, 올해 12월까지 가입하는 유저들은 자동으로 2년 가량의 Base Tier 크레딧을 받을 수 있다.

가입하고 Organization과 Database를 만들고 프로비저닝이 완료될 때 까지 기다리면 이런 대시보드가 나온다.

데이터베이스에 대한 정보도 볼 수 있는데, 비밀번호를 제외하고 전부 자동으로 설정되고, 변경도 되지 않는다. 베타 버전이라 그런 것 같은데, 오히려 이름 짓느라 고민하지 않아도 되서 개인적으로는 좋았다.

Table 생성

테이블명과 PK를 설정하는 부분인데, PK는 4가지 타입으로 제공된다. 나는 uuid로 선택했다.

테이블과 PK를 설정하면 칼럼도 추가할 수 있는데 UI가 상당히 직관적이다.

아직은 베타 버전이라 현재는 웹사이트를 통해서만 Database, Table을 만들 수 있다.

API

사실 데이터베이스는 서버쪽에서 데이터베이스 설정이나 스키마, 레코드 Read/Write를 코드로 관리하기 편해야한다. ORM을 사용하는 이유이기도 하다.

클라이언트측 라이브러리는 현재 자바스크립트만 제공된다. 패키지 이름은 @supabase/supabase-js이고, 사용해보니 타입스크립트도 지원된다.

설치

$ yarn add @supabase/supabase-js
# npm i @supabase/supabase-js

사용법

npm으로 제공하는 라이브러리를 이용해 간단하게 인스턴스를 구현 가능하다.

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'https://siwwiuleewkpfbschahw.supabase.co'
const supabaseKey = process.env.SUPABASE_KEY as string
const supabase = createClient(supabaseUrl, supabaseKey)

레코드 쓰기

await supabase
  .from('User')
  .insert([{ some_column: 'someValue', other_column: 'otherValue' }])

레코드 읽기

let { data: User, error } = await supabase.from('User').select('*')

let { data: User, error } = await supabase
  .from('User')
  .select('some_column, other_column')
  .range(0, 9)

let { data: User, error } = await supabase.from('User').select(`
    some_column,
    other_table (
      foreign_key
    )
  `)

이벤트 훅

// insert
const User = supabase
  .from('User')
  .on('INSERT', payload => {
    console.log('Change received!', payload)
  })
  .subscribe()

// specific row
const User = supabase
  .from('User')
  .eq('column_name', 'someValue')
  .on('*', payload => {
    console.log('Change received!', payload)
  })
  .subscribe()

기존 ORM에 익숙하다면 별 다른 문서없이 바로 이해가 될 정도로 직관적이다.

읽기 쓰기 이외에도 필터링, 수정 삭제 등 필수적으로 제공되야 할 기능들이 제공된다.

GraphQL vs Supabase

GraphQL에 익숙한 사람들을 위해 gql과 비슷하게 사용할 수 있는 인터페이스도 제공된다.

현재는 GraphQL만큼 강력하진 않겠지만, 그에 준하는 기능들을 가지게 된다면 엄청난 장점이 될 것 같다.

// graphql
const { loading, error, data } = useQuery(gql`
  query GetDogs {
    dogs {
      id
      breed
      owner {
        id
        name
      }
    }
  }
`)

// supabase
const { data, error } = await supabase.from('dogs').select(`
      id, breed,
      owner (id, name)
  `)

회원 관리

Firebase나 Supabase같은 툴들이 가진 가장 큰 장점 중 하나는 이 회원 관리 기능이다.

회원 가입

코드 3줄이면 비밀번호 암호화 알고리즘을 구현할 필요없이 회원을 가입시킬 수 있다.

let { user, error } = await supabase.auth.signUp({
  email: 'someone@email.com',
  password: 'nshOpnIDkwgnouwsVLnk'
})

로그인

Supabase는 유저가 회원가입하면 고유한 id를 만들고, 서버가 켜져 있는 동안 어디서든 supabase.auth.user() 를 호출하는 것으로 유저 정보를 가져올 수 있다.

물론 이 id를 다른 테이블에서 FK로 넣어줄 수 있다.

let { user, error } = await supabase.auth.signIn({
  email: 'someone@email.com'
})

// OAuth
let { user, error } = await supabase.auth.signIn({
  provider: 'github'
})

기타 편의 기능

// 비밀번호 재설정 이메일 보내기
let { data, error } = await supabase.auth.api.resetPasswordForEmail(email)

// 현재 세션에 저장된 유저 정보 가져오기
const user = supabase.auth.user()

// 초대 메일 보내기
let { user, error } = await supabase.auth.api.inviteUserByEmail(
  email: 'someone@email.com'
)

초대 메일을 보내는 기능을 써봤더니, 아래처럼 메일이 도착했다.

저 localhost로 보이는 부분은 웹 대시보드에서 변경 가능하다.

추가로 Firebase와 동일하게 회원 관리를 위한 이메일 템플릿은 웹 대시보드에서 수정이 가능하다.

SQL

내부적으로 PostgreSQL을 사용하기 때문에, 이렇게 웹 브라우저에서 SQL을 실행시키는 인터페이스도 제공된다.

Quick start

SQL Editor 탭에서는 몇 가지 예제를 제공하고 있다.

아래 예제는 Supabase에서 만들어 둔 Todo list 앱 개발을 위한 테이블이다. 테이블 읽기/쓰기 정책을 SQL로 설정할 수 있다.

--
-- For use with https://github.com/supabase/supabase/tree/master/examples/todo-next-js
--

create table todos (
  id bigint generated by default as identity primary key,
  user_id uuid references auth.users not null,
  task text check (char_length(task) > 3),
  is_complete boolean default false,
  inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
);

alter table todos enable row level security;

create policy "Individuals can create todos." on todos for
    insert with check (auth.uid() = user_id);

create policy "Individuals can view their own todos. " on todos for
    select using (auth.uid() = user_id);

create policy "Individuals can update their own todos." on todos for
    update using (auth.uid() = user_id);

create policy "Individuals can delete their own todos." on todos for
    delete using (auth.uid() = user_id);

마무리

Supabase는 런칭 후 9달 동안 약 3,000개 가량의 데이터베이스가 만들어졌다고 한다. 그리고 현재 YC와 Mozila에게 총 약 60억의 시드 투자를 받았다.

직접 간단하게 훑어봤는데 아직은 기업에서 쓰긴 기능들이 약하지만, 10GB 용량이 무료로 제공되고 인터페이스가 간단하기 때문에 Firebase 대신에 공부나 토이 프로젝트로 쓰기는 좋을 것 같다.

곧 Storage와 Function 기능을 출시할 예정이라고 한다. 앞으로 더 많은 기능이 제공된다면 정말 강력한 툴이 될 것 같다.

참고