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 규칙 위반 에러 발생.
- Supabase SSR 클라이언트(
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.ts,app/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에러는 더 이상 발생하지 않게 됨.