OPEN HYPER STEP
← 목록으로 (master-project)
MASTER-PROJECT · 28 / 50
master-project
CHAPTER 28 / 50
읽기 약 2
FUNCTION

인증 API: JWT + 소셜 로그인 + 미들웨어


핵심 개념

Supabase JWT·세션 쿠키·middleware 갱신·OAuth 콜백·CSRF·로그아웃 — 인증 흐름 전체.

본문

Supabase JWT 흐름

📋 코드 (5줄)
1. 로그인 → Supabase Auth Server
2. Access Token (1시간) + Refresh Token (영구) 발급
3. 쿠키에 저장 (httpOnly + secure + sameSite=lax)
4. 매 요청마다 middleware가 토큰 검증·갱신
5. 만료 5분 전 자동 갱신 (refresh)

Supabase 클라이언트 (서버)

TS📋 코드 (21줄)
// src/lib/supabase/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_ANON_KEY!,
    {
      cookies: {
        getAll() { return cookieStore.getAll() },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            cookieStore.set(name, value, options)
          )
        },
      },
    }
  )
}

미들웨어 (세션 갱신)

TS📋 코드 (37줄)
// src/middleware.ts
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
  let response = NextResponse.next({ request })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() { return request.cookies.getAll() },
        setAll(cookies) {
          cookies.forEach(({ name, value, options }) => {
            response.cookies.set(name, value, options)
          })
        },
      },
    }
  )

  const { data: { user } } = await supabase.auth.getUser()

  // 인증 필요 페이지 보호
  if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return response
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}

OAuth 콜백 처리

TS📋 코드 (17줄)
// src/app/auth/callback/route.ts
import { NextResponse } from 'next/server'
import { createClient } from '@/lib/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') ?? '/dashboard'

  if (code) {
    const supabase = await createClient()
    const { error } = await supabase.auth.exchangeCodeForSession(code)
    if (!error) return NextResponse.redirect(`${origin}${next}`)
  }

  return NextResponse.redirect(`${origin}/login?error=auth_failed`)
}

로그아웃

TS📋 코드 (9줄)
// src/app/(auth)/logout/route.ts
import { NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'

export async function POST() {
  const supabase = await createClient()
  await supabase.auth.signOut()
  return NextResponse.redirect(new URL('/', process.env.NEXT_PUBLIC_APP_URL!))
}

API에서 인증 검증

TS📋 코드 (12줄)
// src/app/api/generations/route.ts
import { NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'

export async function POST(request: Request) {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) {
    return NextResponse.json({ error: { code: 'UNAUTHORIZED' } }, { status: 401 })
  }
  // 비즈니스 로직...
}

API Token (외부 통합)

SQL📋 코드 (10줄)
-- API token 테이블
create table api_tokens (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references users(id) on delete cascade,
  name text,
  token_hash text not null,
  created_at timestamptz default now(),
  last_used_at timestamptz,
  expires_at timestamptz
);
TS📋 코드 (15줄)
// 검증
import { createHash } from 'crypto'

export async function authenticateApiToken(token: string) {
  const hash = createHash('sha256').update(token).digest('hex')
  const supabase = createServiceClient()  // service role
  const { data } = await supabase
    .from('api_tokens')
    .select('user_id, expires_at')
    .eq('token_hash', hash)
    .single()

  if (!data || (data.expires_at && new Date(data.expires_at) < new Date())) return null
  return data.user_id
}

CSRF 보호 (Server Action 자동)

📋 코드 (3줄)
- Server Action: Next.js가 자동으로 CSRF 토큰 검증
- Route Handler: SameSite=lax 쿠키 + Origin 헤더 체크
- API Token: Bearer 헤더 (CSRF 무관)

다음 챕터

CH.29 "파일 API: S3/Supabase Storage + presigned".


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 마스터 프로젝트의 인증 API 부분을 분석해서
실전 적용 + 개선 우선순위 3가지를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

인증 API 관련 모범 사례·안티패턴 5개를
비교 분석해서 실전 적용를 위한 추천 방안을 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 프로젝트 전체에서 인증 API
최적화 가능 위치와 리스크를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 1인 개발자 시장의
인증 API 트렌드와 차별화 포인트를 정리해줘.

⭐ 이것만 기억하세요
인증 API: JWT + 소셜 로그인 + 미들웨어 이 3가지만 확실히 잡으세요
1.Supabase JWT + middleware 자동 갱신
2.OAuth 콜백 + 외부용 API Token 분리
3.다음 챕터에서 파일 API


공유하기
진행도 28 / 50