본문으로 건너뛰기
AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.

AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.

  • 홈
  • IT기술
    • RAG
    • GRPC
    • Kotlin
    • LLM
    • 금융 IT
    • 에이전트
    • 제로Trust
    • 자동화
  • 일상
    • 자동차
    • 경제/재테크
    • 생활정보
  • About
    • Contact
    • Terms of Service
    • Disclaimer
    • Privacy – Policy
  • 홈
  • IT기술
    • RAG
    • GRPC
    • Kotlin
    • LLM
    • 금융 IT
    • 에이전트
    • 제로Trust
    • 자동화
  • 일상
    • 자동차
    • 경제/재테크
    • 생활정보
  • About
    • Contact
    • Terms of Service
    • Disclaimer
    • Privacy – Policy
Supabase 대시보드가 표시된 노트북 작업 환경
IT기술

Supabase로 서버 없이 풀스택 앱 만들기 실전 가이드

By AICosmus
2026년 06월 24일 14 Min Read
0

사이드 프로젝트를 만들고 싶은 순간은 누구에게나 찾아옵니다. 머릿속에 반짝이는 아이디어를 당장 구현해보고 싶은데, 막상 시작하려면 데이터베이스는 뭘 쓸지, 사용자 로그인은 어떻게 만들지 고민이 꼬리에 꼬리를 물게 됩니다. 하지만 이제는 서버 없이도 인증, 데이터베이스, 파일 저장까지 갖춘 풀스택 앱을 만들 수 있는 시대가 열렸습니다. 프론트엔드 코드 한 줄 쓰기도 전에 인프라 설계에서 지쳐버릴 필요가 없어진 것이죠.

이런 장벽을 허물기 위해 등장한 개념이 BaaS(Backend as a Service)입니다. 서버를 직접 구축하고 운영하지 않아도 인증, 데이터베이스, 파일 저장, 서버리스 함수 같은 백엔드 핵심 기능을 API 호출 한 줄로 바로 쓸 수 있게 해주는 클라우드 서비스입니다. Google의 Firebase가 이 분야를 개척했지만, 2026년 현재 가장 빠르게 성장하며 개발자 커뮤니티의 열광적인 지지를 받고 있는 서비스는 단연 Supabase입니다.

Supabase는 “오픈소스 Firebase 대안”이라는 명쾌한 슬로건으로 2020년에 출발했습니다. 하지만 지금은 그 슬로건을 넘어, Firebase에는 없는 관계형 데이터베이스 PostgreSQL의 힘과 오픈소스 생태계의 유연함을 결합한 독자적인 플랫폼으로 자리잡았습니다. 무료 플랜만으로도 개인 프로젝트를 충분히 운영할 수 있고, 프로젝트가 커지면 유료 플랜으로 자연스럽게 확장할 수 있다는 점도 큰 매력입니다.

이 글에서는 Supabase가 무엇인지, 기존 Firebase와 어떻게 다른지, 그리고 실제로 간단한 앱 하나를 처음부터 만들어보는 과정까지 단계별로 안내하겠습니다. 백엔드 경험이 전혀 없는 프론트엔드 개발자, 코딩을 배우기 시작한 입문자, 또는 사이드 프로젝트의 백엔드를 빠르게 해결하고 싶은 분 모두를 위한 글입니다.

Supabase란 — 오픈소스 Firebase 대안이 만든 새로운 표준

Supabase를 한 문장으로 정의하면 PostgreSQL 위에 인증·API·스토리지·실시간 기능을 올려놓은 오픈소스 BaaS 플랫폼입니다. 사용자가 직접 서버를 구성하지 않아도, Supabase가 제공하는 대시보드와 클라이언트 라이브러리만으로 풀스택 애플리케이션의 백엔드를 완성할 수 있습니다.

핵심 기능은 크게 다섯 가지로 나뉩니다.

  • Authentication(인증) — 이메일·비밀번호, 소셜 로그인(Google, GitHub, Kakao 등), 매직 링크, 전화번호 인증까지 다양한 로그인 방식을 내장하고 있습니다. 별도의 인증 서버를 만들 필요가 없습니다.
  • Database(데이터베이스) — 세계에서 가장 신뢰받는 오픈소스 관계형 데이터베이스인 PostgreSQL을 그대로 사용합니다. NoSQL이 아닌 SQL을 쓸 수 있다는 점이 Firebase와의 가장 큰 차이입니다.
  • Storage(파일 저장) — 이미지, 동영상, 문서 같은 파일을 업로드하고 관리할 수 있는 S3 호환 스토리지입니다. 프로필 사진, 첨부파일 같은 기능을 손쉽게 추가할 수 있습니다.
  • Edge Functions(서버리스 함수) — 서버 없이 백엔드 로직을 실행할 수 있는 Deno 기반 서버리스 함수입니다. 결제 연동, 외부 API 호출, 웹훅 처리 같은 복잡한 작업에 활용합니다.
  • Realtime(실시간) — 데이터베이스의 변경 사항을 클라이언트에 실시간으로 전달합니다. 채팅 앱, 협업 도구, 실시간 대시보드 같은 기능을 별도의 WebSocket 서버 없이 구현할 수 있습니다.
