Firebase를 대체하는 Supabase

깡스·2023년 7월 25일
1
post-thumbnail

저번 프로젝트를 firebase로 작업하면서 생각보다 불편한 부분이 많이 있었기에, 이를 대체할 서비스를 탐색하던 중, supabase를 알게되었고, 이를 사용해본 경험을 남기려 합니다.

먼저 가장 크게 느꼈던 점은 firebasedocs도 잘 정리되어 있다 느꼈지만, supabasedocs가 더 잘 구성되어 있다 느껴졌습니다.

로그인과 회원가입

첫번째로 로그인 회원가입 기능을 구현할 때, 해당 유저의 닉네임, 이메일과 같은 프로필 정보를 DB에 저장해주어야 합니다.

firebase의 경우 회원가입이 완료된 후 별도의 api를 호출해 해당 유저의 정보를 저장해주는 과정이 필요하지만 supbase는 그렇지 않습니다.

supabase에서 제공하는 triggerfunction기능을 활용해 회원가입이 이루어지면, 저장되어있는 function을 실행시켜, 자동으로 유저의 입력 정보를 DB에 저장할 수 있습니다.

const { data, error } = await supabase.auth.signUp({
  email: 'example@email.com',
  password: 'example-password',
  options: {
    data: {
      first_name: 'John',
      age: 27,
    },
  },
})

위와 같은 정보로 유저가 회원가입을 했을 때, 자동으로 저장되도록

-- inserts a row into public.profiles
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
  insert into public.profiles (id)
  values (new.id);
  return new;
end;
$$;

-- trigger the function every time a user is created
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();

auth의 user테이블에 유저가 등록되었을 때, handle_new_user()의 sql이 실행되도록 trigger를 등록할 수 있으며, CLI를 통한 등록이 어렵게 느껴질 경우, UI가 존재하는 등록 방식도 존재하기에 손쉽게 적용할 수 있었습니다.

위의 sql에서는 기본 데이터인 id에만 접근하고 있으나, 유저가 입력한 first_name, age와 같은 meta_data의 경우

begin
  insert into public.profiles(id, email, first_name)
  values(new.id, new.email, new.raw_user_meta_data->>'first_name');
  return new;
end;

이렇게 meta_data에 접근할 수 있습니다. 문서를 통해서도 확인하실 수 있습니다.

그 밖에도 버튼 하나만으로 이메일 인증을 요구할 수 있고, 무엇보다 kakao OAuth를 기본적으로 지원하기에, 너무나 좋은 경험이었습니다.

관계형 데이터베이스

NoSQL기반의 firebase와 달리 supabase는 PostgreSQL기반의 관계형 데이터베이스를 지원합니다.

firebase에서는 post, user 두개의 테이블이 있다고 했을 때, 클라이언트 부분에서 양 데이터를 가져와 병합했어야 했기에 많은 api호출이 요구됩니다. 하지만 supabase는 관계형 데이터베이스 이기에 join기능을 지원합니다.

먼저 table에서 Foreign key relation을 설정해야 사용하실 수 있습니다.

users 테이블의 id와, profiles 테이블의 id를 맺어줍니다.
ActionCasecade로 설정할 경우, 유저가 회원탈퇴를 하여 users 테이블에서 제거될 경우, profiles테이블에서도 함께 제거됩니다.

const response = await supabase.from(posts).select(`*, profiles (*)`)

위와 같이 관계를 맺어주면 사용 준비가 끝났으며, select함수를 통해 테이블을 join할 수 있습니다.

이 밖에도 다양하고 강력한 filter기능을 지원하고 있으며 문서를 통해 확인하실 수 있습니다.

타입스크립트

supabasetypescript를 지원하고 있습니다. 데이터베이스를 자동으로 type으로 변환해주며

export const supabase = createClient<Database>(supabaseUrl, supabaseKey);

위와 같이 변환된 타입을 주입해 사용할 수 있습니다.

타입을 주입하게 되면 관련 데이터를 다룰 때, 자동으로 타입을 추론해줍니다.

자동으로 mountains 테이블의 데이터 타입이 추론된 모습입니다.

type MountainsTable = Database["public"]["Tables"]["mountains"];
type MountainsRow = MountainsTable["Row"];
type MountainsInsert = MountainsTable["Insert"];
type MountainsUpdate = MountainsTable["Update"];

위처럼 해당 테이블의 CRUD에 대한 타입을 선언해두고 사용할 수 있습니다.

위와 같은 테이블의 데이터가 그대로 들어오는 것이 아닌 변형되서 들어오는 상황의 경우 커스텀 타입도 지정할 수 있습니다.

const { data, error } = await supabase
  .from(ROUTE_TABLE)
  .select(`*, ${MOUNTAIN_TABLE} (*)`)
  .eq("mountainId", mountainId)
  .returns<GetMountainRouteResponse[]>();

기존의 type의 경우 mountains 테이블의 데이터가 null일 가능성을 추론해주고 있습니다.
하지만 저는 무조건 mountains의 데이터가 존재할 것을 알기에 커스텀 타입을 만들어 주입해주었습니다.

반환타입이 변환된 것을 확인할 수 있습니다.

마무리

이번에 supabase를 체험해보면서 가장 크게 느낀 장점은 위 세가지 이지만 그 밖에도 너무나 많은 기능들을 지원하고 있습니다.

그렇지만 데이터베이스에 대한 지식이 전혀 없어도 바로 사용할 수 있는 firebase와 달리, supabase는 데이터베이스에 대한 기초 지식이 있어야 더욱 더 잘 활용할 수 있는 서비스인 것 같습니다.

이번 체험으로 인해 앞으로 개인 프로젝트에는 firebase보다는 supabase를 더 활용할 것 같고, 기회가 된다면 데이터베이스를 공부해서 더 체계적으로 사용해 보고 싶습니다.

1개의 댓글

comment-user-thumbnail
2023년 7월 25일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기

관련 채용 정보