Trouble Shooting
2025년 12월 03일

Supabase 사용시 쿠키 사용 에러

트러블슈팅: Cookies can only be modified in a Server Action or Route Handler 에러


1. 문제

  • 에러 메시지Cookies can only be modified in a Server Action or Route Handler.
  • 상황
    • Supabase SSR 클라이언트(@supabase/ssr)를 감싼 createServerClient를 레이아웃(서버 컴포넌트) 에서 사용.
    • Supabase가 세션 동기화 과정에서 내부적으로 cookies().set(...)를 호출하면서 Next.js 규칙 위반 에러 발생.

2. Next.js 관점의 핵심 원인

Next.js App Router의 cookies()는 실행 환경에 따라 권한이 다름.

  • 서버 컴포넌트 / RSC (layout.tsx, 페이지 등)
    • cookies().get() → ✅ 읽기만 허용
    • cookies().set() / cookies().delete() → ❌ 금지 (에러 발생)
  • Server Action / Route Handler (app/actions/*app/api/*)
    • cookies().get() → ✅
    • cookies().set() / cookies().delete() → ✅ (여기서만 쿠키 수정 가능)

즉, “쿠키를 수정하는 Supabase 클라이언트를 서버 컴포넌트에서 사용한 것” 이 에러의 본질이다.


3. 수정 전략: 용도별 Supabase 클라이언트 분리

3-1. RSC 전용: 쿠키 읽기 전용 Supabase 클라이언트

// shared/lib/supabase/server.tsexport function createServerComponentClient() { const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY if (!supabaseUrl || !supabaseAnonKey) { throw new Error('Missing Supabase environment variables') } const cookieStore = cookies() as any return createSupabaseServerClient(supabaseUrl, supabaseAnonKey, { cookies: { get(name: string) { return cookieStore.get(name)?.value }, set() { // RSC에서는 쿠키 수정 금지 → NO-OP }, remove() { // RSC에서는 쿠키 수정 금지 → NO-OP }, }, }) }
  • 사용처 예시: app/layout.tsx
    • 서버에서 유저 정보를 조회만 할 때 사용.
// app/layout.tsximport { createServerComponentClient } from '@/shared/lib/supabase' const supabase = createServerComponentClient() const { data: { user }, } = await supabase.auth.getUser()

3-2. Server Action / Route 전용: 쿠키 읽기 + 쓰기 Supabase 클라이언트

// shared/lib/supabase/server.tsexport async function createServerClient() { const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY if (!supabaseUrl || !supabaseAnonKey) { throw new Error('Missing Supabase environment variables') } const cookieStore = cookies() as any return createSupabaseServerClient(supabaseUrl, supabaseAnonKey, { cookies: { get(name: string) { return cookieStore.get(name)?.value }, set(name: string, value: string, options?: any) { cookieStore.set(name, value, options) }, remove(name: string, options?: any) { cookieStore.set(name, '', { ...(options || {}), maxAge: 0, }) }, }, }) }
  • 사용처 예시: app/actions/auth-actions.tsapp/api/*
    • 로그인/로그아웃, 토큰 쿠키 저장/삭제처럼 실제 쿠키를 수정해야 하는 로직에서만 사용.

4. 결과

  • Next.js 규칙에 맞게:
    • 서버 컴포넌트(RSC)에서는 쿠키 read-only 클라이언트만 사용.
    • Server Action / Route Handler에서는 쿠키 read/write 클라이언트를 사용.
  • 그 결과:
    • Supabase의 세션/토큰 관리는 그대로 유지하면서,
    • Cookies can only be modified in a Server Action or Route Handler 에러는 더 이상 발생하지 않게 됨.