Supabase는 강력한 기능을 제공하는 오픈 소스 백엔드 솔루션으로, 데이터베이스, 인증, 스토리지 등을 쉽게 설정하고 사용할 수 있습니다. 이번 포스트에서는 Supabase를 사용하여 프로젝트를 설정하고 데이터 CRUD(Create, Read, Update, Delete) 작업을 수행하는 방법에 대해 설명하겠습니다.
테이블을 추가할 때 RLS(Row Level Security)를 활성화하여 사이드 프로젝트에서도 안전하게 사용할 수 있도록 합니다. 사용하지 않는 부분은 Null보다는 Empty 값으로 변경하는 것을 잊지 마세요.
yarn add @supabase/ssr @supabase/supabase-js
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
import { createBrowserClient } from "@supabase/ssr";
export const createClient = () =>
createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
const browserClient = createClient();
export default browserClient;
import { createServerClient } from "@supabase/ssr";
import { type NextRequest, NextResponse } from "next/server";
export const updateSession = async (request: NextRequest) => {
try {
let response = NextResponse.next({
request: {
headers: request.headers,
},
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value }) =>
request.cookies.set(name, value),
);
response = NextResponse.next({
request,
});
cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options),
);
},
},
},
);
await supabase.auth.getUser();
return response;
} catch (e) {
return NextResponse.next({
request: {
headers: request.headers,
},
});
}
};
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export const createClient = () => {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options);
});
} catch (error) {
}
},
},
},
);
};
export const getIsLogin = async () => {
const serverClient = createClient();
const {
data: { session },
} = await serverClient.auth.getSession();
return !!session;
};
원하는 Provider를 선택하고 Callback URL을 복사합니다.

Client ID와 Client Secret은 GitHub의 Developer Settings에서 확인할 수 있습니다. OAuth 설정 페이지에서 Callback URL을 붙여넣고 나머지를 채웁니다.

이후 버튼에 아래와 같은 함수를 추가하면 로그인 함수 구현 완료입니다
// login/page.tsx
import { createClient } from "@/utils/supabase/server";
async function signInWithGitHub() {
await browserClient.auth.signInWithOAuth({
provider: "github",
options: {
redirectTo: window.origin + "/auth/callback",
},
});
}
리디렉션 URL을 추가합니다.
// auth/callback/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@/utils/supabase/server';
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get('code');
const next = searchParams.get('next') ?? '/';
if (code) {
const supabase = createClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
const forwardedHost = request.headers.get('x-forwarded-host');
const isLocalEnv = process.env.NODE_ENV === 'development';
if (isLocalEnv) {
return NextResponse.redirect(`${origin}${next}`);
} else if (forwardedHost) {
return NextResponse.redirect(`https://${forwardedHost}${next}`);
} else {
return NextResponse.redirect(`${origin}${next}`);
}
}
}
return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}
클라이언트 컴포넌트에서 세션을 확인하려면 아래 코드를 사용합니다.
useEffect(() => {
browserClient.auth.getSession().then(console.log);
}, []);
로그아웃 기능은 아래와 같이 구현 가능합니다
browserClient.auth.signOut();
로그인 후 Users에 정보가 잘 들어오는지 확인하실 수 있습니다.

RLS를 추가하지 않으면 정책 때문에 데이터를 보거나 쓸 수 없습니다.
SELECT: 모든 유저가 데이터를 볼 수 있도록 설정합니다 - 그대로
INSERT: 로그인한 유저만 데이터를 작성할 수 있도록 설정합니다 - 그대로
Update와 DELETE 앞서서 user_id라는 column을 추가하고 uuid로 타입을 정하고 기본값을 auth.uid()로 설정합니다.
Update부분은 그대로 사용하지 못하여 RLS를 수정하여 user_id가 일치해야 업데이트가 가능하도록 설정합니다.

UPDATE: user_id가 같은 유저만 변경할 수 있도록 설정합니다 - 위처럼 변경
DELETE: user_id가 같은 유저만 데이터를 지울 수 있도록 설정합니다 - 그대로
await serverClient.from("products").select();
await serverClient.from("products").insert({
title,
desc,
});
await serverClient.from("products").update({
title: '변경됨',
}).eq('id', 1);
await serverClient.from("products").delete().eq('id', 1);
버킷을 만들고 정책을 설정합니다:
New Policy를 눌러서 정책을 생성하고 모든 권한을 열어줍니다

// Instagram 버킷에서 logo.png 파일의 URL 가져오기
serverClient.storage.from('Instagram').getPublicUrl("logo.png");
const avatarFile = event.target.files[0];
const { data, error } = await supabase.storage.from('Instagram').upload('public/avatar1.png', avatarFile);
이상으로 Supabase 사용법에 대한 간단한 가이드를 마칩니다!