[ 개인플젝 ] Supabase 이용하기

RN개발자 혼자 프로젝트 완성하기 with Supabase
김보람's avatar
Jan 11, 2026
[ 개인플젝 ] Supabase 이용하기

Supabase 채택이유

개인 프로젝트(운동 인증 앱)를 개발하면서 백엔드로 Firebase 대신 Supabase를 선택했다.

사실 처음에는 익숙한 Firebase로 서버를 구성하고 싶었으나 개발 환경이 비교적 최신(macOS / Xcode)이다 보니 Firebase iOS SDK 및 일부 네이티브 모듈과의 버전 호환 이슈가 발생했다

내가 실직전 회사에서 최대한 늦게 업데이트하는 이유이다…. ㅂㄷㅂㄷ

무튼 환경 디버깅에 너무 많은 시간을 쏟지 않기 위해 차선책으로 비슷한 Supabase를 채택했다


1. 앱 기능 요약

이 프로젝트에서 구현한 핵심 기능은 다음과 같다.

  1. 유저는 앱 최초 진입 시 닉네임을 생성

  2. 로그인은 별도 회원가입 없이 익명 인증으로 처리

  3. 하나의 시즌 안에서

    • 하루에 한 번만 인증 가능

    • 인증 시 사진을 업로드

  4. 이번 시즌 기준으로

    • 연속 기록(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);

Share article

김보람 | 930802qhfka@gmail.com