Supabase를 쓰다 보면 반드시 마주치게 되는 개념 중 하나가 바로 RLS(Row-Level Security)입니다.
이 글에서는 RLS가 무엇이고, 왜 필요한지, Supabase에서는 어떻게 설정하고 활용하는지를 정리했습니다.
RLS란 무엇인가?
RLS(Row-Level Security)는 테이블의 각 행(row)에 대해 접근 권한을 제어하는 보안 기능입니다. PostgreSQL의 고급 기능 중 하나로, Supabase는 이 기능을 적극 활용해 사용자 단위 데이터 보호를 제공합니다.
예를 들어, 사용자가 자신의 할 일 목록만 조회할 수 있게 하려면 어떻게 해야 할까요?
전통적인 백엔드라면 쿼리마다 user_id 조건을 직접 걸어줘야겠지만, RLS를 활용하면 DB 레벨에서 정책(policy)을 설정해서 자동으로 필터링할 수 있습니다.
RLS가 없으면 생기는 문제
// 로그인된 유저가 아니라도 전체 todos를 다 조회할 수 있음
const { data } = await supabase.from('todos').select('*');
이런 코드가 있다면 모든 유저의 데이터가 노출될 수 있습니다.
Supabase에서의 RLS 활용
Supabase는 로그인한 사용자의 ID를 auth.uid()로 제공합니다. 이를 활용해 RLS 정책을 구성할 수 있습니다.
1. RLS 활성화
alter table todos enable row level security;
2. 행 단위 접근 정책(policy) 추가
create policy "Users can view own todos"
on todos for select
using (auth.uid() = user_id);
3. 삽입/수정 권한 제어도 가능
create policy "Users can insert their own todos"
on todos for insert
with check (auth.uid() = user_id);
프론트엔드에서는 어떻게 달라질까?
RLS 정책이 적용되면, 프론트엔드에서 다음처럼 단순한 쿼리를 날려도 안전합니다.
const { data } = await supabase.from('todos').select('*');
이제는 이 쿼리가 자동으로 로그인한 사용자에 해당하는 데이터만 반환합니다.
RLS는 Supabase에만 있는 개념일까?
아닙니다. RLS는 PostgreSQL의 기본 기능입니다. Supabase는 이를 래핑(wrapping)해서 더 쉽게 다룰 수 있도록 콘솔 UI와 API에서 제공할 뿐입니다.
구분
설명
Supabase
PostgreSQL 기반, auth.uid() 제공, RLS 정책 쉽게 구성 가능
PostgreSQL
원래 RLS 기능이 존재, 직접 SQL로 구성 필요
Supabase에서 RLS를 강조하는 이유
Supabase는 PostgreSQL 기반이기 때문에, PostgreSQL의 RLS 기능을 그대로 활용할 수 있습니다. 특히 BaaS 백엔드에서 클라이언트가 직접 쿼리를 날리는 구조이다 보니, RLS 없이 보안사고가 발생할 가능성이 높습니다. 그래서 RLS가 보안 핵심 기능으로 강조되는 것입니다.
요약
RLS는 사용자마다 다른 행(row)에 접근하도록 제한하는 데이터 보안 기능입니다.
Supabase에서는 auth.uid()를 통해 로그인 사용자의 ID를 받아 정책을 구성할 수 있습니다.
아래는 Supabase SQL Editor나 초기 설정 시 사용할 수 있는 회원 및 할 일 테이블 생성 쿼리 예시입니다.
회원 테이블: users
create table if not exists users (
id bigint primary key,
email text unique not null,
nickname text,
provider text,
created_at timestamp with time zone default timezone('utc'::text, now())
);
할 일 테이블: todos
create table if not exists todos (
id bigint generated always as identity primary key,
title text not null,
is_done boolean default false,
user_id bigint references users(id),
created_at timestamp with time zone default timezone('utc'::text, now())
);
RLS 정책 (todos)
alter table todos enable row level security;
create policy "Users can view own todos"
on todos for select
using (auth.uid()::bigint = user_id);
create policy "Users can insert their own todos"
on todos for insert
with check (auth.uid()::bigint = user_id);
마무리 및 다음 단계
Supabase는 인증, DB, 스토리지, 실시간 기능까지 제공하는 매우 강력한 BaaS입니다.