"supabase vs drizzle"을 검색해서 승자를 찾으려 했다면, 반전이 있습니다. 승자가 없다는 것입니다. Supabase는 Postgres 백엔드(Backend-as-a-Service)이고 Drizzle은 데이터베이스 연결(Supabase 포함) 위에서 실행되는 TypeScript ORM이기 때문입니다. 둘은 스택의 다른 레이어에 있으므로 실제로는 경쟁하지 않습니다. 걱정하지 마세요, 이건 생각보다 간단합니다.

한눈에 보는 Supabase vs Drizzle

대부분의 "vs" 페이지가 건너뛰는 나란히 비교가 여기 있습니다. 이 두 가지가 얼마나 겹치지 않는지 보세요. 그것이 핵심입니다.

겹치는 부분이 거의 없는 것 보이나요? Supabase는 "내 앱이 어디에 살아야 하나?"라는 질문에 답하고, Drizzle은 "TypeScript에서 어떻게 쿼리할 것인가?"라는 질문에 답합니다. supabase vs drizzle을 검색하든 drizzle vs supabase를 검색하든, 이 차이가 아래의 모든 결정을 형성합니다.

Supabase가 실제로 하는 일

Supabase는 일반적인 앱이 첫날부터 필요한 거의 모든 것을 번들로 제공하는 관리형 Postgres 백엔드입니다. BaaS(Backend-as-a-Service)는 직접 서버를 구축하지 않고도 실제 백엔드(데이터베이스 + 서비스)를 얻는다는 의미입니다. 핵심 단어는 '실제'입니다. 내부적으로는 진정한 PostgreSQL이지, 나중에 벗어날 수 없는 독점 추상화가 아닙니다.

기본으로 제공되는 것:

  • Auth — 이메일/비밀번호, 매직 링크, 소셜 로그인, SSO, 비밀번호 없는 인증.
  • Storage — S3 기반 파일 스토리지, Row Level Security 정책, 이미지 변환.
  • Realtime — 데이터베이스 변경, presence, 브로드캐스트 채널을 구독.
  • Edge Functions — Deno 위의 TypeScript 함수, 전 세계에 분산.
  • PostgREST를 통한 자동 REST API(테이블을 즉시 REST API로 변환) 및 pg_graphql을 통한 GraphQL.
  • 임베딩을 위한 벡터 지원으로 AI 기능을 위한 홈 제공.

앱에서 이 모든 것과 통신하는 기본 방식은 supabase-js, 공식 클라이언트 라이브러리입니다. 데이터베이스 읽기/쓰기(PostgREST를 통해 라우팅됨), auth 세션, 실시간 구독, 파일 업로드를 처리합니다. 전체 플랫폼을 위한 하나의 클라이언트입니다. 애초에 백엔드를 비교하고 있다면, 우리의 전체 Supabase vs Firebase 분석이 의사 결정의 그 부분을 자세히 다룹니다.

Drizzle이 실제로 하는 일

Drizzle은 경량 TypeScript ORM과 쿼리 빌더입니다. ORM(객체-관계형 매퍼)은 단순히 원시 SQL 문자열 대신 프로그래밍 언어로 데이터베이스 쿼리를 작성하게 해주는 라이브러리입니다. 하지만 Drizzle은 SQL에 신선할 정도로 가깝게 유지하므로 무거운 추상화와 싸울 일이 없습니다.

눈에 띄는 특징:

  • 작은 용량 — 약 7.4kb min+gzip이고 외부 의존성이 없어 edge 친화적입니다.
  • drizzle-kit — generate, migrate, pull(기존 데이터베이스를 TypeScript로 내재)을 처리하는 CLI.
  • Drizzle Studio — 로컬 개발을 위한 무료 시각적 데이터베이스 브라우저.
  • 타입 추론 — TypeScript에서 스키마를 한 번 정의하면 쿼리 결과가 자동으로 완전히 타입화됩니다.
  • 다중 데이터베이스 — Postgres, MySQL, SQLite 등과 함께 작동합니다.
  • 완전 오픈 소스 — $0입니다.

이제 중요한 부분입니다: Drizzle은 백엔드가 아닙니다. auth, storage, realtime, API 서버가 없습니다. 정확히 한 가지만 합니다. 타입 안전한 방식으로 데이터베이스와 통신합니다. "supabase orm" 검색에 대한 답으로 "ORM"이라고 부르는 것이 이상하지 않은 이유입니다. Supabase는 무거운 ORM을 자체적으로 제공하지 않으므로, 사람들이 하나를 원할 때 Drizzle(또는 Prisma)을 찾습니다.

supabase-js vs Drizzle: 같은 쿼리, 두 가지 방식으로 작성

supabase-js와 Drizzle의 차이: supabase-js는 전체 플랫폼 클라이언트(PostgREST를 통한 데이터베이스, plus auth, realtime, storage)이고, Drizzle은 데이터베이스 쿼리만 하는 타입 안전 Postgres ORM입니다. 구체적으로 만들기 위해, 정확히 같은 쿼리 — "저자와 함께 게시된 포스트 가져오기" —를 먼저 supabase-js로 작성하고, 그다음 Drizzle로 작성한 것입니다.

