supabase rls 정책 설정: 개념부터 실무 적용까지 단계별 가이드
supabase rls 정책 설정 방법을 개념, 실무 예제, 주의점 중심으로 정리합니다. RLS 규칙 작성부터 JWT 기반 권한 연동, 운영 체크리스트까지 실무 적용 관점에서 설명합니다.
supabase rls 정책 설정이 필요한 상황
supabase rls 정책 설정은 사용자별 데이터 접근을 DB 레벨에서 강제하려는 경우 필수입니다. 이 글에서는 supabase rls 정책 설정의 핵심 개념, 실무 예제(테이블 수준 정책·JWT 연동), 주의점 및 운영 체크리스트를 통해 바로 적용할 수 있도록 안내합니다.
supabase rls 정책 설정 핵심 개념
Row Level Security(RLS)는 PostgreSQL의 기능으로, 테이블 행 단위로 접근 제어를 적용합니다. Supabase는 PostgreSQL을 기반으로 하므로 RLS를 통해 클라이언트에서 직접 DB에 접근하더라도 안전하게 권한을 보장할 수 있습니다. 기본 흐름은 다음과 같습니다.
- 테이블에 RLS 활성화
- POLICY 정의(USING, WITH CHECK)
- JWT나 세션을 통해 현재 사용자 정보를 policy 내부에서 사용
RLS는 애플리케이션 레이어의 검증을 보완하며, 특히 서버리스 또는 클라이언트-직접 접근 아키텍처에서 유용합니다.
supabase rls 정책 설정 실무 예제
아래 예제는 posts 테이블에서 작성자만 자신의 글을 읽고 수정할 수 있도록 하는 기본 정책입니다.
-- posts 테이블 생성 예시 create table posts ( id uuid primary key default gen_random_uuid(), user_id uuid not null, title text, body text ); -- RLS 활성화 alter table posts enable row level security; -- 모든 사용자는 자신의 행만 SELECT 가능 create policy "select_own_posts" on posts for select using (auth.uid() = user_id); -- 자신의 글만 INSERT 허용 (user_id 일치 강제) create policy "insert_posts" on posts for insert with check (auth.uid() = user_id); -- 자신의 글만 UPDATE/DELETE 가능 create policy "modify_own_posts" on posts for update, delete using (auth.uid() = user_id) with check (auth.uid() = user_id);
Supabase는 auth.uid() 함수를 통해 JWT의 sub 값을 가져옵니다. 클라이언트에서 JWT가 담긴 요청을 보내면 DB에서 자동으로 인증 정보를 사용하여 policy가 평가됩니다.
다음은 Next.js/React 환경에서 Supabase 클라이언트를 사용해 게시글을 조회하는 간단한 예시입니다.
import { createClient } from '@supabase/supabase-js'; const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY); async function fetchMyPosts() { const { data, error } = await supabase .from('posts') .select('*'); // RLS가 적용되어 현재 사용자 행만 반환 if (error) throw error; return data; }
언제 쓰면 좋고 언제 피해야 할까
언제 쓰면 좋은가:
- 클라이언트에서 DB에 직접 접근하는 구조(예: Supabase 사용)일 때
- 사용자 별 데이터 분리(멀티 테넌시)가 필요할 때
- 서버 사이드 로직을 최소화하고 DB에서 권한을 강제하려 할 때
언제 피해야 하는가:
- 매우 복잡한 권한 로직(복합적 비즈니스 규칙)이 DB까지 내려가기 어려운 경우
- 성능 상 세밀한 캐싱 또는 복잡한 조인이 빈번해 RLS가 병목을 유발할 가능성이 있는 경우
- 외부 시스템과 통합하여 권한을 중앙화해야 하는 경우(비즈니스 요구에 따라 서버 레이어가 더 적절할 수 있음)
실무 체크리스트
- 테이블별로 RLS 사용 필요성을 검토했는가?
- RLS 활성화 전 기존 쿼리 및 권한 영향을 테스트했는가?
- 필요한 POLICY를 최소 권한 원칙으로 작성했는가? (USING/ WITH CHECK 구분)
- JWT 클레임(auth.uid()) 매핑이 정확한가?
- 서비스 계정(관리자)용 정책/역할을 별도로 관리하는가?
- 정책 변경 시 마이그레이션 계획 및 롤백 시나리오가 준비되었는가?
성능·보안·운영 주의점
성능
- RLS는 쿼리 플랜에 영향을 주므로 대량 조인이나 복잡한 서브쿼리에서 성능 저하를 일으킬 수 있다. 인덱싱(특히 user_id 인덱스)을 확인하고 EXPLAIN을 활용하여 성능을 검증하세요.
보안
- auth.uid() 같은 함수는 JWT에 의존하므로 토큰 서명 키 관리가 중요합니다. Supabase의 서비스 키(서비스 역할 키)는 안전한 서버 사이드 환경에만 두세요.
- POLICY는 최소 권한 원칙으로 작성하고, 공개 테이블이 필요한 경우 명시적으로 allow policy를 추가하세요.
운영
- 정책 변경은 데이터 접근에 즉시 영향이 있으므로 배포 전 스테이징에서 시나리오 테스트를 수행하세요.
- 감사 로그(누가 언제 어떤 행을 접근/수정했는지)를 별도로 설계하세요.
비교표: RLS 적용 방식 판단 기준
| 구분 | 항목1 | 항목2 | 항목3 |
|---|---|---|---|
| 내용 | 클라이언트-직접접근 | 서버중앙관리 | 복잡 권한 로직 |
| 내용 | 적합(간단 사용자 분리) | 적합(중앙화 필요시) | 비적합(복잡한 비즈니스 룰) |
| 내용 | 보안: DB 레벨 강제 | 보안: 서버 레벨 제어 | 관리: 서버에서 처리 권장 |
자주 묻는 질문
Q: RLS만으로 모든 권한 관리를 대체해도 되나요? A: 대부분 사용자-행 수준 권한은 RLS로 충분하지만, 복잡한 비즈니스 규칙이나 외부 권한 연동은 애플리케이션 레이어에서 추가 검증이 필요합니다.
Q: auth.uid()가 없으면 어떻게 하나요? A: Supabase에서는 기본적으로 JWT의 sub를 auth.uid()로 노출합니다. 커스텀 클레임을 사용하려면 JWT 설정과 정책에서 해당 클레임을 참조하도록 수정해야 합니다.
Q: RLS 활성화가 성능에 미치는 영향은 어느 정도인가요? A: 작은 테이블과 단순 쿼리에서는 영향이 거의 없습니다. 다만 대량 데이터와 복잡한 조인이 많은 쿼리는 EXPLAIN으로 쿼리 플랜을 확인하고 인덱스 튜닝이 필요합니다.
Q: 서비스 계정이나 관리자 권한은 어떻게 처리하나요? A: 서비스 역할(서비스 키 사용)은 별도 정책을 두거나, 정책에서 role()이나 current_setting('jwt.claims.role') 등을 검사하여 예외를 허용할 수 있습니다.
정리
- supabase rls 정책 설정은 DB 레벨에서 사용자별 접근 제어를 강제하는 핵심 수단입니다.
- RLS 적용은 테이블별 활성화, POLICY 작성(auth.uid() 사용), 인덱스 및 성능 검증이 필요합니다.
- 복잡한 비즈니스 로직은 애플리케이션 레이어와 적절히 분리하세요.
- 배포 전 스테이징 테스트와 운영용 감사/롤백 절차를 준비하세요.
- 실무 체크리스트를 통해 정책 변경 리스크를 최소화하세요.