Supabase
Supabase는 데이터베이스와 API를 쉽고 간단하게 구축할 수 있는 severless DB 솔루션.
PostgreSQL을 데이터베이스 엔진으로 사용하고 있어 복잡한 쿼리 작업도 용이하게 사용할 수 있다.
"Supabase is an open source Firebase alternative."
위 슬로건에서 볼 수 있듯이 아주 공식적으로 Firebase를 저격하고 있는데
실제로 Firebase에서 사용할 수 있는 기능들을 웬만하면 모두 처리할 수 있다!
Firebase vs Supabase
데이터베이스 측면에서 Firebase는 NoSQL 데이터베이스인 Firestore와 Realtime Database를 제공한다. 스키마가 없고, 유연성이 높지만 대신에 복잡한 쿼리 작업에는 제약이 있다. 반면 Supabase는 PostgreSQL을 사용한다. 데이터 정규화, 복잡한 쿼리 작업 등 SQL의 모든 기능을 사용할 수 있다.
가격적인 측면에서도 차이를 보인다. Firebase와 Supabase 모두 기본적으로 무료이나, Firebase는 사용량이 증가할 경우 비용이 빠르게 증가하는 반면 Supabse는 비교적 저렴하게 원하는 기능을 사용할 수 있다.
그리고 개인적으로는 UI 면에서도 차이가 느껴졌다. 한글 서비스가 제공해주는 Firebase와는 달리 영문으로 이용해야하는 Supabase이지만 사용 난이도가 어렵지 않았고 문서도 충분히 잘 제공되고 있었다. 그리고 홈페이지 내에 SQL Editor가 존재해 내가 만든 테이블에 대해 SQL 쿼리를 작성해보고 실행 해볼 수 있게 해주는 등의 편안한 기능이 많았다. Firebase를 처음 사용할 때 보다 오히려 쉽게 적응한 것 같다.
Supabase를 선택한 이유?
포트폴리오를 만드는 과정에서 프로젝트 데이터를 REST Api로 만들어 다루면 데이터 복습도 되고 편할 것 같다는 생각이 들었다. BaaS 중 하나인 Firebase는 이미 사용 경험이 있어서 가능하다면 다른 기술을 사용해보고 싶었는데, 이전에 한 멘토링에서 Supabase가 요즘 떠오르는 기술이며 프로젝트에서 사용해 볼 만 하다고 추천받은 것이 기억났다. 실제로 둘을 비교했을 때 Supabase의 장점이 많이 보이기도 했고, Firebase보다 SQL 관점에서 해볼 수 있는 것이 많게 느껴져 사용해보게 됐다.
Supabase를 이용해서 RESTful API 만들어 사용하기
홈페이지에서 프로젝트 만들기
'Create a new project' 를 통해 내가 사용할 프로젝트를 생성한다.
- Organization 과 Region 선택
- Project name 지정
- Database Passowrd 입력 : 나는 자동으로 만들어준 암호를 사용했다. 어딘가에 복사해두는 것은 필수
- RLS 사용 여부 선택
- Row Level Security. Postgres의 기본 보안 정책으로 사용(enable)을 선택하면 인증된 사용자만이 테이블에 접근할 수 있게 된다.
- 나는 일단 사용을 선택하고 진행했다. 프로젝트가 생성된 이후 보안 정책을 수정할 수 있다.
Create new project를 통해 프로젝트 생성 성공!
하단에 보여지는 정보 중 Project URL(RESTful endpoint) 와 Project API Keys(anon key)를 이용하게 된다.
이 값은 Project Settings > CONFIGURATION > API 를 통해서도 다시 확인할 수 있다.
홈페이지에서 테이블 생성하기
사용할 데이터베이스 테이블을 홈페이지에서 생성한다.
Database > DATABASE MANAGEMENT > Tables 로 들어가 'New table' 버튼을 클릭한다.
나는 "projects" 라는 테이블을 만들었다.
Columns 를 통해서 사용할 각 칼럼의 이름과 자료형, 그리고 기본값을 지정할 수 있다.
테이블을 생성한 이후에는 Table Editor를 사용해 만들어진 테이블을 볼 수 있다.
여기에서 'Insert row' 버튼을 통해 데이터를 홈페이지 내에서 직접 추가할 수 있다.
프로젝트에 Supabase 적용하기
이제 데이터베이스를 사용할 예정인 프로젝트의 레포에서 다음 과정을 진행한다.
(* Next.js + typescript 프로젝트를 기준으로 작성)
먼저 supabase를 설치한다.
$ npm i @supabase/supabse-js
그리고 환경 변수를 설정한다.
위에서 확인한 Project URL과 API Keys를 복사해 입력한다.
Next.js 프로젝트의 경우는 각 변수의 접두어로 NEXT_PUBLIC_을 붙여주면 된다.
// .env.local
NEXT_PUBLIC_SUPABASE_URL=프로젝트 URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=프로젝트 API Key(anon)
이어서 프로젝트와 supabase를 연결하기 위한 config 파일을 만든다.
환경 변수로 설정한 값들을 이용하여 클라이언트를 생성하고 이를 다른 파일에서 사용할 수 있도록 export 해준다.
// api/supabase.ts
import { createClient } from "@supabase/supabase-js";
import { Database } from "@/types/supabase";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string;
export const supabase = createClient<Database>(supabaseUrl, supabaseKey); // 클라이언트 생성
🤨: 그런데요? Database는 갑자기 어디에서 나온건가요?
🎄: 타입 파일 입니다요.
Supabase의 장점이 여기에서도 하나 나오는데 타입 파일을 자동으로 만들어 제공해준다.
API Docs > TABLES AND VIEWS > Intoduction 로 들어가 'Generate and download types' 버튼을 클릭하면 내가 만든 테이블과 관련된 타입 파일을 다운로드 할 수 있다. 만일 테이블에 변동사항이 있다면 타입 파일도 최신화를 함께 지정해주면 된다.
나는 이 파일을 supabase.d.ts로 지정하고 불러와 사용할 수 있도록 했다.
만들어진 API 호출하기
생성한 클라이언트를 활용하여 "projects" 테이블의 데이터를 호출해 사용할 수 있다.
// api/projects.ts
import { supabase } from "@/api/subabase";
export async function getProjects() {
const { data, error } = await supabase.from("projects").select("*"); // "projects" 테이블의 모든 값 가져오기
if (error) {
console.error("데이터 패칭 실패", error);
return [];
}
console.log("데이터 패칭 성공", data);
return data;
}
// app/projects/page.tsx
import { getProjects } from "@/api/projects";
import type { Project } from "@/types/project";
export default async function Project() {
const projects: Project[] = await getProjects();
return (
<ul>
{projects.map((project) => (
<>
<li key={project.id}>
<h2>{project.name}</h2>
<p>{project.startDate} - {project.endDate}</p>
<p>{project.skill}</p>
</li>
<hr />
</>
))}
</ul>
);
}
🚨 통신은 성공했는데 데이터가 없어? RLS를 확인하자
위 과정 중에서 데이터 통신은 성공했는데 결과가 빈배열로 보여지는 문제가 있었다.
SQL Editor로 확인하니 데이터는 분명히 존재했다. 왜 안보여지는걸까?
이는 RLS와 관련이 있었다. 앞서 테이블에 RLS를 적용해두었는데 이 경우 인증된 사용자만이 데이터에 접근할 수 있었고, 데이터를 열람하는 것 역시 인증된 사용자만이 가능했다.
내가 만들 프로젝트는 로그인을 거치지 않을 예정이었기에 모든 사용자가 데이터를 육안으로 볼 수는 있어야 했다. 때문에 누구나 select 를 할 수 있도록 정책을 수정해야 했다.
Authentication > CONFIGURATION > Policies 로 이동하여 "projects" 테이블에 'Create policy'를 해줬다.
우측 템플릿의 'Enable read access for all users' 를 클릭해 for SELECT to public using (true) 를 적용하고 저장하면 된다.
위 설정 이후로는 데이터가 잘 호출됐다 👍
axios를 활용하여 API 직접 호출하기
위에서 소개한 방법은 Supabase 클라이언트를 사용하여 api를 호출하는 방법으로 코드 내에서 쿼리문을 일부 사용하는 것을 볼 수 있었다. (from("projects").select("*"))
나의 경우는 이 방법보다는 직접 RESTful API를 호출하고 싶었는데 chatGPT를 통해 이것도 가능하다는 답변을 얻었고 아래와 같이 axios를 결합해서 호출하는 방법을 시도해봤다.
// api/projects-axios.ts
import axios from "axios";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string;
export async function getProjects() {
try {
const response = await axios.get(`${supabaseUrl}/rest/v1/projects`, {
headers: {
apikey: supabaseKey,
},
});
console.log("데이터 패칭 성공", response.data);
return response.data;
} catch (error) {
console.error("데이터 패칭 실패", error);
return [];
}
}
'/rest/v1/프로젝트명'을 endpoint로 잡고 header에 apikey를 포함하여 전달하니 데이터 패칭을 성공했다!
내 프로젝트에서는 이 방법을 활용해보기로 😎
문제점이 보이면 클라이언트 방식으로 변환하고 관련된 포스팅을 남기는 것으로 마무리해본다.
참고
파이어베이스 킬러? 요즘 대세는 수파베이스! - 노마드 코더
'NEXT' 카테고리의 다른 글
[NEXT] App Router의 데이터 캐시에 대해 정리하며 이해하기 (0) | 2024.10.30 |
---|---|
[NEXT] App Router에 대해 정리하며 이해하기 (4) | 2024.08.22 |
[NEXT] React.js와 Next.js 중 무엇을 고를까? CSR와 SSR (0) | 2024.01.20 |