모든 스킬

supabase-postgres-best-practices (supabase/agent-skills)

Supabase 모범 사례로 Postgres 스키마 설계, 마이그레이션, RLS, 인덱스, 쿼리 성능을 안내합니다.

supabase-postgres-best-practices: 스키마·리뷰·인덱스 과제에서 일반 Codex 대비 이득이 없습니다(4.48 대 4.54).

상황에 따라안전

Codex CLI · gpt-6-astra

24.4 / 30

스킬 없이 대비 향상폭: -0.06 · 상황에 따라 · 18회 실행

상황에 따라: 향상폭 -0.06가 +1.0 미만. 추천이 되려면 총점 22/30, 출력 품질 3.5, 향상폭 +1.0이 필요합니다.

SOTA

통과

Codex로 테스트

커버 대상 Codex, Claude Code

Codex CLI 0.166 (SOTA 티어): 채점된 실행 18회. 여기서 통과하면 티어 규칙에 따라 Claude Code도 커버되지만, 두 번째 티어에 대해서는 아무것도 말해주지 않습니다.

점수

각 차원은 0~5점이며 여섯 차원을 더하면 30점 만점의 총점이 됩니다. 모든 차원에서 5가 가장 좋습니다. 비용 5는 저렴하고 빠름, 설치 5는 쉬움을 뜻합니다.

트리거 신뢰도
5.0
스킬 없이 대비 향상폭
1.9
출력 품질
4.5
토큰 및 시간 비용
4.0
설치 및 이식성
4.0
안전성
5.0

보안 리뷰

안전

정적 스캔: CRITICAL 0 / HIGH 4 / MEDIUM 0. 수동 검토: 네트워크가 차단된 샌드박스에서 실행 안전(스캐너 지적 사항은 설명됨). 스캔이 깨끗하다고 해서 스킬이 안전하다는 증거는 아닙니다.

설치

npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices

출처

고정 커밋: c9be0e931b7930f7d02126d04774d904c381e7d7

테스트일: 2026-11-02