먼저, supabase-js (PostgREST) 버전:

이제 동일한 쿼리의 Drizzle 버전:

느낌의 차이를 알아차렸나요? supabase-js는 PostgREST 체이닝 — .from().select().eq() — 을 사용하고 중첩된 author:authors(name) 문자열 구문을 통해 조인을 표현하는데, 간단한 형태에는 아름답게 읽힙니다. Drizzle은 TypeScript 슈퍼파워가 있는 SQL처럼 읽힙니다. 명시적 조인, 명시적 조건, 결과 타입이 별도의 타입 생성 단계 없이 스키마에서 직접 추론됩니다.

어느 것도 진공에서 "더 낫다"고 할 수 없습니다. supabase-js 쿼리는 더 짧고 플랫폼과 함께 제공되지만, Drizzle 쿼리는 조인에 대한 컴파일 타임 안전성을 제공하고 세 개의 조인, 조건부 필터, 집계가 있을 때 더 명확해집니다. 이것이 실제 트레이드오프입니다.

Supabase에서 Drizzle이 정말 필요한가? (의사 결정 프레임워크)

아니오, Supabase에서 Drizzle이 필요한 경우는 거의 없습니다. supabase-js는 대부분의 앱을 자체적으로 프로덕션으로 제공합니다. 복잡한 쿼리에 대한 컴파일 타임 타입 안전성이나 더 작은 edge 번들을 원할 때만 Drizzle을 추가하세요. 의존성을 추가하기 전에 이 솔직한 의사 결정 행렬을 살펴보세요.

supabase-js를 고수하세요:

  • 실시간 구독, 파일/스토리지 작업, 또는 auth 흐름에 의존합니다.
  • 쿼리가 대부분 간단한 CRUD입니다.
  • 전체 앱을 위한 단일 클라이언트를 원합니다.
  • 프로토타이핑 중이고 최대 속도를 원합니다.

Drizzle을 추가하세요:

  • PostgREST 구문에서 어색해지는 복잡한 조인이나 집계가 있습니다.
  • 원시 SQL에 대한 컴파일 타임 타입 안전성을 원합니다.
  • 번들 크기가 중요한데, edge나 서버리스 런타임으로 배포하기 때문입니다.
  • 풀 리퀘스트에서 검토할 수 있는 스키마 기반 마이그레이션을 선호합니다.

둘 다 사용하세요 (가장 일반적인 결과):

  • auth, storage, 실시간을 위해 supabase-js를 원하고, 복잡한 데이터 쿼리를 위해 Drizzle을 원합니다.

하나의 쿼리보다는 전체 스택을 스케치하려면, 올바른 SaaS 기술 스택 선택이 백엔드와 ORM 의사 결정이 전체 그림에 어떻게 맞는지를 보여줍니다.

Supabase와 Drizzle을 함께 사용하는 방법 (올바른 설정)

네, Supabase와 Drizzle을 함께 사용할 수 있습니다. 대부분의 프로덕션 팀이 그렇게 합니다. auth, storage, 실시간을 위해 supabase-js를 유지하고, 타입 안전 데이터 쿼리를 위해 Drizzle을 같은 Supabase Postgres 연결로 가리킵니다. 몇 가지 정확성 세부 사항이 사람들을 걸려넘게 하지만, 한 곳에서 모두 다루겠습니다.

설치 및 연결 (postgres-js 드라이버)

필요한 세 가지 패키지를 설치하세요:

그다음 postgres-js 드라이버를 사용하여 연결하고 클라이언트를 Drizzle에 전달하세요:

그 prepare: false 플래그는 선택 사항이 아닙니다. 읽어 계속하세요. Supabase + Drizzle 설정이 깨지는 가장 일반적인 단일 이유이기 때문입니다.

Pooler vs 직접 연결: 어느 문자열을 사용하나?

Supabase는 두 개의 연결 문자열을 제공하며, 올바른 것은 런타임에 따라 다릅니다:

  • Pooler (포트 6543, 트랜잭션 모드) — 서버리스 및 edge 함수에 사용하세요. 각 호출은 단명(short-lived)이므로 트랜잭션별로 전달되는 풀된 연결을 원합니다.
  • 직접 연결 (포트 5432) — 연결을 열어둔 장시간 실행 서버에 사용하세요.

잘못된 것을 선택하면 연결이 소진되거나(서버리스에서 직접 연결) 불필요한 오버헤드가 추가됩니다. pooler도 다음 문제를 필요하게 만드는 정확히 그것입니다.

prepare: false 함정

prepare: false가 필요한 이유는 Supabase의 트랜잭션 모드 pooler가 준비된 명령문을 지원하지 않기 때문입니다. Drizzle의 postgres-js 드라이버는 기본적으로 이를 사용합니다. 이 두 사실을 함께 놓으면, 플래그 없이는 쿼리가 pooler를 통과하는 순간 오류를 던집니다. 서버리스에서는 항상 그렇습니다.

프로덕션에서 이것이 사람들을 물어뜯는 라인입니다. 모든 것이 로컬 직접 연결에 대해 작동한 다음

출처 바로가기