저번 프로젝트를 firebase
로 작업하면서 생각보다 불편한 부분이 많이 있었기에, 이를 대체할 서비스를 탐색하던 중, supabase
를 알게되었고, 이를 사용해본 경험을 남기려 합니다.
먼저 가장 크게 느꼈던 점은 firebase
의 docs
도 잘 정리되어 있다 느꼈지만, supabase
의 docs
가 더 잘 구성되어 있다 느껴졌습니다.
첫번째로 로그인 회원가입 기능을 구현할 때, 해당 유저의 닉네임, 이메일과 같은 프로필 정보를 DB에 저장해주어야 합니다.
firebase
의 경우 회원가입이 완료된 후 별도의 api를 호출해 해당 유저의 정보를 저장해주는 과정이 필요하지만 supbase
는 그렇지 않습니다.
supabase
에서 제공하는 trigger
와 function
기능을 활용해 회원가입이 이루어지면, 저장되어있는 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를 맺어줍니다.
Action
을 Casecade
로 설정할 경우, 유저가 회원탈퇴를 하여 users 테이블에서 제거될 경우, profiles테이블에서도 함께 제거됩니다.
const response = await supabase.from(posts).select(`*, profiles (*)`)
위와 같이 관계를 맺어주면 사용 준비가 끝났으며, select
함수를 통해 테이블을 join
할 수 있습니다.
이 밖에도 다양하고 강력한 filter기능을 지원하고 있으며 문서를 통해 확인하실 수 있습니다.
supabase
는 typescript
를 지원하고 있습니다. 데이터베이스를 자동으로 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
를 더 활용할 것 같고, 기회가 된다면 데이터베이스를 공부해서 더 체계적으로 사용해 보고 싶습니다.
잘 읽었습니다. 좋은 정보 감사드립니다.