오늘은 Supabse 사용기에 대해 포스팅하겠습니다.
Expense-Tracker 프로젝트에서 사용자가 수입 및 지출을 입력하면 데이터베이스에 저장되도록 하기 위해 Supabase를 채택했습니다.
채택한 이유는 다음과 같습니다.

Firebase는 read, write, delete에 요청에 대해 제한이 있습니다. 또한 가장 큰 문제는 개발 상황에서 api request가 제대로 기능하는지 계속해서 확인할 텐데, Firebase Database 탭에 접근하는 것만으로도 document read 카운트로 집계됩니다. 2만회? 5만회? 금방입니다...
Supabase 로 들어가서, 회원 가입 및 로그인을 진행합니다.
React Supabase CRUD Tutorial 해당 링크의 유튜버를 따라 진행해줍니다~
Table Editor 탭에서, New Table을 생성해줍니다.


저는 이렇게 작성해줬습니다.
이제 vscode로 넘어와서 supabase 설정을 해줍시다.
터미널에 아래 명령어를 입력해줍니다. (저는 pnpm을 사용합니다.)
pnpm install supabase
supabase gen types typescript --project-id 여러분의프로젝트ID > database.types.ts
프로젝트 ID는 Project Setting 탭에서 확인할 수 있습니다.
저렇게 하면 데이터베이스의 타입들을 자동으로 생성해줄 수 있습니다. 만약 supabase에서 타입을 변경해도, 같은 명령어를 입력하면 갱신해서 가져올 수 있습니다.
typescript를 사용하므로 type 정의는 중요합니다.
supabase 폴더를 하나 만들어, supabase.ts 파일을 생성해줍니다.
import { Database } from '../database.types';
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_APP_SUPABASE_URL
const supabaseKey = import.meta.env.VITE_APP_SUPABASE_API_KEY
if (!supabaseUrl || !supabaseKey) {
throw new Error('Missing Supabase environment variables')
}
const supabase = createClient<Database>(supabaseUrl, supabaseKey)
export default supabase
Database 타입은 위에서 supabase gen types ~~ 명령어로 생성해준 파일에서 가져옵니다. 조금 더 안정적이게 되겠죠.
VITE_APP_SUPABASE_URL, VITE_APP_SUPABASE_API_KEY가 뭐지? 하시는 분들이 계실 수도 있습니다. API Key를 노출하는 것은 보안상 좋지 않습니다. 때문에 루트 경로에 .env 파일을 생성하고, .gitignore에 추가해준 뒤, 본인의 URL과 API Key를 작성해줍니다.
VITE_APP_SUPABASE_URL = https://본인URL.supabase.co
VITE_APP_SUPABASE_API_KEY = abcdsedfafeaf~~~
URL과 API Key는 Home 탭에서 아래로 살짝 내리면 찾을 수 있습니다.
저는 react-query를 사용해서 Supabase를 사용해 볼 겁니다.
api 파일 만들기api 폴더를 하나 만들어서, api.ts 파일을 하나 생성합니다. 이제 우리는 이 파일에서 api 로직을 관리하게 됩니다.
interface RequestValue {
input_type: string
category: Json
amount: number
place: string
memo: string
date: string
}
const categoryToJson = (category : ICategory): Json => ({
id: category.id,
name: category.name,
value: category.value,
})
export const addData = async (input: IInputFormData) => {
const requestData : RequestValue = {
input_type: input.inputType,
category: categoryToJson(input.category),
amount: input.amount,
place: input.place,
memo: input.memo,
date: input.date,
}
const { data, error } = await supabase
.from('expenseList')
.insert(requestData)
if (error) {
throw error
} else {
console.log('Data inserted Success')
}
return true
}
IInputFormData 라는 형식에 맞춰 input을 받고, RequestValue 타입에 맞춰 supabase의 expenseList 테이블에 데이터를 전송합니다.
이제 api를 호출할 컴포넌트로 이동합니다. 저는 InputForm 이라는 컴포넌트에서 onSubmit 으로 넘겨줄 겁니다.
const InputForm = ({ children }: { children: React.ReactNode }) => {
const { handleSubmit } = useFormContext<IInputFormData>()
const mutation = useMutation({
mutationFn: addData,
onSuccess: () => {
alert('저장이 완료되었습니다.')
},
onError: () => {
alert('저장에 실패하였습니다. 다시 시도해주세요.')
},
})
const onSubmit: SubmitHandler<IInputFormData> = (data) => {
mutation.mutate(data)
}
return (
<div className='pt-[40px] pb-[60px] px-10'>
<form onSubmit={handleSubmit(onSubmit)} className='h-full'>
{children}
<button type='submit'>
확인
</button>
</form>
</div>
)
}
데이터를 저장할 때는 useMutation 을 사용합니다. mutation할 함수를 정해주고, 성공 및 실패 각각에 대한 로직을 간단하게 작성해줍니다.

테이블에 제대로 데이터가 들어오는 것을 확인할 수 있습니다!
참고
https://www.youtube.com/watch?v=tW1HO7i9EIM&t=1517s
https://hanbin-sla.tistory.com/entry/Supabase-vs-Firebase
https://supabase.com/docs/reference/javascript/typescript-support