Supabase 핵심 5대 기능 아키텍처 구조도 - 서버 없이

여기에 2024년부터 추가된 Vector(벡터 검색) 기능은 pgvector 확장을 통해 AI 임베딩을 데이터베이스에 직접 저장하고 유사도 검색을 수행할 수 있게 해줍니다. RAG(검색 증강 생성) 파이프라인을 별도의 벡터 DB 없이 Supabase 안에서 완성할 수 있다는 뜻이죠. AI 시대에 걸맞은 기능입니다.

무엇보다 Supabase는 완전한 오픈소스입니다. 모든 구성 요소의 소스 코드가 GitHub에 공개되어 있고, 원한다면 자체 서버에 직접 설치해서 운영할 수도 있습니다. 특정 클라우드 서비스에 종속되는 벤더 락인 걱정이 적다는 것은, 장기적으로 프로젝트를 운영할 때 큰 안심이 됩니다.

Supabase vs Firebase — 무엇이 다르고, 왜 갈아타는가

BaaS 하면 가장 먼저 떠오르는 이름은 여전히 Firebase입니다. 2014년 Google에 인수된 이후 막강한 생태계를 구축했고, 모바일 앱 개발에서는 사실상 표준에 가까운 위치를 차지했습니다. 그런데 왜 개발자들이 Supabase로 옮겨가고 있을까요?

Supabase와 Firebase 핵심 차이점 비교표

데이터베이스의 근본적 차이

가장 큰 차이는 데이터베이스 철학입니다. Firebase의 Firestore는 NoSQL 문서형 데이터베이스로, JSON과 비슷한 구조에 데이터를 저장합니다. 간단한 앱에서는 직관적이지만, 데이터 간의 관계가 복잡해지면 중복 저장과 비정규화에 시달리게 됩니다. 예를 들어 “게시글에 달린 댓글의 작성자 정보”를 가져오려면 여러 컬렉션을 따로 조회해야 합니다.

반면 Supabase는 PostgreSQL이라는 40년 역사의 관계형 데이터베이스를 씁니다. 테이블 간 외래 키(Foreign Key)로 관계를 맺고, SQL 한 줄로 여러 테이블의 데이터를 조인해서 가져올 수 있습니다. 이미 SQL을 아는 분이라면 학습 비용이 거의 제로이고, SQL을 모르더라도 Supabase 대시보드의 테이블 에디터에서 시각적으로 데이터를 관리할 수 있습니다.

오픈소스 vs 독점 서비스

Firebase는 Google Cloud 위에서만 돌아가는 완전 독점 서비스입니다. Google이 가격 정책을 바꾸거나, 특정 기능을 중단하면 사용자는 선택의 여지가 없습니다. 실제로 Firebase의 과거 실시간 데이터베이스(Realtime Database)에서 Firestore로의 전환 과정에서 많은 개발자가 혼란을 겪었습니다.

Supabase는 각 구성 요소가 모두 오픈소스입니다. PostgreSQL, GoTrue(인증), PostgREST(API 자동 생성), Storage API 등 검증된 오픈소스 프로젝트를 조합한 구조입니다. 만약 Supabase 클라우드 서비스가 마음에 들지 않으면, Docker Compose로 자체 서버에 동일한 환경을 구축할 수 있습니다.

가격 구조의 투명성

Firebase의 과금 모델은 읽기·쓰기 횟수 기반이라, 트래픽이 예측 불가능한 초기 프로젝트에서 갑작스러운 요금 폭탄을 맞을 수 있습니다. “Firestore에서 100만 원 청구서를 받았다”는 개발자 경험담이 커뮤니티에 종종 올라오곤 합니다.

Supabase의 무료 플랜은 프로젝트 단위로 제공되며, 데이터베이스 용량·스토리지·인증 사용자 수에 기반한 예측 가능한 과금 모델을 따릅니다. 무료 플랜에서 프로 플랜(월 25달러)으로 넘어가는 기준도 명확해서, 비용 관리가 훨씬 수월합니다.

언제 Firebase를 선택할까

공정하게 말하자면, Firebase가 더 나은 선택인 경우도 있습니다. Google Analytics, Cloud Messaging(푸시 알림), Crashlytics(앱 충돌 분석) 같은 모바일 앱 특화 기능이 필요하다면 Firebase 생태계가 아직은 더 풍부합니다. 또한 Flutter와의 통합도 Firebase 쪽이 더 매끄럽습니다. 하지만 웹 앱 중심이거나, SQL 기반 데이터 모델링을 선호하거나, 오픈소스 기반의 유연한 스택을 원한다면 Supabase가 확실한 우위입니다.

5분 만에 첫 프로젝트 시작하기

Supabase를 시작하는 데 필요한 것은 딱 두 가지입니다. 웹 브라우저와 GitHub 계정(또는 이메일). 지금부터 첫 프로젝트를 만들어보겠습니다.

1단계 — 계정 생성

