Supabase
핵심 원칙
1. Supabase는 자주 변경되므로 구현 전에 체인지로그와 최신 문서를 확인하세요. Supabase 기능에 대해 훈련 데이터에 의존하지 마세요. 함수 시그니처, config.toml 설정, API 규칙은 버전 간에 변경됩니다.
먼저 https://supabase.com/changelog.md를 가져온 후(가벼운 요약 인덱스 — 무거운 풀이 아님), 작업과 관련된 breaking-change 태그를 스캔하고, 해당하는 경우 링크된 페이지를 참조하세요. 그런 다음 아래 문서 접근 방법을 사용하여 관련 주제를 조회하세요.
2. 작업을 검증하세요. 수정 사항을 구현한 후, 테스트 쿼리를 실행하여 변경 사항이 작동하는지 확인하세요. 확인 없는 수정은 불완전합니다.
3. 오류에서 복구하고, 반복하지 마세요. 접근 방식이 2-3회 시도 후 실패하면 중단하고 재고하세요. 다른 방법을 시도하고, 문서를 확인하고, 오류를 더 주의 깊게 검사하고, 가능할 때 관련 로그를 검토하세요. Supabase 문제가 항상 동일한 명령어를 재시도하여 해결되는 것은 아니며, 답이 항상 로그에 있는 것도 아니지만, 진행하기 전에 로그를 확인하는 것이 종종 가치가 있습니다.
4. Data API에 테이블 노출: 사용자의 Data API 설정에 따라, 새로 생성된 테이블이 Data(REST) API를 통해 자동으로 노출되지 않을 수 있습니다. 이 경우 anon 및 authenticated 역할에 명시적으로 액세스 권한을 부여해야 합니다.
이는 RLS와는 별개입니다. RLS는 테이블에 접근 가능할 때 어떤 행이 보이는지를 제어하며, 테이블 자체의 접근 가능 여부를 제어하지 않습니다.
사용자가 SQL로 생성한 테이블에 예기치 않게 접근할 수 없다고 보고하면, 해당 사용자의 Data API 설정과 역할에 명시적 GRANT SQL을 통해 액세스 권한이 부여되었는지 확인하세요. 공개(anon/authenticated) 액세스를 부여할 때는 항상 RLS도 활성화하세요. 전체 설정 워크플로는 Data API에 테이블 노출을 참조하세요.
5. 노출된 스키마의 RLS.
기본적으로 public을 포함하여 노출된 모든 스키마의 모든 테이블에서 RLS를 활성화하세요. Supabase에서는 노출된 스키마의 테이블이 anon/authenticated 역할에 액세스 권한이 있을 때 Data API를 통해 도달할 수 있으므로 이는 중요합니다(Data API에 테이블 노출 참조). 비공개 스키마의 경우 심층 방어 수단으로 RLS를 선호하세요. RLS를 활성화한 후에는 모든 테이블에 동일한 auth.uid() 패턴을 기본값으로 사용하지 말고 실제 액세스 모델과 일치하는 정책을 만드세요.
6. 보안 체크리스트. auth, RLS, 뷰, 스토리지 또는 사용자 데이터를 다루는 Supabase 작업을 할 때는 이 체크리스트를 실행하세요. 이는 자동으로 취약점을 생성하는 Supabase 특정 보안 함정입니다.
-
인증 및 세션 보안
- JWT 기반 권한 부여 결정에
user_metadata클레임을 절대 사용하지 마세요. Supabase에서raw_user_meta_data는 사용자가 편집 가능하며auth.jwt()에 나타날 수 있으므로 RLS 정책이나 다른 권한 부여 로직에 안전하지 않습니다. 대신 권한 부여 데이터를raw_app_meta_data/app_metadata에 저장하세요. - 사용자를 삭제해도 기존 액세스 토큰이 무효화되지 않습니다. 먼저 로그아웃하거나 세션을 취소하고, 민감한 앱의 경우 JWT 만료 시간을 짧게 유지하며, 엄격한 보장을 위해 중요한 작업에서는
session_id를auth.sessions에 대해 검증하세요. - 권한 부여에
app_metadata또는auth.jwt()를 사용하는 경우, 사용자의 토큰이 새로고침될 때까지 JWT 클레임이 항상 최신 상태가 아니라는 점을 기억하세요.
- JWT 기반 권한 부여 결정에
-
API 키 및 클라이언트 노출
service_role또는 시크릿 키를 공개 클라이언트에 절대 노출하지 마세요. 프론트엔드 코드에는 게시 가능한 키를 선호하세요. 레거시anon키는 호환성 용도로만 사용됩니다. Next.js에서NEXT_PUBLIC_환경 변수는 브라우저로 전송됩니다.- Management API, CLI 및 MCP 서버에는 범위가 지정된 개인 액세스 토큰을 사용하세요. 클래식 개인 액세스 토큰은 모든 조직과 프로젝트에 대한 사용자의 전체 계정 액세스 권한을 전달합니다. 스크립트, CI 및 에이전트의 경우 사용자가 작업에 필요한 조직, 프로젝트 및 권한으로 제한된 범위가 지정된 토큰을 생성하도록 하세요. 개인 액세스 토큰 가이드에는 각 Management API 엔드포인트 및 MCP 도구에 필요한 권한이 나열되어 있습니다. 추측하지 말고 거기서 권한을 조회하세요.
-
RLS, 뷰 및 권한 있는 데이터베이스 코드
- 뷰는 기본적으로 RLS를 우회합니다. Postgres 15 이상에서는
CREATE VIEW ... WITH (security_invoker = true)를 사용하세요. 이전 버전의 Postgres에서는anon및authenticated역할의 액세스를 취소하거나 노출되지 않은 스키마에 배치하여 뷰를 보호하세요. - UPDATE에는 SELECT 정책이 필요합니다. Postgres RLS에서 UPDATE는 먼저 행을 SELECT해야 합니다. SELECT 정책이 없으면 업데이트는 자동으로 0행을 반환합니다. 오류 없이 변경 사항도 없습니다.
auth.role()은 더 이상 사용되지 않습니다. 대신TO절을 사용하세요. Supabase는auth.role()대신 정책에서TO authenticated또는TO anon으로 대상 역할을 직접 지정하는 것을 권장합니다. 더 이상 사용되지 않는 것 외에도,auth.role() = 'authenticated'는 익명 로그인이 활성화되면 자동으로 중단됩니다. 익명 사용자는authenticatedPostgres 역할을 가지므로 사용자가 실제로 로그인했는지 여부와 관계없이 검사를 통과하기 때문입니다.-- 더 이상 사용되지 않음 (사용하지 마세요) create policy "example" on table_name for select using ( auth.role() = 'authenticated' );TO authenticated만 사용하는 것은 권한 부여 없는 인증입니다(BOLA / IDOR).TO authenticated는 역할만 확인합니다. 사용자가 액세스할 수 있는 행을 제한하지 않습니다. 올바른 패턴은TO authenticated와USING의 소유권 조건을 결합하는 것입니다:create policy "example" on table_name for select to authenticated using ( (select auth.uid()) = user_id );- UPDATE 정책에는
USING과WITH CHECK가 모두 필요합니다.WITH CHECK가 없으면 사용자가 행의user_id를 다른 사용자로 재할당할 수 있습니다:create policy "example" on table_name for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); SECURITY DEFINER함수는 RLS를 우회합니다.SECURITY DEFINER함수는 생성자의 권한으로 실행됩니다. 일반적으로bypassrls(예:postgres)가 있는 역할입니다. 권한 오류를 해결하기 위해SECURITY DEFINER를 추가하지 마세요. 이는 근본 원인을 해결하지 않고 자동으로 액세스 제어를 제거합니다.SECURITY INVOKER를 선호하세요.public의SECURITY DEFINER함수는 모든 역할이 호출할 수 있습니다. Postgres는 모든 새 함수에 대해 기본적으로PUBLIC에EXECUTE를 부여하므로,public의 모든SECURITY DEFINER함수는 추가 부여 없이anon및authenticated(PUBLIC에서 상속)가 호출할 수 있는 공개 API 엔드포인트입니다.SECURITY DEFINER가 진정으로 필요한 경우(예: 내부 조회 테이블에서 RLS 우회), 함수를 노출되지 않은 스키마에 유지하고, 함수 본문에 항상auth.uid()검사를 포함하며, 변경 후supabase db advisors를 실행하세요.
- 뷰는 기본적으로 RLS를 우회합니다. Postgres 15 이상에서는
-
스토리지 액세스 제어
- 스토리지 upsert에는 INSERT + SELECT + UPDATE가 필요합니다. INSERT만 부여하면 새 업로드는 허용되지만 파일 교체(upsert)는 자동으로 실패합니다. 세 가지 모두 필요합니다.
-
종속성 및 공급망 보안
- Supabase 패키지(
supabase-js,@supabase/ssr,supabase-py등)를 설치할 때는 항상 패키지 버전을 고정하고 잠금 파일을 커밋하세요. 전체 체크리스트는 npm 보안 가이드를 참조하세요.
- Supabase 패키지(
위에서 다루지 않은 보안 관련 문제는 Supabase 제품 보안 인덱스를 가져오세요: https://supabase.com/docs/guides/security/product-security.md
Supabase CLI
항상 --help를 통해 명령어를 발견하세요. 추측하지 마세요. CLI 구조는 버전 간에 변경됩니다.
supabase --help # 모든 최상위 명령어
supabase <group> --help # 하위 명령어 (예: supabase db --help)
supabase <group> <command> --help # 특정 명령어의 플래그
Supabase CLI 알려진 문제점:
supabase db query는 CLI v2.79.0+ 가 필요합니다 → 대신 MCPexecute_sql또는psql을 사용하세요supabase db advisors는 CLI v2.81.3+ 가 필요합니다 → 대신 MCPget_advisors를 사용하세요supabase login의 브라우저 흐름은 전체 계정 액세스 권한이 있는 클래식 토큰을 생성합니다. CI에서는 대신SUPABASE_ACCESS_TOKEN을 범위가 지정된 개인 액세스 토큰으로 설정하세요. 저장된 로그인보다 우선합니다. 데이터베이스 비밀번호로 연결하는 명령어(--password또는SUPABASE_DB_PASSWORD, 예:supabase db push)는 토큰의 권한에 제한을 받지 않습니다.- 명령형 마이그레이션 프로젝트에서는 먼저
supabase migration new <name>으로 새로운 수동 마이그레이션 파일을 생성하세요. 마이그레이션 파일 이름을 직접 만들거나 예상 형식을 기억에 의존하지 마세요. 선언적 스키마 프로젝트는supabase/schemas/에서 마이그레이션을 생성합니다. 아래 "스키마 변경 사항 만들기 및 커밋"을 참조하세요.
버전 확인 및 업그레이드: supabase --version을 실행하여 확인하세요. CLI 체인지로그 및 버전별 기능은 CLI 문서 또는 GitHub 릴리스를 참조하세요.
Supabase MCP 서버
설정 방법, 서버 URL 및 구성은 MCP 설정 가이드를 참조하세요.
연결 문제 해결 — 다음 단계를 순서대로 따르세요:
-
서버에 연결할 수 있는지 확인:
curl -so /dev/null -w "%{http_code}" https://mcp.supabase.com/mcp401은 토큰이 없어 예상된 결과이며 서버가 작동 중임을 의미합니다. 시간 초과 또는 "connection refused"는 서버가 다운되었을 수 있음을 의미합니다. -
.mcp.json구성 확인: 프로젝트 루트에 올바른 서버 URL이 포함된 유효한.mcp.json이 있는지 확인하세요. 없으면https://mcp.supabase.com/mcp를 가리키는 파일을 생성하세요. -
MCP 서버 인증: 서버에 연결할 수 있고
.mcp.json이 올바르지만 도구가 보이지 않으면 사용자가 인증해야 합니다. Supabase MCP 서버는 OAuth 2.1을 사용합니다. 사용자에게 에이전트에서 인증 흐름을 트리거하고, 브라우저에서 완료한 후 세션을 다시 로드하도록 안내하세요. 브라우저 흐름을 실행할 수 없는 CI 또는 기타 환경에서는 대신Authorization: Bearer헤더에 범위가 지정된 개인 액세스 토큰을 전달하세요. -
범위가 지정된 토큰 권한 확인: 범위가 지정된 개인 액세스 토큰을 사용하는 동안 도구가 "이 작업을 수행할 권한이 없습니다"로 실패하면 토큰에 해당 도구의 권한이 없거나 프로젝트가 토큰의 범위를 벗어난 것입니다. 개인 액세스 토큰 가이드에서 도구의 필수 권한을 확인하세요.
Supabase 문서
Supabase 기능을 구현하기 전에 관련 문서를 찾으세요. 우선 순위에 따라 다음 방법을 사용하세요:
- MCP
search_docs도구 (권장 — 관련 스니펫을 직접 반환) - 마크다운으로 문서 페이지 가져오기 — URL 경로에
.md를 추가하여 모든 문서 페이지를 가져올 수 있습니다. - 웹 검색 — 어떤 페이지를 봐야 할지 모를 때 Supabase 관련 주제를 검색합니다.
스키마 변경 사항 만들기 및 커밋
먼저 프로젝트에서 사용하는 스키마 워크플로를 결정하세요.
옵션 A: 선언적 스키마
supabase/schemas/가 존재하거나 config.toml이 schema_paths를 설정하는 경우 사용합니다. 해당 파일에서 원하는 스키마 상태를 편집한 다음 마이그레이션을 생성하고 검토하세요. 수동 마이그레이션을 작성하는 것으로 시작하지 마세요. 선언적 데이터베이스 스키마 가이드를 참조하세요.
옵션 B: 명령형 마이그레이션
프로젝트가 선언적 스키마를 사용하지 않는 경우 사용합니다.
스키마를 변경하려면 execute_sql(MCP) 또는 supabase db query(CLI)를 사용하세요. 이들은 마이그레이션 기록 항목을 생성하지 않고 데이터베이스에서 직접 SQL을 실행하므로 자유롭게 반복하고 준비가 되면 깔끔한 마이그레이션을 생성할 수 있습니다.
로컬 데이터베이스 스키마를 변경하기 위해 apply_migration을 사용하지 마세요. 호출할 때마다 마이그레이션 기록 항목을 작성하므로 반복할 수 없고, supabase db diff / supabase db pull이 빈 diff나 충돌하는 diff를 생성합니다. 사용하는 경우 첫 번째 시도에서 전달한 SQL에 갇히게 됩니다.
변경 사항을 마이그레이션 파일에 커밋할 준비가 되면:
- 어드바이저 실행 →
supabase db advisors(CLI v2.81.3+) 또는 MCPget_advisors. 문제를 수정하세요. - 위의 보안 체크리스트 검토 — 변경 사항에 뷰, 함수, 트리거 또는 스토리지가 포함된 경우.
- 마이그레이션 생성 →
supabase db pull <descriptive-name> --local --yes - 확인 →
supabase migration list --local
디버깅
Supabase 관련 요청에서 오류가 발생하면(예: Supabase REST API, Postgres 데이터베이스 또는 PostgREST의 오류 코드, 빈 결과, 예기치 않은 RLS 차단, Auth, Realtime, Edge Functions 또는 Storage와 같은 Supabase 서비스의 오류), 기억에 의존하지 않고 진단하거나 수정을 제안하기 전에 Supabase의 모니터링 및 디버깅 문서를 가져와야 합니다. 동일한 문서에는 느린 쿼리 및 누락된 인덱스와 같은 성능 최적화도 포함되어 있습니다.
참조 가이드
- 스킬 피드백 → references/skill-feedback.md 반드시 읽어야 할 때: 사용자가 이 스킬이 잘못된 지침을 제공했거나 정보가 누락되었다고 보고하는 경우.