☁️ goormTIL | Next.js #68

매루·2025년 12월 15일

goormTIL

목록 보기
66/67
post-thumbnail

📅 2025-12-15

➡️ Shadcn/ui, Next js Supabase에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리


🔎 학습 리마인드

📌 Shadcn/ui

🔗 The Foundation for your Design System - shadcn/ui

  • vercel에서 만든 UI 도구로 Tailwind CSS 기반으로 하는 컴포넌트 라이브러리

설치

npx shadcn@latest init

예시 - 버튼

npx shadcn@latest add button
  • components 폴더 안에 ui 폴더가 생성 되고, button.tsx 파일이 생성됨

📌 Next.js + Supabase

🔗 Creating a Supabase client for SSR | Supabase Docs

  • Next.js(App Router)에서 Supabase를 사용하려면, 클라이언트 / 서버 / 미들웨어를 분리해서 설정해야 함

설치

npm i @supabase/supabase-js @supabase/ssr

환경 변수 설정

  • .env 파일
    NEXT_PUBLIC_SUPABASE_URL=supabase_project_url
    NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=supabase_publishable_key

Supabase Client 구조

구분사용 위치특징
client.tsClient Component브라우저용
server.tsServer Component / Server Action요청마다 새로 생성
middleware.tsMiddleware세션 유지 / 보호 라우트

  • utils/client.ts
    import { createBrowserClient } from "@supabase/ssr";
    export function createClient() {
      return createBrowserClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!
      );
    }
    
    const browserClient = createClient();
    export default browserClient;
    • .env의 정보들을 이용해서 클라이언트를 생성하고, 브라우저에서 1개의 클라이언트를 공유하여 사용

  • utils/server.ts
    import { createServerClient } from '@supabase/ssr'
    import { cookies } from 'next/headers'
    
    export async function createClient() {
      const cookieStore = await cookies()
    
      return createServerClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
        {
          cookies: {
            getAll() {
              return cookieStore.getAll()
            },
            setAll(cookiesToSet) {
              try {
                cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options))
              } catch {
                // The `setAll` method was called from a Server Component.
                // This can be ignored if you have middleware refreshing
                // user sessions.
              }
            },
          },
        }
      )
    };
    • 서버에서는 클라이언트를 생성하고, 사용할 때마다 createClient를 불러서 사용

  • utils/middleware.ts
    import { createServerClient } from '@supabase/ssr'
    import { NextResponse, type NextRequest } from 'next/server'
    
    export async function updateSession(request: NextRequest) {
      let supabaseResponse = NextResponse.next({
        request,
      })
    
      // With Fluid compute, don't put this client in a global environment
      // variable. Always create a new one on each request.
      const supabase = createServerClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
        {
          cookies: {
            getAll() {
              return request.cookies.getAll()
            },
            setAll(cookiesToSet) {
              cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
              supabaseResponse = NextResponse.next({
                request,
              })
              cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))
            },
          },
        }
      )
    
      // Do not run code between createServerClient and
      // supabase.auth.getClaims(). A simple mistake could make it very hard to debug
      // issues with users being randomly logged out.
    
      // IMPORTANT: If you remove getClaims() and you use server-side rendering
      // with the Supabase client, your users may be randomly logged out.
      const { data } = await supabase.auth.getClaims()
    
      const user = data?.claims
    
      if (
        !user &&
        !request.nextUrl.pathname.startsWith('/login') &&
        !request.nextUrl.pathname.startsWith('/auth')
      ) {
        // no user, potentially respond by redirecting the user to the login page
        const url = request.nextUrl.clone()
        url.pathname = '/login'
        return NextResponse.redirect(url)
      }
    
      // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
      // creating a new response object with NextResponse.next() make sure to:
      // 1. Pass the request in it, like so:
      //    const myNewResponse = NextResponse.next({ request })
      // 2. Copy over the cookies, like so:
      //    myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
      // 3. Change the myNewResponse object to fit your needs, but avoid changing
      //    the cookies!
      // 4. Finally:
      //    return myNewResponse
      // If this is not done, you may be causing the browser and server to go out
      // of sync and terminate the user's session prematurely!
    
      return supabaseResponse
    }
    • 세션 자동 갱신
    • 새로고침 시 로그아웃 방지
    • 보호된 페이지 접근 제어

0개의 댓글