supabase.com에 접속해서 “Start your project” 버튼을 클릭합니다. GitHub 계정으로 바로 로그인할 수 있고, 이메일로도 가입할 수 있습니다. 별도의 신용카드 등록 없이 무료 플랜이 즉시 활성화됩니다.

2단계 — 새 프로젝트 생성

로그인하면 대시보드가 나타납니다. “New Project”를 누르고 다음 정보를 입력합니다.

  • 프로젝트 이름 — 식별하기 쉬운 이름(예: my-todo-app)
  • 데이터베이스 비밀번호 — 자동 생성 버튼으로 강력한 비밀번호를 만들고, 안전한 곳에 저장해두세요
  • 리전(Region) — 한국 사용자라면 Northeast Asia (Tokyo) 또는 Southeast Asia (Singapore)를 선택하면 지연 시간이 가장 적습니다

“Create new project”를 누르면 약 1~2분 후에 PostgreSQL 데이터베이스가 포함된 전체 백엔드 환경이 자동으로 프로비저닝됩니다. 서버를 직접 설치하거나 설정 파일을 만질 필요가 전혀 없습니다.

3단계 — API 키 확인

프로젝트가 생성되면 왼쪽 메뉴에서 Settings → API로 이동합니다. 여기서 두 가지 핵심 정보를 확인할 수 있습니다.

  • Project URL — https://xxxxxxxx.supabase.co 형태의 고유 주소
  • anon (public) key — 클라이언트 코드에서 사용하는 공개 키

이 두 값이 앞으로 프론트엔드 코드에서 Supabase에 연결할 때 사용할 열쇠입니다. anon 키는 이름처럼 공개해도 되는 키입니다. 실제 보안은 뒤에서 설명할 Row Level Security(RLS) 정책이 담당합니다.

Supabase 테이블 에디터와 SQL 편집기 화면

4단계 — 클라이언트 라이브러리 설치

Supabase는 JavaScript, Python, Flutter, Swift, Kotlin 등 다양한 언어의 클라이언트 라이브러리를 제공합니다. 웹 프로젝트라면 JavaScript 라이브러리를 설치합니다.

npm install @supabase/supabase-js

설치 후 프로젝트 코드에서 클라이언트를 초기화합니다.

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://xxxxxxxx.supabase.co',  // Project URL
  'eyJhbGciOiJIUzI1NiIs...'        // anon key
)

이 한 줄이면 인증, 데이터베이스 쿼리, 파일 업로드, 실시간 구독 등 Supabase의 모든 기능을 사용할 준비가 끝납니다. 별도의 서버 코드를 작성할 필요가 없습니다.

인증(Auth) — 로그인 기능을 코드 몇 줄로 완성하기

웹 앱에서 가장 먼저 필요하면서 가장 구현이 번거로운 기능이 사용자 인증입니다. 비밀번호 해싱, 세션 관리, 토큰 갱신, 비밀번호 재설정 메일 발송 같은 작업을 직접 구현하면 보안 취약점 없이 완성하기까지 수 주가 걸립니다. Supabase Auth는 이 모든 것을 내장하고 있습니다.

이메일·비밀번호 인증

가장 기본적인 이메일·비밀번호 회원가입과 로그인은 다음과 같이 구현합니다.

// 회원가입
const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'securePassword123!'
})

// 로그인
const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'securePassword123!'
})

// 로그아웃
await supabase.auth.signOut()

// 현재 로그인한 사용자 정보 확인
const { data: { user } } = await supabase.auth.getUser()

코드 네 줄이면 회원가입·로그인·로그아웃·사용자 확인이 모두 끝납니다. 비밀번호는 자동으로 bcrypt 해싱되어 저장되고, JWT 토큰 기반 세션 관리도 라이브러리가 알아서 처리합니다.

소셜 로그인(OAuth)

Google, GitHub, Apple, Kakao 같은 소셜 로그인도 대시보드에서 해당 프로바이더를 활성화한 뒤, 클라이언트 코드 한 줄이면 됩니다.

// Google 소셜 로그인
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google'
})

// GitHub 소셜 로그인
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github'
})

대시보드의 Authentication → Providers 메뉴에서 원하는 소셜 로그인 프로바이더를 켜고, 해당 서비스에서 발급받은 Client ID와 Secret만 입력하면 설정이 끝납니다. 한국 서비스라면 Kakao 프로바이더를 활성화하면 카카오 로그인도 동일한 방식으로 연동할 수 있습니다.

Row Level Security — 인증의 진짜 핵심

Supabase에서 가장 중요한 보안 개념이 RLS(Row Level Security)입니다. 앞서 anon 키가 공개해도 되는 키라고 했는데, 그 이유가 바로 RLS 때문입니다.

RLS는 PostgreSQL의 내장 기능으로, 테이블의 각 행에 대해 “누가 읽을 수 있는지, 누가 수정할 수 있는지”를 SQL 정책으로 정의합니다. 예를 들어 “자기 자신의 데이터만 볼 수 있다”는 정책을 설정하면, 설령 누군가가 anon 키를 알아내더라도 다른 사용자의 데이터에 접근할 수 없습니다.

