![[ 개인플젝 ] Supabase 이용하기](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog-custom%3Ftitle%3D%255B%2B%25EA%25B0%259C%25EC%259D%25B8%25ED%2594%258C%25EC%25A0%259D%2B%255D%2BSTREAKLY%26tag%3DTemplate%2B1%26description%3DSupabase%2B%25EC%259D%25B4%25EC%259A%25A9%25ED%2595%2598%25EA%25B8%25B0%26template%3D3%26backgroundImage%3Dhttps%253A%252F%252Fsource.inblog.dev%252Fog_image%252Fdefault.png%26bgStartColor%3D%2523ffffff%26bgEndColor%3D%2523ffffff%26textColor%3D%2523000000%26tagColor%3D%2523000000%26descriptionColor%3D%2523000000%26logoUrl%3Dhttps%253A%252F%252Fsource.inblog.dev%252Flogo%252F2025-12-24T06%253A07%253A46.034Z-ddf15b75-3608-41bd-8914-cd5d2d9efc83%26blogTitle%3D&w=3840&q=75)
Supabase 채택이유
개인 프로젝트(운동 인증 앱)를 개발하면서 백엔드로 Firebase 대신 Supabase를 선택했다.
사실 처음에는 익숙한 Firebase로 서버를 구성하고 싶었으나 개발 환경이 비교적 최신(macOS / Xcode)이다 보니 Firebase iOS SDK 및 일부 네이티브 모듈과의 버전 호환 이슈가 발생했다
내가 실직전 회사에서 최대한 늦게 업데이트하는 이유이다…. ㅂㄷㅂㄷ
무튼 환경 디버깅에 너무 많은 시간을 쏟지 않기 위해 차선책으로 비슷한 Supabase를 채택했다
1. 앱 기능 요약
이 프로젝트에서 구현한 핵심 기능은 다음과 같다.
유저는 앱 최초 진입 시 닉네임을 생성
로그인은 별도 회원가입 없이 익명 인증으로 처리
하나의 시즌 안에서
하루에 한 번만 인증 가능
인증 시 사진을 업로드
이번 시즌 기준으로
연속 기록(streak)
캘린더
랭킹
이 기능을 기준으로 필요한 테이블을 구성해야한다
2. 전체 DB 구조
users → 유저 기본 정보
seasons → 시즌 정의 (운영자 관리)
records → 유저의 인증 기록
3. users 테이블
유저 기본 정보
Supabase Auth의
auth.uid()를 그대로 사용자 ID로 사용한다.create table users ( id uuid primary key, nickname text not null, notification_enabled boolean default true, created_at timestamptz default now() );설계 포인트
id는 Supabase Auth의 UID닉네임은 앱 최초 진입(OOBE)에서 한 번 생성
필요 시 언제든 수정 가능
4. seasons 테이블
시즌 정의 (운영자 === 내가 관리)
시즌은 유저가 생성하지 않는다.
운영자가 직접 관리하는 기준 데이터다.create table seasons ( id uuid primary key default gen_random_uuid(), season_key text not null unique, -- 예: '2026_01' start_date date not null, end_date date not null, is_current boolean default false, created_at timestamptz default now() );Row Level Security (읽기 전용)
alter table seasons enable row level security; create policy "anyone can read seasons" on seasons for select using (true);초기 시즌 데이터
insert into seasons (season_key, start_date, end_date, is_current) values ( '2026_01', '2026-01-01', '2026-03-31', true );설계 포인트
시즌은 “2026년 첫 시즌” 같은 논리 단위
앱에서는 항상
is_current = true인 시즌만 사용시즌 변경은 SQL로 한 번 실행하면 끝
5. records 테이블
인증 기록
유저의 인증 기록을 저장하는 테이블이다.
create table records ( id uuid primary key default gen_random_uuid(), user_id uuid not null, season_id uuid not null, record_date date not null, -- 인증 날짜 record_month text not null, -- '2026-01' (캘린더/집계용) image_url text not null, created_at timestamptz default now(), constraint fk_records_user foreign key (user_id) references users (id) on delete cascade, constraint fk_records_season foreign key (season_id) references seasons (id) on delete cascade, constraint unique_user_day unique (user_id, record_date) );하루 1회 인증 보장
unique (user_id, record_date) // 중복 요청 // 네트워크 지연 // 클라이언트 버그 // 를 DB 레벨에서 모두 방지records 테이블 보안 (RLS)
records는 절대 공개되면 안 되는 데이터다.
alter table records enable row level security;기록 조회
alter table records enable row level security; create policy "users can read records in current season" on records for select using ( -- 본인 기록이거나 auth.uid() = user_id OR -- 현재 시즌의 기록이면 season_id = ( select id from seasons where is_current = true ) );본인 기록만 생성
create policy "users can insert own records" on records for insert with check (auth.uid() = user_id);본인 기록만 삭제
create policy "users can delete own records" on records for delete using (auth.uid() = user_id);