1Supabase 프로젝트 생성
- supabase.com 에서 가입 (GitHub 로그인 추천)
- New project 클릭
- 프로젝트 이름 입력
- 데이터베이스 비밀번호 설정 (안전한 곳에 저장!)
- 리전 선택:
Northeast Asia (Seoul)추천 - Create new project 클릭
정보
프로젝트 생성에 1-2분 정도 소요됩니다. 생성 완료 후 대시보드에서 API 키를 확인할 수 있습니다.
2테이블 생성 (SQL Editor)
대시보드에서 SQL Editor를 열고 아래 스키마를 실행하세요.
SQL Editor
-- todos 테이블 생성
create table todos (
id uuid default gen_random_uuid() primary key,
user_id uuid references auth.users(id) on delete cascade,
title text not null,
completed boolean default false,
created_at timestamptz default now()
);
-- RLS (Row Level Security) 활성화
alter table todos enable row level security;
-- 정책: 자신의 데이터만 조회 가능
create policy "Users can view own todos"
on todos for select
using (auth.uid() = user_id);
-- 정책: 자신의 데이터만 추가 가능
create policy "Users can insert own todos"
on todos for insert
with check (auth.uid() = user_id);
-- 정책: 자신의 데이터만 수정 가능
create policy "Users can update own todos"
on todos for update
using (auth.uid() = user_id);
-- 정책: 자신의 데이터만 삭제 가능
create policy "Users can delete own todos"
on todos for delete
using (auth.uid() = user_id);RLS가 중요한 이유
RLS 없이는 모든 사용자가 모든 데이터에 접근할 수 있습니다.
auth.uid()는 현재 로그인한 사용자의 ID를 반환합니다.
3클라이언트 설정 (React)
의존성 설치
터미널
npm install @supabase/supabase-js @tanstack/react-query환경 변수 설정
.env.local
# .env.local
VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-keySupabase 대시보드 → Settings → API에서 확인하세요.
Supabase 클라이언트
src/lib/supabase.ts
// src/lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey);4CRUD 예제 (React Query + Supabase)
타입 정의
src/types/todo.ts
// src/types/todo.ts
export interface Todo {
id: string;
user_id: string;
title: string;
completed: boolean;
created_at: string;
}
export type CreateTodoInput = Pick<Todo, 'title'>;
export type UpdateTodoInput = Partial<Pick<Todo, 'title' | 'completed'>>;Hooks (TanStack Query)
src/hooks/useTodos.ts
// src/hooks/useTodos.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '../lib/supabase';
import type { Todo, CreateTodoInput, UpdateTodoInput } from '../types/todo';
// 목록 조회
export function useTodos() {
return useQuery({
queryKey: ['todos'],
queryFn: async () => {
const { data, error } = await supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false });
if (error) throw error;
return data as Todo[];
},
});
}
// 생성
export function useCreateTodo() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (input: CreateTodoInput) => {
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not authenticated');
const { data, error } = await supabase
.from('todos')
.insert({ ...input, user_id: user.id })
.select()
.single();
if (error) throw error;
return data as Todo;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
}
// 수정
export function useUpdateTodo() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, ...input }: UpdateTodoInput & { id: string }) => {
const { data, error } = await supabase
.from('todos')
.update(input)
.eq('id', id)
.select()
.single();
if (error) throw error;
return data as Todo;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
}
// 삭제
export function useDeleteTodo() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (id: string) => {
const { error } = await supabase
.from('todos')
.delete()
.eq('id', id);
if (error) throw error;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
}5인증 예제 (선택)
Supabase Auth를 사용하면 이메일/비밀번호 로그인을 쉽게 구현할 수 있습니다.
src/hooks/useAuth.ts
// src/hooks/useAuth.ts
import { useState, useEffect } from 'react';
import { supabase } from '../lib/supabase';
import type { User } from '@supabase/supabase-js';
export function useAuth() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 현재 세션 확인
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null);
setLoading(false);
});
// 인증 상태 변경 감지
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => {
setUser(session?.user ?? null);
}
);
return () => subscription.unsubscribe();
}, []);
// 회원가입
const signUp = async (email: string, password: string) => {
const { error } = await supabase.auth.signUp({ email, password });
if (error) throw error;
};
// 로그인
const signIn = async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) throw error;
};
// 로그아웃
const signOut = async () => {
const { error } = await supabase.auth.signOut();
if (error) throw error;
};
return { user, loading, signUp, signIn, signOut };
}소셜 로그인
GitHub, Google, Discord 등 소셜 로그인도 지원합니다. Supabase 대시보드 → Authentication → Providers에서 설정하세요.
언제 Supabase를 사용하면 좋을까요?
✅ 추천하는 경우
- 빠르게 MVP 만들 때
- 백엔드 개발 없이 프론트만 집중하고 싶을 때
- 실시간 기능이 필요할 때
- 인증 + DB를 한 번에 해결하고 싶을 때
⚠️ 고려가 필요한 경우
- 복잡한 비즈니스 로직이 있을 때
- 외부 API 연동이 많을 때
- 커스텀 백엔드 로직이 필수일 때
- 데이터 구조가 자주 바뀔 때
다음 단계
- 인증 가이드 - JWT, Session, OAuth 비교
- Supabase 공식 문서 - 더 자세한 기능
- Cycle 5: Runtime - 환경 설정 관리