-- todos 테이블에 RLS 활성화
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;

-- 자기 데이터만 조회 가능
CREATE POLICY "본인 데이터만 조회"
  ON todos FOR SELECT
  USING (auth.uid() = user_id);

-- 자기 데이터만 추가 가능
CREATE POLICY "본인 데이터만 추가"
  ON todos FOR INSERT
  WITH CHECK (auth.uid() = user_id);

auth.uid()는 Supabase가 제공하는 함수로, 현재 로그인한 사용자의 고유 ID를 반환합니다. 이 함수 덕분에 클라이언트 코드에서 별도의 필터링을 하지 않아도, 데이터베이스 레벨에서 접근 제어가 자동으로 이루어집니다.

RLS가 없으면 anon 키를 가진 누구든 모든 데이터를 읽고 쓸 수 있으므로, Supabase에서 테이블을 만들면 반드시 RLS를 활성화하고 적절한 정책을 설정해야 합니다. 대시보드의 Table Editor에서도 GUI로 RLS 정책을 만들 수 있으니, SQL이 아직 익숙하지 않더라도 걱정 없습니다.

데이터베이스 — PostgreSQL의 진짜 힘을 경험하는 순간

Supabase를 선택하는 가장 큰 이유 중 하나가 바로 PostgreSQL입니다. 단순히 데이터를 저장하는 것을 넘어, 40년간 검증된 관계형 데이터베이스의 풍부한 기능을 모두 활용할 수 있습니다.

Table Editor로 시각적으로 관리하기

Supabase 대시보드의 Table Editor는 스프레드시트처럼 직관적입니다. 버튼 클릭만으로 새 테이블을 만들고, 컬럼을 추가하고, 데이터를 입력할 수 있습니다. SQL 한 줄 모르는 상태에서도 데이터베이스를 관리할 수 있다는 것이 큰 장점입니다.

하지만 조금 더 복잡한 작업을 하려면 SQL Editor를 활용하는 것이 훨씬 효율적입니다. 대시보드의 SQL Editor는 자동 완성과 구문 강조를 지원하는 웹 기반 SQL 편집기로, 복잡한 쿼리를 작성하고 즉시 실행할 수 있습니다.

JavaScript 클라이언트로 CRUD 구현

Supabase 클라이언트 라이브러리는 SQL을 몰라도 데이터를 다룰 수 있는 직관적인 API를 제공합니다. 아래 예시로 기본적인 CRUD(생성·조회·수정·삭제) 연산을 살펴보겠습니다.

// 데이터 추가 (Create)
const { data, error } = await supabase
  .from('todos')
  .insert({ title: '블로그 글 쓰기', is_complete: false })

// 데이터 조회 (Read)
const { data, error } = await supabase
  .from('todos')
  .select('*')
  .order('created_at', { ascending: false })

// 조건부 조회 — 완료되지 않은 항목만
const { data, error } = await supabase
  .from('todos')
  .select('*')
  .eq('is_complete', false)

// 데이터 수정 (Update)
const { data, error } = await supabase
  .from('todos')
  .update({ is_complete: true })
  .eq('id', 1)

// 데이터 삭제 (Delete)
const { data, error } = await supabase
  .from('todos')
  .delete()
  .eq('id', 1)

SQL의 SELECT, INSERT, UPDATE, DELETE에 대응하는 메서드가 체이닝 방식으로 제공됩니다. .eq()은 WHERE 조건, .order()는 ORDER BY에 해당합니다. 이 외에도 .like(), .in(), .gte(), .lte() 같은 다양한 필터 메서드가 있어서, 대부분의 쿼리를 SQL 없이 작성할 수 있습니다.

테이블 관계와 조인

관계형 데이터베이스의 진짜 힘은 테이블 간 관계에서 나옵니다. 예를 들어 게시글(posts) 테이블과 댓글(comments) 테이블이 외래 키로 연결되어 있다면, Supabase 클라이언트로 한 번에 조인 결과를 가져올 수 있습니다.

// 게시글과 함께 댓글 목록을 한 번에 조회
const { data, error } = await supabase
  .from('posts')
  .select(`
    id, title, content,
    comments ( id, body, created_at )
  `)
  .order('created_at', { ascending: false })

Firebase의 Firestore에서는 이런 조인이 불가능해서 두 번의 쿼리를 따로 날려야 합니다. Supabase에서는 PostgreSQL의 외래 키 관계를 자동 감지해서, select 문의 중첩 구문만으로 깔끔하게 해결됩니다.

실시간 구독(Realtime)

데이터베이스의 변경 사항을 실시간으로 감지하고 싶다면 Realtime 구독을 사용합니다. 채팅 앱, 협업 도구, 실시간 대시보드 같은 기능에 필수적입니다.

// todos 테이블의 모든 변경 사항을 실시간으로 수신
const channel = supabase
  .channel('todos-changes')
  .on('postgres_changes',
    { event: '*', schema: 'public', table: 'todos' },
    (payload) => {
      console.log('변경 감지:', payload.eventType, payload.new)
      // UI 업데이트 로직
    }
  )
  .subscribe()

