
Next.js + Supabase 프로젝트 - (4) : Supabase 연결 - 코드 구현
이 글은 "Next.js + Supabase 프로젝트" 시리즈의 네 번째 글로, 브라우저와 서버 환경에서 각각 Supabase 클라이언트를 설정하는 방법을 상세히 안내합니다. utils/supabase/client.ts에서는 createBrowserSupabaseClient()를 통해 익명 키 기반 브라우저 클라이언트를 생성하고, utils/supabase/server.ts에서는 쿠키 관리와 함께 서버용 클라이언트를 사용하거나 관리(admin) 권한 클라이언트를 만드는 방법을 설명합니다
Supabase 의 기능을 사용할 코드를 작성할 텐데, 두 가지로 나누어서 구현하려고 합니다.
1) Supabase 의 client 기능
2) Supabase 의 server 기능
app 폴더 밖의 root 폴더에 utils 폴더를 만들고
그 안에 supabase 폴더를 만들고, 이 안에 파일들을 생성할 예정입니다
utils/supabase/client.ts
목적: 브라우저 환경에서 Supabase 클라이언트를 생성하기 위한 함수
주요 기능:
createBrowserSupabaseClient(): 브라우저에서 사용할 Supabase 클라이언트를 생성합니다. Supabase의 URL과 익명 키를 환경 변수에서 읽어옵니다.
"use client";
import { createBrowserClient } from "@supabase/ssr";
// 내 supabase 프로젝트랑 연관된 이 api를 통해서 anon key를 통해서 브라우저 클라이언트가 하나 자동으로 생성이 된다.
export const createBrowserSupabaseClient = () =>
createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
utils/supabase/server.ts
목적: 서버 환경에서 Supabase 클라이언트를 생성하기 위한 함수들
주요 기능:
createServerSupabaseClient(): 서버에서 사용할 Supabase 클라이언트를 생성합니다. 환경 변수에서 Supabase의 URL과, 주어진 권한에 따라 비공식 또는 관리 권한 키를 사용합니다. 쿠키 관리 기능도 포함되어 있습니다.
createServerSupabaseAdminClient(): 관리 권한을 가진 Supabase 클라이언트를 생성합니다. 서버 측에서만 호출되며, admin 플래그를 true로 설정하여 관리 키를 사용합니다.
"use server";
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { Database } from "../../types_db";
// 서버 컴포넌트들에서만 사용을 할 거임
export const createServerSupabaseClient = async (
cookieStore?: Awaited<ReturnType<typeof cookies>>,
admin: boolean = false
) => {
const resolvedCookieStore = cookieStore || await cookies();
return createServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
admin
? process.env.NEXT_SUPABASE_SERVICE_ROLE!
: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
// 쿠기 get, set, remove 하는 부분 설정을 해줘야
// 이제 유저 관련된 오퍼레이션들이 전부 동작을 함. 인증 부분 구축을 할 때에 많이 사용을 하게 될 거임.
// supabase 에 다 나와있긴 한데, 좀 복잡하니 이거 사용
cookies: {
get(name: string) {
return resolvedCookieStore.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
try {
resolvedCookieStore.set({ name, value, ...options });
} catch (error) {
// The `set` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
remove(name: string, options: CookieOptions) {
try {
resolvedCookieStore.set({ name, value: "", ...options });
} catch (error) {
// The `delete` method was called from a Server Component.
// This can be ignored if you have middleware refreshing
// user sessions.
}
},
},
}
);
};
// 이 함수는 admin 을 true 로 줘서 하는거 정리하려고.
export const createServerSupabaseAdminClient = async (
cookieStore?: Awaited<ReturnType<typeof cookies>>
) => {
return createServerSupabaseClient(cookieStore, true);
};
// 인증 여부 확인 함수
export const requireAuth = async () => {
const supabase = await createServerSupabaseClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) {
// 인증되지 않은 경우 /auth로 리디렉션
redirect("/auth");
}
return session; // 인증된 경우 세션 반환
};
app/middleware.ts
목적: Next.js 애플리케이션에서 요청 및 응답을 처리하고 Supabase 클라이언트를 적용하기 위한 미들웨어
주요 기능:
applyMiddlewareSupabaseClient(): 요청을 처리하며 Supabase 클라이언트를 생성하고, 쿠키를 관리합니다. 요청과 응답에 Supabase 클라이언트를 통합하고 인증 토큰을 갱신합니다.
middleware(): applyMiddlewareSupabaseClient()를 호출하여 미들웨어를 구현합니다. 특정 요청 경로에 대해 미들웨어가 동작하도록 설정합니다.
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { type NextRequest, NextResponse } from "next/server";
export const applyMiddlewareSupabaseClient = async (request: NextRequest) => {
// Create an unmodified response
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: {
get(name: string) {
return request.cookies.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
// If the cookie is updated, update the cookies for the request and response
request.cookies.set({
name,
value,
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value,
...options,
});
},
remove(name: string, options: CookieOptions) {
// If the cookie is removed, update the cookies for the request and response
request.cookies.set({
name,
value: "",
...options,
});
response = NextResponse.next({
request: {
headers: request.headers,
},
});
response.cookies.set({
name,
value: "",
...options,
});
},
},
}
);
// refreshing the auth token
await supabase.auth.getUser();
return response;
};
export async function middleware(request: NextRequest) {
return await applyMiddlewareSupabaseClient(request);
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};
이렇게 기본적인 설정들을 해주고
기본적인 사이트 설정들을 해주었습니다
1) SEO 설정
app/page.tsx
public 폴더에 images 라는 폴더를 만들고, JM_logo.png 라는 파일을 추가해두었습니다.
그리고 아래의 코드를 page.tsx 코드 위에다가 붙여넣었습니다 (대체하라는게 아님. 추가하는거임)
export async function generateMetadata() {
return {
title: "큐라시의 블로그 입니다",
description:
"Next.js와 Supabase를 사용하여 만든 큐라시의 블로그입니다. 최신 기술 스택을 활용하여 빠르고 효율적인 웹 애플리케이션을 제공합니다.",
openGraph: {
title: "큐라시의 블로그 입니다",
description:
"Next.js와 Supabase를 사용하여 만든 큐라시의 블로그입니다. 최신 기술 스택을 활용하여 빠르고 효율적인 웹 애플리케이션을 제공합니다.",
images: "/images/JM_logo.png",
},
};
}
2) favicon 변경 (크롬 브라우저의 탭에 들어갈 아이콘)
public 폴더에 favicon.ico 파일을 추가했고,
app 폴더의 favicon.ico 파일을 삭제했습니다. 저는 사진이나 이런 모든 파일들을 public 에다가 헷갈리지 않게 전부 넣으려고 한 조치입니다!