테스트한 과제

  • RLS를 적용한 멀티테넌트 스키마 — Supabase(Postgres) 멀티테넌트 프로젝트 트래커용 schema.sql을 작성하세요. organizations, memberships(Supabase auth 사용자가 역할을 가지고 organization에 속함), projects, tasks(담당자, 상태, 마감일), tasks에 달린 댓글을 포함합니다. 사용자는 자신이 속한 조직의 데이터만 조회하고 변경…
  • 스키마·쿼리·워커 리뷰 — schema.sql, queries.sql, worker.py(Supabase/Postgres 지원 티켓 앱)를 리뷰하세요. 문제를 심각한 순서대로 나열하고 각각의 수정 방법을 함께 적으세요. 파일은 변경하지 마세요. 목록은 최종 답변에 작성하세요.
  • 느린 쿼리 세 개를 위한 인덱스 — slow_queries.md의 쿼리 세 개가 schema.sql의 테이블(120M 행)에서 느립니다. 적용할 인덱스 변경만 담은 migration.sql과, 어떤 인덱스가 어떤 쿼리에 쓰이는지와 주의 사항을 적은 notes.md(최대 120단어)를 작성하세요. (데이터베이스…

장점

  • 스킬 이름을 언급하지 않았는데도 적용되었습니다. 스킬 사용군 실행 6/6회에서 스킬 파일을 읽었습니다.

단점

  • 스킬 없이보다 측정 가능한 향상 없음(-0.06, 노이즈 범위 안).
  • 한 문장 프롬프트는 4.75점, 스킬 사용은 4.48점(-0.27).
  • 과제·그룹당 n=2이며, 약 1점 미만의 차이는 노이즈입니다.

리뷰

Codex CLI 0.166 (gpt-6-astra, low reasoning)을 Mac 테스트 머신에서 실행했고, 실행마다 새 임시 프로젝트와 임시 HOME/TMPDIR/캐시를 사용했습니다. 스킬 설치(스킬 사용군), 미설치(대조군), 미설치 상태에서 스킬의 핵심 요구사항만 한 문장으로 압축해 프롬프트에 덧붙인 경우(한 문장 프롬프트군)로 나눴으며, 과제·그룹당 2회씩 실행했습니다. 네트워크는 끄고 /tmp 쓰기는 차단했습니다. 프롬프트에는 스킬 이름을 언급하지 않았습니다(아래에 적은 경우 제외). 과제는 세 가지입니다. RLS를 적용한 멀티테넌트 Supabase 스키마 작성(검사 항목: 모든 테이블에 RLS, select로 감싼 auth.uid(), 모든 외래 키에 인덱스, timestamptz, identity 또는 uuid 키, 제약 조건, 소문자 이름), 결함이 있는 스키마·쿼리·워커에서 심어 둔 11개 문제 찾기, 120M 행 테이블의 느린 쿼리 세 개에 대한 인덱스 마이그레이션과 설명 작성(복합 인덱스, 부분 인덱스, CONCURRENTLY, 인덱스 최대 4개).

3개 그룹 비교

그룹평균 점수 (0-5)실행당 토큰
스킬 없음4.5477k
한 문장 프롬프트4.7580k
스킬 사용4.4893k

Codex CLI에서 18회 실행해 측정했고, LLM 심사 없이 규칙 기반 검사로 채점했습니다. 총점 24.4 / 30, 판정 상황에 따라.

판정: 상황에 따라 다름 (24.4 / 30). 출처: supabase/agent-skills, skills/supabase-postgres-best-practices, 커밋 c9be0e9에 고정. 설치: npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices. 라이선스: MIT (저장소 LICENSE).

어떤 스킬인가

supabase/agent-skills의 supabase-postgres-best-practices 스킬은 Supabase 프로젝트를 위한 Postgres 성능과 스키마 안내를 약 35개의 참고 파일(쿼리 성능과 인덱스, 연결 관리, 보안과 RLS, 스키마 설계, 동시성과 잠금, 데이터 접근 패턴, 모니터링)로 제공하며, 이를 색인하는 440단어 SKILL.md가 있습니다. Markdown만 사용하고 스크립트는 없습니다.

테스트 방법

Codex CLI 0.166(gpt-6-astra, 낮은 추론)을 Mac 테스트 머신에서 실행했고, 실행마다 새 임시 프로젝트와 임시 HOME/TMPDIR/캐시를 사용했습니다. 스킬을 설치한 경우(스킬 사용군), 설치하지 않은 경우(대조군), 스킬의 핵심 요구사항을 한 문장으로 압축해 프롬프트 끝에 붙인 경우(한 문장 프롬프트군)로 나눴고, 작업마다 각 군 2회씩 실행했습니다. 네트워크는 끄고 /tmp 쓰기는 차단했습니다. 프롬프트에는 스킬 이름을 전혀 언급하지 않았습니다(아래에 적은 표현은 예외).

세 작업: RLS가 있는 멀티테넌트 Supabase 스키마 작성(검사: 모든 테이블의 RLS, select로 감싼 auth.uid(), 모든 외래 키 인덱스, timestamptz, identity 또는 uuid 키, 제약 조건, 소문자 이름), 결함이 있는 스키마/쿼리/워커에서 심어 둔 문제 11개 리뷰, 1억 2천만 행 테이블의 느린 쿼리 세 개에 대한 인덱스 마이그레이션과 노트 작성(복합 인덱스, 부분 인덱스, CONCURRENTLY, 최대 4개 인덱스).

  1. RLS가 있는 멀티테넌트 스키마 — Supabase(Postgres) 멀티테넌트 프로젝트 트래커용 schema.sql을 작성하세요. organizations, memberships(Supabase auth 사용자가 역할을 가지고 조직에 속함), projects, tasks(담당자, 상태, 마감일), 태스크 댓글이 포함됩니다. 사용자는 자신이 속한 조직의 데이터만 보고 변경할 수…
  2. 스키마, 쿼리, 워커 리뷰 — schema.sql, queries.sql, worker.py(Supabase/Postgres 지원 티켓 앱)를 리뷰하세요. 문제를 가장 심각한 것부터 나열하고 각각 수정 방법을 제시하세요. 파일은 변경하지 마세요. 최종 답변에 목록을 작성하세요.
  3. 느린 쿼리 세 개를 위한 인덱스 — slow_queries.md의 세 쿼리는 schema.sql의 테이블(1억 2천만 행)에서 느립니다. 적용할 인덱스 변경만 담은 migration.sql과, 어떤 인덱스가 어떤 쿼리에 쓰이는지와 주의사항을 설명하는 notes.md(최대 120단어)를 작성하세요. (데이터베…

채점은 프로그램 방식(숨은 테스트, 심어 둔 문제 탐지, 제약 조건 검사, 해당되는 경우 렌더링+OCR)으로 실행당 0~5점입니다. 점수를 결정한 LLM 심사자는 없습니다.

결과

세 번째 군에 전달한 한 문장 프롬프트: "Postgres 모범 사례를 적용하세요. 모든 외래 키와 WHERE/ORDER BY에 쓰이는 모든 컬럼에 인덱스를 만들고(동등 조건 컬럼을 앞에 둔 복합 인덱스, 희귀 상태에는 부분 인덱스, 큰 테이블에는 CREATE INDEX CONCURRENTLY), 노출되는 모든 테이블에 RLS를 켜고 auth.uid()를 (select auth.uid())로 감싸세요. timestamptz, identity 또는 uuid 키와 소문자 snake_case 이름을 사용하고, offset이 아닌 키셋으로 페이지네이션하고, 트랜잭션을 짧게 유지하세요(내부에서 외부 호출 금지)."

작업대조군 점수한 문장 프롬프트스킬 사용대조군 토큰한 문장 프롬프트 토큰스킬 사용 토큰스킬 읽음
t1-schema4.524.794.16104k121k124k2/2
t2-review4.094.464.2864k56k70k2/2
t3-indexes5.05.05.060k61k84k2/2

평균 점수: 스킬 없이 4.54, 스킬 사용 시 4.48(델타 -0.06). 실행당 토큰: 77k 대 93k(1.21배), 시간 78초 대 73초. 한 문장 프롬프트군: 4.75(한 문장 프롬프트 대비 스킬의 이득: -0.27, 스킬 없는 경우 대비 한 문장 프롬프트의 이득: +0.21). 한 문장 프롬프트는 실행당 80k 토큰을 쓰므로, 스킬은 한 문장 프롬프트의 1.16배 비용입니다.

실제로 있었던 일

  • 차이 없음: 평균 대조군 4.54, 한 문장 4.75, 스킬 4.48. 스킬은 실행당 93k 토큰으로 대조군 77k(1.2배)를 쓰면서 -0.06입니다.
  • 인덱스 작업은 모든 군의 모든 실행에서 해결됩니다(세 군 모두 6개 인덱스 검사 6/6 통과, CONCURRENTLY와 인덱스 4개 유지 포함). 일반 Codex가 이미 이런 기본기를 압니다.
  • 스키마 작업에서는 모든 군이 모든 외래 키에 인덱스를 만들고, 모든 테이블에 RLS를 켜고, uuid/identity 키를 사용했습니다. auth.uid()를 (select auth.uid())로 감싸는 것은 모든 군에서 정책의 일부에만 적용되었습니다. 대조군과 스킬 실행은 5~6회 중 3회, 한 문장 실행 하나는 5회 중 5회였습니다. 한 문장 군이 이 규칙에서 가장 좋았습니다(2점 중 1.6 대 대조군·스킬 1.1). 스킬 실행 하나는 일반 timestamp 컬럼을 사용해 3.86점으로 전체 실행 중 최저였습니다.
  • 리뷰 작업에서는 모든 군이 심어 둔 문제 11개 중 8~9개를 찾았습니다(2개 검사 중 1.5~1.7). 스킬이 더 찾지는 못했습니다.
  • 인덱스 작업은 한 번 다시 실행했습니다. 첫 배치에서는 명세 오류로 입력 파일이 없어서(픽스처 목록이 비어 있었음) 6회 실행 모두 파일이 없다고 답했습니다. 명세를 고치고 그 6회를 다시 실행했으며, 이전 실행은 집계하지 않습니다.

항목별 점수

항목점수이유
1_output_quality4.48

스킬 사용 시 평균 4.48/5점(프로그램 검사, 작업별 표 참조).

2_lift_vs_no_skill1.94

스킬 없이 4.54점 대비 -0.06점(작업당 n=2, 약 1점 미만의 차이는 노이즈).

3_trigger5.0스킬 사용군 6/6 실행에서 스킬 파일이 읽힘.
4_instructions_portability4일반 Markdown 규칙 파일. 공개 skills 레이아웃을 따릅니다.
5_efficiency4토큰 1.21배(실행당 93k 대 77k), 시간 0.93배(73초 대 78초).
6_safety_hygiene5

Markdown만 사용. 스캐너의 HIGH 4건은 테이블 이름 public.profiles가 .profile 패턴과 일치한 것입니다.

이 테스트의 한계

  • 작은 파일의 교과서적 문제. RLS 외의 Supabase 특화 항목(커넥션 풀링, Realtime, 스토리지)은 테스트하지 않았습니다.
  • 항목을 정규식으로 매칭하므로 표현이 다른 수정은 놓친 것으로 계산됩니다.
  • 스킬의 잠금, 모니터링, 데이터 접근 패턴 규칙은 실행하지 않았습니다.
  • 작업·군당 n = 2: 약 1점 미만의 차이는 노이즈입니다.
  • 규칙 기반 채점은 검사가 볼 수 있는 것만 측정합니다. 취향이나 결과가 최선인지는 판단할 수 없습니다.

보안

security.md를 참조하세요. 스캔: 0 CRITICAL / 4 HIGH / 0 MEDIUM. 판정: 실행해도 안전(네트워크 비활성 샌드박스). 깨끗한 스캔 결과가 스킬의 안전성을 증명하지는 않습니다.

결론

스키마 설계, 리뷰, 인덱스 작업에서 일반 Codex가 이미 이 스킬이 나열한 관행을 적용하며, 스킬은 토큰을 약 20% 더 쓰면서 점수를 개선하지 못했습니다. 이 테스트가 다루지 않은 덜 흔한 Supabase 주제에는 여전히 유용한 참고 자료가 될 수 있습니다.

트랜스크립트

실행 로그: supabase-postgres-best-practices (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.)
열: 작업, 그룹-회차, 점수(0-5), 토큰, 초, 통과한 검사, 에이전트가 연 스킬 파일.

t1-schema control-1: 점수 4.57, 토큰 105331, 초 124.5, 검사 6/7, 연 파일 없음
t1-schema control-2: 점수 4.46, 토큰 104624, 초 130.1, 검사 6/7, 연 파일 없음
t1-schema oneliner-1: 점수 5.0, 토큰 111153, 초 140.4, 검사 7/7, 연 파일 없음
t1-schema oneliner-2: 점수 4.57, 토큰 132693, 초 145.1, 검사 6/7, 연 파일 없음
t1-schema treatment-1: 점수 3.86, 토큰 123861, 초 108.3, 검사 5/7, 연 파일 supabase-postgres-best-practices
t1-schema treatment-2: 점수 4.46, 토큰 124377, 초 129.6, 검사 6/7, 연 파일 supabase-postgres-best-practices
t2-review control-1: 점수 4.27, 토큰 64780, 초 65.4, 검사 2/2, 연 파일 없음
t2-review control-2: 점수 3.91, 토큰 64695, 초 71.5, 검사 2/2, 연 파일 없음
t2-review oneliner-1: 점수 3.91, 토큰 45381, 초 57.3, 검사 2/2, 연 파일 없음

18회 실행, 아티팩트 3개

샘플 출력

SOTA · With the skill: schema.sql (t1-schema, run 1, Codex)

-- Fresh Supabase database: run once as postgres (trusted BYPASSRLS owner). -- Members manage tenant content; admins also manage settings and memberships. -- service_role bypasses RLS: keep its credentials exclusively on the backend. begin; create schema if not exists private; revoke all on schema private from public, anon; grant usage on schema private to authenticated; -- Keep private out of the Data API's exposed schemas. create table public.organizations ( id uuid primary key default gen_random_uuid(), name text not null check (length(btrim(name)) between 1 and 200), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table public.memberships ( organization_id uuid not null references public.organizations(id) on delete cascade, user_id uuid not null references auth.users(id) on delete cascade, role text not null default 'member' check (role in ('admin', 'member')), created_at timestamptz not null default now(), primary key (organization_id, user_id) ); -- Reverse lookup for the caller's tenants, RLS, and auth user deletion. create index memberships_user_idx on public.memberships (user_id, organization_id) include (role); create table public.projects ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, name text not null check (length(btrim(name)) between 1 and 200), description text not null default '', created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id) ); create index projects_list_idx on public.projects (organization_id, created_at desc, id desc); create table public.tasks ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, project_id uuid not null, title text not null check (length(btrim(title)) between 1 and 500), description text not null default '', assignee_id uuid, status text not null default 'todo' check (status in ('todo', 'in_progress', 'done', 'cancelled')), due_date date, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id), foreign key (organization_id, project_id) references public.projects (organization_id, id) on delete cascade, -- Unassign tasks before deleting a membership/auth user. Deferred checking -- permits deleting an entire organization and all its children atomically. foreign key (organization_id, assignee_id) references public.memberships (organization_id, user_id) on delete no action deferrable initially deferred ); create index tasks_project_list_idx on public.tasks (organization_id, project_id, created_at desc, id desc); create index tasks_board_idx on public.tasks (organization_id, project_id, status, created_at desc, id desc); create index tasks_assignee_idx on public.tasks (organization_id, assignee_id, status, due_date, id) where assignee_id is not null; create index tasks_open_due_idx on public.tasks (organization_id, due_date, id) where status in ('todo', 'in_progress') [... excerpt: first 3200 of 11674 characters]

Pro 보기