event에 'INSERT', 'UPDATE', 'DELETE'를 지정하면 특정 이벤트만 감지할 수 있고, '*'를 쓰면 모든 변경을 수신합니다. 별도의 WebSocket 서버를 구축하지 않아도 실시간 기능이 동작한다는 점이 놀랍습니다.

스토리지, Edge Functions, 그리고 확장 기능들

인증과 데이터베이스만으로도 대부분의 앱을 만들 수 있지만, Supabase는 파일 저장과 서버리스 함수까지 제공해서 진정한 의미의 풀스택을 완성해줍니다.

Storage — 파일 저장과 관리

프로필 사진, 첨부 파일, 동영상 같은 바이너리 파일을 관리하려면 Supabase Storage를 사용합니다. S3와 호환되는 오브젝트 스토리지로, 버킷(bucket) 단위로 파일을 정리합니다.

// 버킷 생성 (대시보드에서도 가능)
const { data, error } = await supabase.storage
  .createBucket('avatars', { public: true })

// 파일 업로드
const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user123/profile.jpg', file)

// 공개 URL 가져오기
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/profile.jpg')

// 이미지 변환 (리사이즈)
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user123/profile.jpg', {
    transform: { width: 200, height: 200, resize: 'cover' }
  })

버킷을 public: true로 만들면 누구나 파일에 접근할 수 있고, public: false로 만들면 인증된 사용자만 접근할 수 있습니다. 이미지 변환 기능은 원본 파일을 건드리지 않고 URL 파라미터만으로 리사이즈·크롭된 이미지를 반환하므로, 썸네일 생성에 매우 유용합니다.

Edge Functions — 서버 없이 해결 안 되는 순간의 대안

아무리 BaaS가 편하더라도, 클라이언트에서 실행할 수 없는 로직이 있습니다. 결제 처리, 외부 API 호출(API 키를 숨겨야 하는 경우), 이메일 발송, 웹훅 수신 같은 작업에는 서버 사이드 코드가 필요합니다. Supabase Edge Functions가 바로 이 역할을 합니다.

Edge Functions는 Deno 런타임 기반의 서버리스 함수로, TypeScript로 작성합니다. Supabase CLI를 사용해서 로컬에서 개발하고 명령어 하나로 배포할 수 있습니다.

// supabase/functions/send-welcome-email/index.ts
import { serve } from 'https://deno.land/std/http/server.ts'

