개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Supabase 환경 변수 — API 키를 안전하게 관리하는 방법

2025년 4월 28일
Supabase환경 변수SUPABASE_URLANON_KEYSERVICE_ROLE_KEY

Supabase 프로젝트를 처음 만들면 대시보드에 키가 두 개 보인다. 어떤 키를 어디에 써야 하는지 모른 채 둘 다 프론트엔드에 넣으면 보안 사고로 이어진다. 세 가지 환경 변수의 역할을 정확히 이해하면 실수를 원천 차단할 수 있다.

세 가지 환경 변수

변수명값 예시용도
SUPABASE_URLhttps://xxx.supabase.co프로젝트 API 엔드포인트
SUPABASE_ANON_KEYeyJ... (JWT)클라이언트 공개 키
SUPABASE_SERVICE_ROLE_KEYeyJ... (JWT)서버 전용 관리자 키

SUPABASE_URL은 단순한 엔드포인트 주소다. 공개되어도 무방하지만, 키 없이는 아무것도 할 수 없다.

SUPABASE_ANON_KEY는 Row Level Security(RLS) 정책을 통과해야만 데이터에 접근할 수 있는 공개 키다. 브라우저에 노출되는 것을 전제로 설계되어 있다.

SUPABASE_SERVICE_ROLE_KEY는 RLS를 완전히 우회한다. 이 키가 클라이언트에 노출되면 테이블 전체를 읽고 쓰고 지울 수 있다.

RLS(Row Level Security): PostgreSQL의 행 단위 접근 제어 기능. Supabase는 이를 기반으로 사용자별 데이터 격리를 구현한다.

anon vs service_role 차이

클라이언트 (브라우저)          서버 (Next.js API Route / Edge Function)
        |                                      |
   ANON_KEY                          SERVICE_ROLE_KEY
        |                                      |
        v                                      v
  Supabase API  ----> RLS 정책 적용    Supabase API  ----> RLS 우회
        |                                      |
   허용된 행만 반환                      전체 행 반환

anon 키는 RLS 정책이 없는 테이블에서도 기본적으로 모든 데이터를 차단한다. 반드시 RLS를 활성화하고 정책을 작성해야 데이터가 보인다.

환경 변수 설정 방법

.env.local 파일에 아래와 같이 작성한다.

NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...

# 서버에서만 사용 — NEXT_PUBLIC 접두사 금지
SUPABASE_SERVICE_ROLE_KEY=eyJ...

Next.js에서 NEXT_PUBLIC_ 접두사가 붙은 변수는 번들에 포함되어 브라우저에서 읽힌다. SERVICE_ROLE_KEY에는 절대 이 접두사를 붙이지 않는다.

클라이언트와 서버 클라이언트 분리

// lib/supabase/client.ts — 브라우저용
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
// lib/supabase/admin.ts — 서버 전용
import { createClient } from '@supabase/supabase-js'

export const adminClient = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

adminClient는 API Route, Server Action, Edge Function 안에서만 import해야 한다. 'use client' 컴포넌트에서 import하면 키가 번들에 포함된다.

키 노출 여부 확인

빌드 후 번들에 서비스 키가 포함됐는지 확인하는 방법이다.

# Next.js 빌드 후 정적 파일에서 키 검색
grep -r "SERVICE_ROLE" .next/static

결과가 나오면 해당 키는 즉시 Supabase 대시보드에서 재발급해야 한다.

RLS 없이 anon 키를 쓰면 어떻게 되나

RLS가 비활성화된 테이블은 anon 키만으로 전체 데이터를 읽을 수 있다. 테이블을 만들 때 반드시 RLS를 켜고, 최소 권한 원칙에 따라 정책을 작성한다.

-- RLS 활성화
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- 본인 행만 읽기 허용
CREATE POLICY "본인 프로필만 조회"
  ON profiles FOR SELECT
  USING (auth.uid() = user_id);

SERVICE_ROLE_KEY는 관리자 작업(배치 처리, Webhook, 어드민 페이지)에만 쓰고, 나머지는 anon 키와 RLS 조합으로 처리하는 것이 원칙이다.

← 이전 글Supabase Type Generation — 스키마 기반 타입 자동 생성
다음 글 →Supabase 보안 — RLS와 API 키 관리 체크리스트