Skip to content

Supabase RLS는 뭔가요?

Published: at 오후 01:48

개요

Supabase를 본격적으로 쓰면서 가장 먼저 맞닥뜨린 개념 중 하나가 바로 RLS(Row-Level Security)였다. RLS는 그냥 보안 설정이 아니라, 데이터에 대한 권리와 책임을 설계하는 장치였다. 이 글은 Supabase에서 RLS를 설정하면서 내가 직접 겪은 시행착오와 배운 것들을 정리한 글이다.

목차

RLS란 무엇일까?

처음 Supabase에서 RLS를 마주했을 때, SELECT문을 실행해도 데이터를 출력하지 않았다. 이유는 RLS 때문이었다. Supabase는 RLS가 활성화되면 기본적으로 모든 쿼리를 차단한다. 권한이 있든 없든, 일단 막고 시작하고, 그 후에 policy를 통해 열 수 있는 조건을 명시적으로 설정해야 한다.

Postgres의 Row-Level Security 기능이다. 말 그대로 “Row 단위 보안”이다. 즉, 테이블의 행 단위로 접근 조건을 설정할 수 있다.

RLS 정책 만들기: USING과 WITH CHECK

-- 본인만 자신의 데이터를 SELECT할 수 있게 하는 정책
CREATE POLICY "Allow user to select their own data"
ON users
AS PERMISSIVE
FOR SELECT
USING (auth_id = auth.uid());
-- 회원가입 시 본인의 데이터만 insert할 수 있게 하는 정책
CREATE POLICY "Allow user to insert self"
ON users
AS PERMISSIVE
FOR INSERT
WITH CHECK (auth_id = auth.uid());

알아두면 좋은 함수, 예약어들

쿼리 안에서 마치 현재 유저현재 시간처럼 쓸 수 있는 내장 함수들이 존재한다. 내가 직접 써본 것들 위주로 정리해본다.

함수/예약어 설명

PERMISSIVE vs RESTRICTIVE

처음 정책을 여러 개 만들고 나서, 정책이 두 개일 땐 어떻게 동작할지 궁금했다. Supabase는 정책의 조합을 설정할 수 있다. 기본값은 PERMISSIVE, 즉 **하나라도 조건을 만족하면 허용**이다.

하지만 때로는 모든 조건을 만족해야 한다, 즉 AND처럼 동작하게 하고 싶을 때가 있다. 그때는 RESTRICTIVE로 바꿔주면 된다.

예를 들어, 아래 두 정책이 있다고 하자.

-- active_until > now()
-- is_hidden_from_search = false

정책 적용의 대상이 되는 역할(target roles)

처음에는 RLS에 TO authenticated, TO anon 같은 문구가 있어서 이게 뭔지 몰랐다.

target roles는 Supabase가 내부적으로 각 요청자에게 할당하는 역할이다. 정책에서 특정 역할에게만 적용되도록 할 수도 있다.

역할 설명

예를 들어, 아래처럼 설정하면 로그인한 사용자만 SELECT 가능하게 만들 수 있다.

CREATE POLICY "Logged-in users only"
ON payments
AS PERMISSIVE
FOR SELECT
TO authenticated
USING (auth_id = auth.uid());

service_role은 Supabase Edge Function이나 서버 측에서 admin 권한으로 호출할 때 사용된다. 이 역할은 RLS를 무시하기 때문에 실제 서비스에서는 조심해야 한다.

실전에서 내가 작성한 RLS 정책들

서비스를 만들면서 작성한 대표적인 RLS 정책 예시를 정리해봤다.

-- 디자이너 프로필: 본인만 수정 가능
CREATE POLICY "Update own profile"
ON designers
AS PERMISSIVE
FOR UPDATE
USING (auth_id = auth.uid())
WITH CHECK (auth_id = auth.uid());
-- 디자이너 검색은 비로그인도 가능, 단 공개된 프로필만
CREATE POLICY "Public can search designers"
ON designers
AS PERMISSIVE
FOR SELECT
USING (active_until > now() AND is_hidden_from_search = false);
-- 결제 내역은 본인만 조회 가능
CREATE POLICY "User can view their own payments"
ON payments
AS PERMISSIVE
FOR SELECT
USING (auth_id = auth.uid());

참고자료