serve(async (req) => {
  const { email, name } = await req.json()

  // 외부 이메일 서비스 API 호출 (키를 환경변수로 관리)
  const apiKey = Deno.env.get('EMAIL_API_KEY')
  const response = await fetch('https://api.email-service.com/send', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${apiKey}` },
    body: JSON.stringify({
      to: email,
      subject: `${name}님, 환영합니다!`,
      body: '가입해 주셔서 감사합니다.'
    })
  })

  return new Response(JSON.stringify({ success: true }),
    { headers: { 'Content-Type': 'application/json' } })
})

배포는 CLI에서 supabase functions deploy send-welcome-email 한 줄이면 끝입니다. 호출도 클라이언트 라이브러리에서 간단하게 할 수 있습니다.

const { data, error } = await supabase.functions.invoke('send-welcome-email', {
  body: { email: '[email protected]', name: '홍길동' }
})

서버를 직접 운영하지 않으면서도, 필요할 때만 서버 사이드 로직을 실행할 수 있는 것이 Edge Functions의 핵심 가치입니다. 함수가 호출될 때만 리소스를 사용하므로 비용 효율도 높습니다.

실전 미니 프로젝트 — 할 일 관리 앱 만들기

지금까지 배운 내용을 하나로 엮어서, 실제로 동작하는 간단한 할 일 관리(To-Do) 앱을 만들어보겠습니다. 인증 로그인 → 할 일 CRUD → 실시간 동기화까지 포함하는 완전한 앱입니다.

할 일 앱의 Supabase 데이터 흐름 구조도

1단계 — 데이터베이스 테이블 생성

Supabase 대시보드의 SQL Editor에서 다음 SQL을 실행합니다.

-- 할 일 테이블 생성
CREATE TABLE todos (
  id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  user_id UUID REFERENCES auth.users NOT NULL DEFAULT auth.uid(),
  title TEXT NOT NULL,
  is_complete BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Row Level Security 활성화
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;

-- 정책: 본인 데이터만 조회
CREATE POLICY "본인 조회" ON todos
  FOR SELECT USING (auth.uid() = user_id);

-- 정책: 본인 데이터만 추가
CREATE POLICY "본인 추가" ON todos
  FOR INSERT WITH CHECK (auth.uid() = user_id);

-- 정책: 본인 데이터만 수정
CREATE POLICY "본인 수정" ON todos
  FOR UPDATE USING (auth.uid() = user_id);

-- 정책: 본인 데이터만 삭제
CREATE POLICY "본인 삭제" ON todos
  FOR DELETE USING (auth.uid() = user_id);

-- Realtime 활성화
ALTER PUBLICATION supabase_realtime ADD TABLE todos;

user_id 컬럼의 기본값을 auth.uid()로 설정했기 때문에, 데이터를 삽입할 때 사용자 ID를 따로 넘기지 않아도 자동으로 현재 로그인한 사용자의 ID가 들어갑니다. RLS 정책 네 개를 설정해서 본인의 데이터만 읽고, 쓰고, 수정하고, 삭제할 수 있도록 보호합니다.

2단계 — 프론트엔드 코드 작성

프레임워크 없이 순수 HTML과 JavaScript만으로 구현합니다. 아래 코드를 index.html로 저장하고 브라우저에서 열면 바로 동작합니다.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>Supabase 할 일 관리</title>
  <style>
    body { font-family: sans-serif; max-width: 480px; margin: 40px auto; }
    .todo { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
    .todo.done span { text-decoration: line-through; color: #999; }
    input[type="text"] { flex: 1; padding: 8px; font-size: 16px; }
    button { padding: 8px 16px; cursor: pointer; }
    #auth-section, #app-section { margin-top: 20px; }
  </style>
</head>
<body>
  <h1>할 일 관리</h1>

  <div id="auth-section">
    <input id="email" type="email" placeholder="이메일" />
    <input id="password" type="password" placeholder="비밀번호" />
    <button onclick="signUp()">회원가입</button>
    <button onclick="signIn()">로그인</button>
  </div>

  <div id="app-section" style="display:none">
    <p>환영합니다, <span id="user-email"></span>
      <button onclick="signOut()">로그아웃</button>
    </p>
    <div style="display:flex; gap:8px">
      <input id="new-todo" type="text" placeholder="새 할 일 입력" />
      <button onclick="addTodo()">추가</button>
    </div>
    <div id="todo-list"></div>
  </div>

  <script type="module">
    import { createClient } from
      'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm'

    const supabase = createClient(
      'YOUR_PROJECT_URL',   // 본인 프로젝트 URL로 교체
      'YOUR_ANON_KEY'       // 본인 anon key로 교체
    )

    // 전역 함수 등록
    window.signUp = async () => {
      const email = document.getElementById('email').value
      const password = document.getElementById('password').value
      const { error } = await supabase.auth.signUp({ email, password })
      if (error) alert(error.message)
      else alert('가입 완료! 이메일을 확인하세요.')
    }

    window.signIn = async () => {
      const email = document.getElementById('email').value
      const password = document.getElementById('password').value
      const { error } = await supabase.auth.signInWithPassword(
        { email, password })
      if (error) alert(error.message)
    }

    window.signOut = async () => {
      await supabase.auth.signOut()
    }

    window.addTodo = async () => {
      const input = document.getElementById('new-todo')
      const title = input.value.trim()
      if (!title) return
      await supabase.from('todos').insert({ title })
      input.value = ''
    }

    window.toggleTodo = async (id, current) => {
      await supabase.from('todos')
        .update({ is_complete: !current }).eq('id', id)
    }

    window.deleteTodo = async (id) => {
      await supabase.from('todos').delete().eq('id', id)
    }

    // 할 일 목록 렌더링
    async function renderTodos() {
      const { data } = await supabase.from('todos')
        .select('*').order('created_at', { ascending: false })
      const list = document.getElementById('todo-list')
      list.innerHTML = (data || []).map(t => `
        <div class="todo ${t.is_complete ? 'done' : ''}">
          <input type="checkbox" ${t.is_complete ? 'checked' : ''}
            onchange="toggleTodo(${t.id}, ${t.is_complete})" />
          <span>${t.title}</span>
          <button onclick="deleteTodo(${t.id})">삭제</button>
        </div>
      `).join('')
    }

    // 인증 상태 감지
    supabase.auth.onAuthStateChange((event, session) => {
      if (session) {
        document.getElementById('auth-section').style.display = 'none'
        document.getElementById('app-section').style.display = 'block'
        document.getElementById('user-email').textContent =
          session.user.email
        renderTodos()
      } else {
        document.getElementById('auth-section').style.display = 'block'
        document.getElementById('app-section').style.display = 'none'
      }
    })

    // 실시간 구독 — 데이터 변경 시 자동 새로고침
    supabase.channel('todos-realtime')
      .on('postgres_changes',
        { event: '*', schema: 'public', table: 'todos' },
        () => renderTodos()
      )
      .subscribe()
  </script>
</body>
</html>

코드 해설

이 코드 하나에 앞서 설명한 Supabase의 핵심 기능이 모두 들어가 있습니다.

  • 인증 — signUp, signIn, signOut, onAuthStateChange로 완전한 인증 플로우가 구현되어 있습니다. 로그인 상태에 따라 화면이 자동으로 전환됩니다.
  • 데이터베이스 CRUD — insert, select, update, delete로 할 일의 추가·조회·수정·삭제를 처리합니다.
  • 실시간 구독 — postgres_changes 이벤트를 구독해서, 다른 기기에서 데이터를 변경해도 화면이 자동으로 갱신됩니다.
  • RLS 보안 — 서버 코드가 없어도, 데이터베이스 레벨의 정책이 사용자별 데이터 격리를 보장합니다.

CDN에서 Supabase 라이브러리를 직접 불러오기 때문에, npm 설치나 빌드 도구 없이 HTML 파일 하나만으로 바로 실행할 수 있습니다. YOUR_PROJECT_URL과 YOUR_ANON_KEY만 본인 프로젝트의 값으로 교체하면 됩니다.

무료 플랜 한도와 실전 운영 팁

Supabase의 무료 플랜은 개인 프로젝트와 학습 목적으로는 매우 넉넉합니다. 하지만 프로덕션 운영 전에 한도를 정확히 이해하고 있어야 예상치 못한 서비스 중단을 피할 수 있습니다.

무료 플랜 주요 한도 (2026년 기준)

  • 프로젝트 수 — 무료 플랜으로 최대 2개의 활성 프로젝트를 운영할 수 있습니다
  • 데이터베이스 — 500MB 용량. 텍스트 위주 앱이라면 수만 건의 레코드를 저장하기에 충분합니다
  • 스토리지 — 1GB 용량, 단일 파일 최대 50MB
  • 인증 — 월간 활성 사용자(MAU) 50,000명. 소규모 서비스에는 사실상 무제한입니다
  • Edge Functions — 월 50만 회 호출, 실행 시간 제한 있음
  • 대역폭 — 월 5GB 전송량
  • 일시정지 — 7일 이상 비활성 시 프로젝트가 자동 일시정지될 수 있습니다. 대시보드에서 한 클릭으로 재개 가능하지만, 프로덕션 서비스라면 주기적인 활성 상태 유지가 필요합니다

비용 최적화 팁

무료 플랜에서 최대한 오래 버티기 위한 실전 팁을 정리합니다.

  • 이미지는 외부 CDN 활용 — 프로필 사진 같은 정적 이미지는 Cloudflare R2나 imgbb 같은 무료 이미지 호스팅에 올리고, Supabase 데이터베이스에는 URL만 저장하면 스토리지 용량을 크게 절약할 수 있습니다
  • 불필요한 Realtime 구독 끄기 — 모든 테이블에 Realtime을 켜두면 연결 수와 대역폭이 빠르게 소모됩니다. 진짜 실시간이 필요한 테이블에만 선별적으로 활성화하세요
  • 인덱스 설정 — 자주 검색하는 컬럼에 인덱스를 추가하면 쿼리 성능이 개선되고, 데이터베이스 리소스를 효율적으로 사용합니다
  • Edge Functions 최소화 — 클라이언트에서 직접 할 수 있는 로직을 굳이 Edge Function으로 만들지 마세요. API 키를 숨기거나, 서버 사이드 검증이 반드시 필요한 경우에만 사용합니다

셀프호스팅이라는 선택지

Supabase가 오픈소스라는 것은 곧, 본인 서버에 직접 설치할 수 있다는 뜻입니다. Docker Compose 하나로 PostgreSQL, GoTrue(인증), PostgREST(API), Kong(API 게이트웨이), Realtime, Storage 등 전체 스택을 로컬이나 NAS에 올릴 수 있습니다.

# Supabase 셀프호스팅 시작
git clone https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env
# .env 파일의 비밀번호·JWT 시크릿 등을 수정한 뒤
docker compose up -d

셀프호스팅하면 용량·사용자 수·호출 횟수에 제한이 없어지고, 데이터가 외부로 나가지 않으므로 프라이버시도 확보됩니다. 다만 PostgreSQL 백업, 보안 패치, 인증서 관리 등 운영 부담이 생기므로, 운영 경험이 어느 정도 쌓인 후에 도전하는 것을 권장합니다. 이미 NAS에 Docker를 돌리고 있는 분이라면 충분히 시도해볼 만합니다.

프로 플랜으로 넘어가는 시점

다음 중 하나라도 해당한다면 프로 플랜(월 25달러)으로 업그레이드를 고려할 때입니다.

  • 데이터베이스 용량이 500MB에 근접하거나 초과
  • 프로덕션 서비스를 운영하면서 자동 일시정지를 피하고 싶을 때
  • 일일 백업, 7일 로그 보존 같은 운영 기능이 필요할 때
  • Edge Functions의 호출 한도가 부족할 때

무료 플랜에서 프로 플랜으로의 업그레이드는 대시보드에서 클릭 한 번이면 되고, 데이터 이전이나 코드 변경이 필요하지 않습니다. 작게 시작해서 필요할 때 확장하는 접근법이 가능한 것이 Supabase의 큰 장점입니다.

다음 단계 — Supabase로 더 멀리 가기

이 글에서 다룬 내용은 Supabase의 핵심 기능 중 일부에 불과합니다. 더 깊이 파고들고 싶다면 다음 방향을 추천합니다.

  • React·Vue·Svelte 통합 — 바닐라 JavaScript 대신 프론트엔드 프레임워크와 결합하면 더 체계적인 앱을 만들 수 있습니다. Supabase 공식 문서에 프레임워크별 가이드가 잘 정리되어 있습니다.
  • Supabase CLI 활용 — 로컬 개발 환경에서 Supabase를 Docker로 띄우고, 마이그레이션 파일로 데이터베이스 스키마를 버전 관리할 수 있습니다. 팀 프로젝트에서 특히 유용합니다.
  • pgvector로 AI 기능 추가 — PostgreSQL의 pgvector 확장을 활성화하면 텍스트 임베딩을 저장하고 의미 기반 검색(시맨틱 서치)을 구현할 수 있습니다. RAG 챗봇을 만들 때 별도의 Pinecone이나 Weaviate 없이 Supabase 안에서 해결할 수 있습니다.
  • Database Functions과 Triggers — PostgreSQL의 함수(Function)와 트리거(Trigger)를 활용하면 데이터 삽입·수정 시 자동으로 실행되는 비즈니스 로직을 데이터베이스 레벨에서 구현할 수 있습니다. 예를 들어 새 사용자가 가입하면 자동으로 프로필 테이블에 초기 레코드를 만드는 식입니다.
  • Supabase + Cloudflare Pages 배포 — 프론트엔드를 Cloudflare Pages에 무료로 배포하고, 백엔드를 Supabase에 두면 인프라 비용 0원으로 전 세계에 서비스할 수 있는 풀스택 아키텍처가 완성됩니다.

서버 없이 풀스택 앱을 만든다는 것이 몇 년 전까지만 해도 비현실적으로 들렸습니다. 하지만 Supabase 덕분에 이제 혼자서도, 무료로도, 인증·데이터베이스·스토리지·서버리스·실시간 기능까지 갖춘 완전한 애플리케이션을 만들 수 있는 시대가 됐습니다.

머릿속에 묵혀둔 사이드 프로젝트가 있다면, 이번 여름에 Supabase로 시작해보는 건 어떨까요? 서버 걱정은 Supabase에 맡기고, 여러분은 아이디어에만 집중하세요. 프로젝트 생성부터 첫 번째 API 호출까지 걸리는 시간은 5분이면 충분합니다.

참고 자료

  • Supabase 공식 문서 — 인증, 데이터베이스, 스토리지, Edge Functions 등 전체 기능의 공식 가이드 및 API 레퍼런스
  • 위키백과 — 서버리스 컴퓨팅 — 서버 없이 애플리케이션을 구축하는 서버리스 아키텍처의 개념과 역사

Tags:

BaaSFirebase 대안Supabase서버리스풀스택 개발
작성자

AICosmus

Follow Me
다른 기사
직장인 D-30 시험 마무리 공부 모습
Previous

[생활스포츠지도사 2급 필기 합격 프로젝트] 28/30화: 생활스포츠지도사 2급 D-30 마무리 플랜, 과목별 학습 분량 총정리

온프레미스 AI 서버와 홀로그램 AI 시각화
Next

[온프레미스 AI Assistant 아키텍처 — Qwen3·Qwen3-VL 14일 설계] 1/14화: 온프레미스 AI 자체 호스팅, Qwen3을 선택한 6가지 이유

댓글 없음! 첫 댓글을 남겨보세요.

답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글

  • [opencode 시즌 2 심화 — 나만의 도메인 특화 에이전트 만들기] 1/12화: opencode 에이전트 아키텍처 완전 해부 — 2026 Primary·Subagent 5계층 구조
  • Kotlin 코루틴 핵심 5가지 개념과 실전 활용법
  • [opencode 12일 집중 — 터미널 네이티브 AI 코딩 에이전트 완전 정복] 12/12화: opencode 로컬 모델 완전 가이드 2026 — Ollama·에어갭·규제 환경 도입 체크리스트
  • LLM 파인튜닝 실전 5단계 — 2026 LoRA 완벽 가이드
  • 금융 앱 생체인증 작동 원리, 지문·얼굴 보안 5단계 완전 해부

최신 댓글

  1. [온프레미스 AI Assistant 아키텍처 — Qwen3·Qwen3-VL 14일 설계] 9/14화: 온프레미스 RAG 파이프라인 — bge-m3·Qdrant 자체 호스팅 실전의 Dockerfile 최적화 실전 가이드 — 빌드·크기·보안 총정리 - AICosmus
  2. RAG 평가 프레임워크, 답변 품질을 수치로 측정하는 법의 RAG 리랭킹 가이드, 검색 결과 정확도 높이는 법 - AICosmus
  3. RAG 평가 프레임워크, 답변 품질을 수치로 측정하는 법의 RAG 리랭킹 가이드, 검색 결과 정확도 높이는 법 - AICosmus
  4. gRPC Interceptor 완벽 가이드: 인증부터 로깅까지의 gRPC 데드라인과 재시도 정책으로 장애 전파 차단하기 - AICosmus
  5. gRPC Interceptor 완벽 가이드: 인증부터 로깅까지의 gRPC 데드라인과 재시도 정책으로 장애 전파 차단하기 - AICosmus
  • About
  • Contact
  • Disclaimer
  • Privacy - Policy
  • Terms of Service
Copyright 2026 — AICosmus. All rights reserved. Blogsy WordPress Theme