Drizzle를 사용하면 Postgres 테이블에 대해 **행 단위 보안(RLS)**을 활성화할 수 있습니다. 다양한 옵션으로 정책을 생성하고, 해당 정책이 적용될 역할을 정의하고 관리할 수 있습니다.
Drizzle는 Postgres 정책과 역할을 원시(raw) 형태로 지원하며, 이를 원하는 방식으로 활용할 수 있습니다. 이 기능은 Neon이나 Supabase와 같은 인기 있는 Postgres 데이터베이스 프로바이더와 함께 작동합니다.
Drizzle에서는 두 데이터베이스 프로바이더를 위한 특정 사전 정의된 RLS 역할과 함수를 제공합니다. 하지만 여러분이 직접 로직을 정의할 수도 있습니다.
RLS 활성화
테이블에 정책을 추가하지 않고 RLS(Row Level Security)만 활성화하려면 .enableRLS()를 사용할 수 있습니다.
PostgreSQL 문서에 언급된 대로:
테이블에 정책이 존재하지 않으면 기본적으로 거부 정책이 적용됩니다. 이는 어떤 행도 보이지 않거나 수정할 수 없음을 의미합니다.
TRUNCATE 및 REFERENCES와 같은 전체 테이블에 적용되는 작업은 행 수준 보안의 영향을 받지 않습니다.
중요
테이블에 정책을 추가하면 RLS가 자동으로 활성화됩니다. 따라서 테이블에 정책을 추가할 때 RLS를 명시적으로 활성화할 필요가 없습니다.
역할(Roles)
현재 Drizzle은 아래와 같이 몇 가지 옵션을 통해 역할을 정의할 수 있습니다. 더 많은 옵션에 대한 지원은 추후 릴리스에서 추가될 예정입니다.
만약 데이터베이스에 이미 역할이 존재하고, drizzle-kit이 이를 ‘인식’하거나 마이그레이션에 포함시키지 않도록 하려면, 해당 역할을 기존 역할로 표시할 수 있습니다.
정책
RLS(행 수준 보안)를 최대한 활용하려면 Drizzle 테이블 내에서 정책을 정의할 수 있습니다.
info
PostgreSQL에서 정책은 기존 테이블에 연결되어야 합니다. 정책은 항상 특정 테이블과 연결되기 때문에, 정책 정의를 pgTable의 매개변수로 정의하기로 결정했습니다.
모든 가능한 속성을 포함한 pgPolicy 예제
정책 옵션
as
가능한 값은 permissive 또는 restrictive입니다.
to
정책이 적용될 역할을 지정합니다. 가능한 값은 public, current_role, current_user, session_user 또는 문자열로 된 다른 역할 이름입니다. pgRole 객체를 참조할 수도 있습니다.
for
이 정책이 적용될 명령을 정의합니다. 가능한 값은 all, select, insert, update, delete입니다.
using
정책 생성 문의 USING 부분에 적용될 SQL 문입니다.
withCheck
정책 생성 문의 WITH CHECK 부분에 적용될 SQL 문입니다.
기존 테이블에 정책 연결
데이터베이스에 이미 존재하는 테이블에 정책을 연결해야 하는 경우가 있습니다.
가장 일반적인 사용 사례는 Neon이나 Supabase와 같은 데이터베이스 프로바이더에서,
기존 테이블에 정책을 추가해야 할 때입니다. 이 경우 .link() API를 사용할 수 있습니다.
마이그레이션
drizzle-kit을 사용해 스키마와 역할을 관리할 때, Drizzle 스키마에 정의되지 않은 역할을 참조해야 하는 경우가 있습니다. 이럴 때는 drizzle-kit이 각 역할을 스키마에 정의하고 .existing()으로 표시하지 않고도 해당 역할을 관리하지 않도록 할 수 있습니다.
이런 경우 drizzle.config.ts 파일에서 entities.roles를 사용할 수 있습니다. 자세한 내용은 drizzle.config.ts 문서를 참고하세요.
기본적으로 drizzle-kit은 역할을 관리하지 않으므로, 이 기능을 사용하려면 drizzle.config.ts에서 활성화해야 합니다.
추가 설정 옵션이 필요한 경우, 몇 가지 예제를 살펴보겠습니다.
admin 역할이 있고, 이를 관리 가능한 역할 목록에서 제외하려는 경우
admin 역할이 있고, 이를 관리 가능한 역할 목록에 포함하려는 경우
Neon을 사용 중이고, Neon에서 정의한 역할을 제외하려는 경우 provider 옵션을 사용할 수 있습니다
Supabase를 사용 중이고, Supabase에서 정의한 역할을 제외하려는 경우 provider 옵션을 사용할 수 있습니다
중요
데이터베이스 프로바이더가 지정한 새로운 역할에 비해 Drizzle이 약간 뒤처지는 상황이 발생할 수 있습니다.
이런 경우 provider 옵션과 exclude를 함께 사용해 추가 역할을 제외할 수 있습니다:
뷰에 RLS 적용하기
Drizzle를 사용하면 뷰에도 RLS(행 수준 보안) 정책을 지정할 수 있습니다. 이를 위해서는 뷰의 WITH 옵션에서 security_invoker를 사용해야 합니다. 아래는 간단한 예제입니다:
이 예제에서는 rooms_users_profiles라는 뷰를 생성하면서 securityInvoker 옵션을 활성화했습니다. 이렇게 하면 뷰에 접근할 때 RLS 정책이 적용됩니다.
Neon과 함께 사용하기
Neon 팀은 우리의 원시 정책 API 위에 래퍼를 구현하는 데 도움을 주었습니다. 우리는 미리 정의된 함수와 Neon의 기본 역할을 포함하는 crudPolicy 함수를 사용하여 /neon 임포트를 정의했습니다.
다음은 crudPolicy 함수를 사용하는 예제입니다:
이 정책은 다음과 동일합니다:
Neon은 미리 정의된 authenticated와 anonymous 역할 및 관련 함수를 제공합니다. RLS(행 수준 보안)를 위해 Neon을 사용한다면, 이러한 역할과 관련 함수를 RLS 쿼리에서 사용할 수 있습니다.
예를 들어, Neon의 미리 정의된 역할과 함수를 다음과 같이 사용할 수 있습니다:
Supabase와 함께 사용하기
Drizzle은 Supabase와 함께 사용할 수 있도록 미리 정의된 역할들을 포함한 /supabase 임포트를 제공합니다. 이 임포트는 향후 릴리스에서 RLS(Row Level Security)와 Supabase를 더 쉽게 사용할 수 있도록 추가 함수와 헬퍼들로 확장될 예정입니다.
예를 들어, Supabase의 미리 정의된 역할을 다음과 같이 사용할 수 있습니다:
/supabase 임포트에는 애플리케이션에서 사용할 수 있는 미리 정의된 테이블과 함수들도 포함되어 있습니다.
이를 통해 코드에서 사용할 수 있으며, Drizzle Kit은 이를 기존 데이터베이스로 간주하고 다른 엔티티와 연결하는 데 필요한 정보로만 사용합니다.
Supabase에 존재하는 테이블에 정책을 추가하는 예제를 확인해 보겠습니다.
Drizzle RLS를 Supabase와 함께 사용하는 방법과 실제 쿼리를 수행하는 방법을 보여주는 훌륭한 예제도 있습니다. 이 예제에는 Supabase와의 모든 트랜잭션 작업을 처리할 수 있는 createDrizzle 래퍼도 포함되어 있습니다. 향후 릴리스에서는 이 래퍼가 drizzle-orm/supabase로 이동하여 기본적으로 사용할 수 있게 될 예정입니다.