블로그로 돌아가기
Javascript

supabase rls 정책 설정: 개념부터 실무 적용까지 단계별 가이드

supabase rls 정책 설정 방법을 개념, 실무 예제, 주의점 중심으로 정리합니다. RLS 규칙 작성부터 JWT 기반 권한 연동, 운영 체크리스트까지 실무 적용 관점에서 설명합니다.

supabaserlspostgresqlauthenticationdatabase-security
supabase rls 정책 설정과 적용 예시를 보여주는 기술 블로그 썸네일

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() 사용), 인덱스 및 성능 검증이 필요합니다.
  • 복잡한 비즈니스 로직은 애플리케이션 레이어와 적절히 분리하세요.
  • 배포 전 스테이징 테스트와 운영용 감사/롤백 절차를 준비하세요.
  • 실무 체크리스트를 통해 정책 변경 리스크를 최소화하세요.
supabase rls 정책 설정 가이드: 실무 적용과 주의점 | RYUSLOG DEV