콘텐츠로 이동
Study NoteSupabase

실전 가이드 — Supabase 앱 첫 배포

첫 배포는 서버 하나를 올리는 일이 아니라 서로 다른 제어판의 계약을 맞추는 일이다

로컬에서 잘 되던 앱을 인터넷에 공개할 때는 코드만 배포되지 않는다. 데이터베이스 스키마, 브라우저에 공개할 키, OAuth 콜백, 실행 리전, DNS와 절대 URL 기준점까지 함께 옮겨야 한다. 이 장은 Next.js + Supabase 앱을 Vercel에 배포하고 Google 로그인과 Cloudflare DNS를 붙이는 한 번의 전체 흐름을 다룬다.

다섯 시스템이 있지만 책임은 겹치지 않는다.

시스템맡는 것이 시스템에 넣지 않는 것
GitHub코드와 마이그레이션 파일운영 시크릿
SupabasePostgres·RLS·Storage·사용자 세션Google 동의 화면, 앱 빌드
Google사용자 인증과 동의앱의 최종 복귀 경로 허용 판단
VercelNext.js 빌드·함수·프리뷰·TLS영속 데이터
Cloudflare도메인의 DNS 레코드이 구성에서는 앱 실행과 CDN
GitHub push로 Vercel에 배포되고 Cloudflare DNS가 CNAME으로 가리키며, 브라우저 로그인이 Supabase Auth와 Google을 거쳐 세션 쿠키로 돌아오는 6단계

위 그림은 배포 뒤의 실행 트래픽을 보여준다. 설정할 때는 반대 방향으로 값을 복사하는 일이 많다. 다음 표를 먼저 읽어 두면 어느 대시보드에서 값을 만들고 어느 대시보드에 붙여 넣어야 하는지 헷갈리지 않는다.

값을 만든 곳전달하는 설정값값을 넣는 곳만들어지는 관계
SupabaseProject URL·publishable keyVercel Settings > Environment VariablesVercel에서 실행되는 앱 → Supabase Data API·Auth
SupabaseAuth Callback URLGoogle Clients > Authorized redirect URIsGoogle 인증 완료 → Supabase Auth
GoogleOAuth Client ID·Client SecretSupabase Authentication > Sign In / Providers > GoogleSupabase Auth → Google 사용자 인증 위임
VercelCNAME Target·소유권 확인 TXTCloudflare DNS > Records커스텀 도메인 → Vercel 배포
Vercel에 추가한 커스텀 도메인최종 앱 URL·originVercel 환경 변수·Supabase URL Configuration·Google Branding/Client절대 URL과 OAuth 복귀 주소를 한 도메인으로 통일

Cloudflare를 DNS only로 두면 Cloudflare는 도메인 질의에 Vercel의 목적지를 알려줄 뿐이다. 이후 브라우저의 HTTPS 요청은 Cloudflare 프록시를 거치지 않고 Vercel로 직접 간다.

순서를 바꾸면 되돌아갈 일이 생긴다. Supabase 프로젝트 참조 ID가 있어야 Google 콜백을 만들 수 있고, 최종 도메인이 생기면 Vercel 환경 변수와 Supabase Auth URL을 다시 고쳐야 한다.

메뉴 경로는 2026-08-17 영문 UI를 기준으로 상위 메뉴 > 하위 메뉴 > 버튼 순서로 쓴다. Google Cloud는 공식 한국어 UI가 있으므로 실제 한국어 명칭도 함께 적는다. Supabase·Vercel·Cloudflare는 계정과 출시 상태에 따라 영문 또는 혼합 UI가 보일 수 있어, 괄호 안 한국어는 화면을 찾기 위한 뜻풀이다.

English: Authentication > URL Configuration
한국어 뜻: 인증 > URL 구성

대시보드 메뉴는 문서보다 자주 바뀐다. 경로가 달라졌다면 먼저 같은 영문 설정 이름을 검색하고, 페이지 말미의 공식 문서에서 최신 경로를 확인한다. 특히 Supabase의 API 설정은 과거 Settings > API에서 현재 Settings > API Keys와 Integrations > Data API로 나뉘었다.

가장 헷갈리는 것 — URL 세 종류

섹션 제목: “가장 헷갈리는 것 — URL 세 종류”

OAuth 설정 화면마다 redirect, callback, site URL이라는 비슷한 말을 쓴다. 주소를 외우지 말고 누가 누구에게 돌려보내는가로 구분한다.

설정 위치예시뜻
Google의 승인된 리디렉션 URIhttps://<ref>.supabase.co/auth/v1/callbackGoogle → Supabase Auth
Supabase Redirect URLshttps://app.example.com/auth/callbackSupabase Auth → 우리 앱
Supabase Site URLhttps://app.example.comredirectTo가 없을 때의 기본 복귀점
Vercel의 사이트 URL 환경 변수https://app.example.comcanonical·OG·sitemap 같은 절대 URL의 기준점

0. 콘솔을 열기 전에 코드를 준비한다

섹션 제목: “0. 콘솔을 열기 전에 코드를 준비한다”

배포 버튼을 누르기 전에 다음이 코드에 있어야 한다.

  • 필요한 환경 변수의 이름만 적은 .env.example과 누락 시 알아볼 수 있는 오류
  • supabase/migrations/ 아래 재현 가능한 스키마, RLS 정책, 함수와 Storage 설정
  • signInWithOAuth가 가리키는 PKCE 콜백 라우트와 exchangeCodeForSession
  • 로그인 후 next 값을 앱 내부 경로로만 제한하는 오픈 리디렉션 방어
  • 요청의 실제 호스트를 쓰는 callback origin과 배포별 URL을 고려한 metadataBase
  • 프로덕션에서 노출하면 안 되는 개발·프로토타입 라우트 차단
  • 빈 프로덕션 DB에서도 깨지지 않는 홈과 탐색 화면

운영 키를 코드에 넣지 않는다. 브라우저에서 쓰는 publishable 키는 공개 전제지만, secret/service-role 키와 DB 비밀번호는 공개 변수가 아니다.

1. Supabase Cloud에 상태를 먼저 만든다

섹션 제목: “1. Supabase Cloud에 상태를 먼저 만든다”

프로젝트 만들기

  • English: Dashboard home > Organization > New project
  • 한국어 뜻: 대시보드 홈 > 조직 > 새 프로젝트
  1. Supabase Dashboard에서 프로젝트를 담을 Organization을 선택하고 New project를 누른다.

  2. 프로젝트 생성 폼을 채운다.

    필드timeline에서 선택한 값주의
    Project name알아볼 수 있는 이름URL이나 DB 이름과 같을 필요는 없다
    Database password비밀번호 관리자가 만든 값CLI 연결에 다시 필요하다
    RegionNortheast Asia (Seoul)Vercel icn1과 가까운 ap-northeast-2
    Pricing plan현재 용도에 맞는 플랜한도·상업 이용 조건은 배포 전에 다시 확인

    사용자의 위치보다 DB를 호출하는 서버 함수와 가까운 리전을 고른다. 리전은 나중에 바꾸려면 새 프로젝트를 만들고 데이터를 이전해야 하므로 이 화면에서 결정한다.

  3. Create new project를 누르고 상태가 준비될 때까지 기다린다.

프로젝트 참조 ID와 앱 연결 값 찾기

필요한 값영문 메뉴한국어 뜻
Project referenceSettings > General > Reference ID설정 > 일반 > 참조 ID
Project URL상단 Connect 또는 Integrations > Data API연결 또는 통합 > 데이터 API
publishable key상단 Connect 또는 Settings > API Keys연결 또는 설정 > API 키
legacy anon keySettings > API Keys > Legacy API Keys설정 > API 키 > 레거시 API 키

Project reference는 브라우저 주소의 /dashboard/project/<project-ref>에서도 확인할 수 있다. 새 앱은 sb_publishable_... 키를 우선한다. 기존 코드의 환경 변수 이름이 NEXT_PUBLIC_SUPABASE_ANON_KEY여도 그 값으로 publishable 키를 받을 수 있지만, secret key나 legacy service_role 키를 넣어서는 안 된다.

로컬 마이그레이션 연결

  1. 터미널에서 로컬 프로젝트를 원격 프로젝트에 연결하고 적용 대상을 미리 본다.

    터미널 창
    supabase login
    supabase link --project-ref <project-ref>
    supabase migration list --linked
    supabase db push --linked --dry-run
  2. 예상한 마이그레이션만 보이면 적용하고 다시 목록을 확인한다.

    터미널 창
    supabase db push --linked
    supabase migration list --linked
  3. Dashboard에서 결과를 확인한다.

    확인 대상영문 메뉴한국어 뜻
    앱 테이블과 RLSTable Editor > public > <table>테이블 편집기 > public > <테이블>
    함수·트리거·마이그레이션 이력SQL Editor > New querySQL 편집기 > 새 쿼리
    Storage 버킷Storage > Buckets스토리지 > 버킷
    Data API URL·schemaIntegrations > Data API통합 > 데이터 API
    응답 행 상한Integrations > Data API > Settings > Max rows통합 > 데이터 API > 설정 > 최대 행 수

    마이그레이션 성공 메시지만으로 애플리케이션에 필요한 모든 객체가 있다는 사실까지 증명되지는 않는다. timeline에서는 테이블 9개, RPC 4개와 비공개 item-images 버킷을 직접 확인했다.

db push는 기본적으로 마이그레이션만 적용한다. 프로덕션에는 --include-seed를 붙이지 않는다. 로컬 테스트 계정과 약한 비밀번호가 들어 있는 seed라면 그대로 보안 사고가 된다. 운영 초기 데이터는 별도의 멱등한 스크립트나 관리자 흐름으로 만든다.

Google 설정을 시작하기 전에 Supabase가 받을 callback 주소를 먼저 복사한다.

  • English: Authentication > Sign In / Providers > Google
  • 한국어 뜻: 인증 > 로그인 / 제공업체 > Google

Google 항목을 열면 Callback URL이 보인다. 아직 provider를 켜지 않아도 이 값을 복사할 수 있다. 보통 다음 모양이다.

https://<project-ref>.supabase.co/auth/v1/callback

Google Auth Platform 화면 찾기

예전 APIs & Services > OAuth consent screen 설명을 따라가지 않는다. 현재는 Google Auth Platform(한국어: Google 인증 플랫폼) 아래 다섯 화면으로 나뉜다. 모든 화면에서 상단 프로젝트 선택기가 올바른 Google Cloud 프로젝트를 가리키는지 먼저 확인한다.

화면한국어 UI바로가기
Overview개요Auth overview
Branding브랜딩Auth branding
Audience대상Auth audience
Clients클라이언트Auth clients
Data Access데이터 액세스Auth scopes

Google Cloud 프로젝트 자체가 없다면 상단의 Select a project > New Project (프로젝트 선택 > 새 프로젝트)에서 먼저 만든다.

동의 화면과 테스트 사용자 설정

  1. Overview > Get started (개요 > 시작하기)를 누른다.

  2. 초기 설정 마법사에서 App name과 User support email을 넣고, 일반 Google 계정을 받을 앱은 Audience = External (대상 = 외부)을 선택한다. 개발자 연락처 이메일까지 넣고 완료한다.

  3. Audience > Test users > Add users (대상 > 테스트 사용자 > 사용자 추가)에서 배포 검증에 쓸 본인 계정을 추가한다. 테스트 상태에서는 이 단계를 빠뜨렸을 때 403: access_denied로 막힐 수 있다.

  4. Data Access > Add or remove scopes (데이터 액세스 > 범위 추가 또는 삭제)를 연다. 현재 Supabase 공식 가이드 기준 최소 범위는 openid, userinfo.email, userinfo.profile이다. 기본으로 들어 있는 email/profile을 확인하고, openid가 없으면 추가한다. Drive·Calendar 같은 다른 Google API scope는 필요할 때만 추가한다.

OAuth 클라이언트 만들기

  1. Clients > Create Client (클라이언트 > 클라이언트 만들기)를 누른다.

  2. Application type = Web application (애플리케이션 유형 = 웹 애플리케이션)을 선택하고 관리용 이름을 붙인다.

  3. Authorized JavaScript origins (승인된 JavaScript 원본)에는 origin만 넣는다.

    https://app.example.com
    http://localhost:3000 # 클라우드 Auth로 로컬 시험을 할 때만

    경로나 /auth/callback을 붙이지 않는다. timeline처럼 Google JS SDK·One Tap 없이 signInWithOAuth 리디렉션만 쓰는 흐름은 비워도 동작했지만, 현재 Supabase 공식 가이드는 앱 origin 등록을 안내한다.

  4. Authorized redirect URIs (승인된 리디렉션 URI)에 앞에서 복사한 Supabase Callback URL을 넣는다.

    https://<project-ref>.supabase.co/auth/v1/callback
  5. Create (만들기)를 누르고 Client ID와 Client Secret을 즉시 비밀번호 관리자에 보관한다. Client Secret은 소스 코드나 브라우저 공개 환경 변수에 넣지 않는다.

승인된 리디렉션 URI는 scheme, 대소문자, 경로와 마지막 /까지 실제 요청과 같아야 한다. 틀리면 Google의 redirect_uri_mismatch가 난다.

Supabase에서 Google provider 켜기

  • English: Authentication > Sign In / Providers > Google
  • 한국어 뜻: 인증 > 로그인 / 제공업체 > Google

Google 항목을 다시 열고 다음을 설정한다.

화면 필드값
Enable Sign in with Google켬
Client IDsGoogle에서 만든 Web client ID
Client SecretGoogle에서 만든 client secret
Skip nonce checks끔 — One Tap용 예외를 일반 OAuth에 켜지 않는다

Save를 누른 뒤, 같은 화면의 Callback URL과 Google의 Authorized redirect URI가 글자 단위로 같은지 다시 확인한다.

Supabase의 앱 복귀 주소 설정

  • English: Authentication > URL Configuration
  • 한국어 뜻: 인증 > URL 구성

Site URL과 Redirect URLs는 다음처럼 나눈다.

Site URL
https://app.example.com
Redirect URLs
https://app.example.com/auth/callback
https://*-<vercel-team-slug>.vercel.app/**
http://localhost:3000/** # 클라우드 Auth로 로컬 OAuth를 시험할 때만

Redirect URLs의 Add URL로 각 주소를 추가하고 Save한다. 도메인이 아직 없다면 첫 Vercel 프로덕션 주소를 Site URL로 잠시 쓰고, 커스텀 도메인을 붙인 뒤 다시 돌아와 바꾼다.

프로덕션 콜백은 정확한 경로를 쓰고, ** 와일드카드는 주소가 계속 바뀌는 프리뷰와 로컬에만 쓴다. Vercel의 팀·계정 slug와 실제 프리뷰 URL 모양을 보고 패턴을 만든다.

Google 앱 공개는 마지막에

전체 OAuth 흐름을 실제 도메인에서 검증한 뒤 Audience > Publish app (대상 > 앱 게시)로 Testing에서 In production으로 전환한다. Branding과 scope 검증은 별도 상태이므로, 앱 이름·로고나 민감 scope를 추가했다면 Google 화면이 요구하는 검토 절차를 따른다.

GitHub 저장소 import

  • English: Dashboard > Add New... > Project
  • 한국어 뜻: 대시보드 > 새로 추가 > 프로젝트
  1. Git provider로 GitHub를 선택하고, 저장소 목록에서 대상 저장소의 Import를 누른다.

  2. Configure Project 화면에서 Framework Preset = Next.js가 자동 감지됐는지 확인한다. 모노레포라면 Root Directory만 실제 앱 위치로 맞춘다. Build Command·Output Directory는 실패 원인이 확실하지 않으면 기본값을 유지한다.

  3. 첫 배포 전에 같은 화면의 Environment Variables를 펼쳐 Supabase URL과 공개 키를 넣는다. timeline은 두 값이 없으면 모듈 로드 시점에 실패하므로 Production·Preview·Development에 모두 필요하다.

  4. Deploy를 누른다. 빌드가 끝나면 생성된 프로덕션 *.vercel.app 주소를 기록한다.

환경 변수

  • English: Project > Settings > Environment Variables
  • 한국어 뜻: 프로젝트 > 설정 > 환경 변수

첫 배포 뒤 값을 추가하거나 바꿀 때는 이 화면의 추가 폼에 Name·Value를 넣고 적용할 Environment를 선택한 다음 Save한다. timeline의 실제 변수는 다음과 같다.

변수값을 찾는 곳적용 환경공개 여부
NEXT_PUBLIC_SUPABASE_URLSupabase Connect 또는 Integrations > Data APIProduction·Preview·Development공개
NEXT_PUBLIC_SUPABASE_ANON_KEYSupabase Settings > API Keys의 publishable 또는 legacy anon keyProduction·Preview·Development공개 전제
NEXT_PUBLIC_SITE_URL현재 프로덕션 주소, 최종적으로 커스텀 도메인Production만공개

NEXT_PUBLIC_SITE_URL은 첫 배포 전에는 몰라도 된다. timeline은 VERCEL_URL로 폴백하므로 첫 배포 뒤 생긴 주소를 Production에 넣고 재배포한다. Preview·Development에도 고정하면 프리뷰의 OG와 OAuth가 프로덕션을 가리켜 변경을 검증하기 어렵다.

Next.js의 NEXT_PUBLIC_ 값은 브라우저 번들에 들어간다. publishable 키에는 맞지만 secret 키에는 절대 붙이지 않는다. Supabase secret/service-role 키와 DB 비밀번호는 이 앱의 Vercel 런타임에 넣지 않는다.

환경 변수를 추가하거나 바꾼 뒤에는 새 배포가 필요하다. 이미 만들어진 배포에는 소급되지 않는다.

함수와 DB 리전

  • English: Project > Settings > Functions > Function Regions
  • 한국어 뜻: 프로젝트 > 설정 > 함수 > 함수 리전

함수는 사용자보다 데이터 소스에 가깝게 둔다. 정적 파일은 전 세계 CDN에 있지만, 서버 컴포넌트와 Route Handler가 DB를 세 번 순차 호출하면 함수↔DB 왕복도 세 번 발생한다.

Supabase: ap-northeast-2 (Seoul)
Vercel Functions: icn1 (Seoul)

Function Regions를 펼쳐 Seoul, South Korea (icn1)을 선택하고 저장한다. Vercel Functions의 기본 리전은 iad1이므로 대시보드 기본값을 그대로 믿지 않는다.

리전 변경도 기존 배포에 소급되지 않는다.

  • English: Project > Deployments > <latest production deployment> > ... > Redeploy
  • 한국어 뜻: 프로젝트 > 배포 > 최신 프로덕션 배포 > 더보기 > 재배포

새 배포 뒤 응답의 x-vercel-id 헤더가 icn1::...인지 확인한다. Deployment 상세의 요약과 Observability > Vercel Functions에서도 실제 함수 실행을 확인할 수 있다.

4. Cloudflare DNS로 커스텀 도메인을 붙인다

섹션 제목: “4. Cloudflare DNS로 커스텀 도메인을 붙인다”

이 순서는 도메인의 nameserver가 이미 Cloudflare를 가리키는 경우다. 아직 Cloudflare에 도메인이 없다면 Account home > Domains > Onboard a domain (계정 홈 > 도메인 > 도메인 온보딩)으로 apex domain을 먼저 추가하고 등록기관에서 nameserver를 변경한다.

  1. Vercel 프로젝트에 도메인을 먼저 추가한다.

    • English: Project > Settings > Domains > Add Domain
    • 한국어 뜻: 프로젝트 > 설정 > 도메인 > 도메인 추가

    timeline.upggu.com처럼 사용할 전체 도메인을 입력하고 Production에 연결한다. Vercel이 표시한 DNS 레코드의 Type·Name·Value를 복사한다. 일반 예제 값을 외워 쓰지 않는다.

  2. Cloudflare에서 해당 zone의 DNS 레코드 화면으로 간다.

    • English: Account home > <domain> > DNS > Records > Add record
    • 한국어 뜻: 계정 홈 > <도메인> > DNS > 레코드 > 레코드 추가
  3. 서브도메인 CNAME을 만든다.

    Type: CNAME
    Name: timeline
    Target: <Vercel Domains 화면이 제시한 고유 대상>
    Proxy status: DNS only (회색 구름)
    TTL: Auto

    Cloudflare 화면에서는 Target이 Content로 보일 수 있다. Proxy status 토글은 주황색 Proxied가 아니라 회색 DNS only인지 확인하고 Save한다.

    위 예시는 서브도메인이다. 루트 도메인은 A 레코드나 CNAME flattening을 쓸 수 있으므로, 레코드 값을 외워 쓰지 말고 현재 Vercel Domains 화면이 제시한 유형과 대상을 그대로 따른다.

    Vercel이 소유권 확인용 TXT 레코드도 요구하면, 같은 Cloudflare 화면에서 별도의 TXT 레코드를 추가한다. TXT는 proxy 토글이 없으며 Name과 Content를 Vercel 화면 그대로 복사한다.

  4. Vercel의 Settings > Domains로 돌아가 Valid Configuration과 인증서 발급 완료를 확인한다. DNS 조회 결과도 Vercel이 제시한 대상과 맞는지 본다.

  5. 새 도메인을 절대 URL의 기준으로 승격한다. 다음 네 군데를 순서대로 다시 방문한다.

    제품영문 메뉴바꿀 것
    VercelSettings > Environment VariablesProduction의 NEXT_PUBLIC_SITE_URL → 새 도메인
    VercelDeployments > latest production > ... > Redeploy환경 변수를 넣은 새 배포 생성
    SupabaseAuthentication > URL ConfigurationSite URL과 정확한 프로덕션 /auth/callback
    GoogleGoogle Auth Platform > BrandingHomepage·Privacy Policy·Authorized domains
    GoogleGoogle Auth Platform > Clients > <client>등록했다면 Authorized JavaScript origins에 새 origin 추가
  6. Supabase의 기존 *.vercel.app 프리뷰 패턴은 지우지 않는다. 프리뷰 로그인 검증에 계속 필요하다.

Cloudflare의 주황 구름은 DNS 기능이 아니라 Cloudflare가 HTTP 트래픽 중간에 들어오는 리버스 프록시다. Vercel도 이미 CDN·TLS·보호 계층을 제공하므로 둘을 겹치면 인증서 검증, 실제 클라이언트 IP, 지역 판정과 캐시 무효화가 복잡해진다. Vercel도 외부 프록시 중첩을 권장하지 않으므로 이 구성은 DNS only로 시작한다. Cloudflare WAF 같은 명확한 요구가 생겨 프록시를 켜려면 Vercel의 프록시·ACME 요구와 Cloudflare SSL 모드를 별도 설계한다.

DNS only에서는 Cloudflare가 HTTPS 트래픽을 중계하지 않으므로 Cloudflare의 SSL/TLS mode를 바꿀 필요가 없다. 의도적으로 proxy를 켠 뒤 redirect loop가 생겼다면 SSL/TLS > Overview (SSL/TLS > 개요)에서 Flexible인지 확인한다. 다만 문제를 격리할 때의 첫 조치는 다시 DNS only로 돌려 Vercel에 직접 연결하는 것이다.

5. 배포 완료를 사용자 흐름으로 증명한다

섹션 제목: “5. 배포 완료를 사용자 흐름으로 증명한다”

시크릿 창과 두 개의 서로 다른 계정으로 확인한다.

영역실제 사용자 검증함께 볼 대시보드 메뉴
DNS·TLS커스텀 도메인 HTTPS 응답Vercel Settings > Domains, Cloudflare DNS > Records
실행 위치동적 응답의 x-vercel-id가 icn1::...Vercel Deployments 또는 Observability > Vercel Functions
스키마핵심 테이블·RPC·RLS 존재Supabase Table Editor, SQL Editor
OAuthGoogle → Supabase callback → 앱 callback → 보호 페이지Supabase Authentication > Users, Google Overview > Errors
권한사용자 A의 비공개 데이터를 사용자 B와 익명이 읽지 못함Supabase 각 table의 RLS policies
파일실제 브라우저 업로드와 다시 읽기Supabase Storage > Buckets > <bucket>
프리뷰프리뷰 로그인 후 같은 프리뷰로 복귀Supabase Authentication > URL Configuration
절대 URLOG·canonical·robots·sitemap이 새 도메인 사용Vercel Settings > Environment Variables
운영관리자만 운영 화면 접근Supabase Authentication > Users, Table Editor > profiles

페이지가 200이라는 사실은 Auth·RLS·Storage가 맞다는 증거가 아니다. 로컬에서 크롤러가 접근하지 못한 OG 카드나 실제 OAuth provider 흐름은 공개 URL에서 마지막으로 확인해야 한다.

profiles가 auth.users INSERT trigger로 생기는 앱은 순서를 지켜야 한다.

  1. 실제 Google 계정으로 한 번 로그인한다.
  2. Authentication > Users (인증 > 사용자)에서 계정 생성을 확인한다.
  3. Table Editor > public > profiles에서 trigger가 만든 profile 행을 확인한다.
  4. SQL Editor > New query에서 그 계정 하나만 식별하는 조건으로 관리자 필드를 갱신한다.
  5. 관리자 화면에 들어간 뒤 일반 계정에서는 같은 화면이 거부되는지 다시 확인한다.

프로덕션 auth.users에 로컬 seed 계정을 직접 넣거나, 조건 없는 update profiles set is_admin = true를 실행하지 않는다.

증상먼저 볼 곳
Google의 redirect_uri_mismatchGoogle OAuth client의 Supabase callback 정확성
로그인 후 localhost나 엉뚱한 앱으로 감Supabase Site URL과 코드의 redirectTo
프로덕션만 되고 프리뷰 로그인 실패Supabase Redirect URLs의 Vercel 와일드카드
*.vercel.app은 되는데 커스텀 도메인만 실패Cloudflare CNAME·proxy 상태, Vercel Domains
커스텀 도메인에서 OAuth만 예전 주소로 복귀Supabase Site/Redirect URL, Vercel 사이트 URL 재배포
환경 변수를 바꿨는데 HTML·OG가 그대로새 Vercel 배포를 만들었는지
배포 DB가 비어 있음정상일 수 있다. db push는 seed를 자동 배포하지 않음
다른 사용자 데이터가 보임RLS 활성화·정책과 secret/service-role 사용 경로
Cloudflare를 켠 뒤 redirect loopProxy 상태와 SSL mode. 우선 DNS only로 원인 격리

mechurak/timeline issue #8은 이 순서를 실제로 진행하며 체크리스트와 막힌 지점을 함께 남긴 기록이다.

선택결과와 이유
Supabase ap-northeast-2 + Vercel icn1순차 DB 왕복이 많은 앱이라 같은 도시에 배치
7개 migration만 db push로컬 seed 계정은 배포하지 않고 운영 DB를 빈 상태로 시작
Google → Supabase → Next.js PKCE callbackcallback 두 종류를 분리하고 세션을 쿠키에 저장
NEXT_PUBLIC_SITE_URL은 Production만프리뷰 OG와 OAuth가 프리뷰 주소를 유지
Cloudflare CNAME, DNS onlytimeline.upggu.com을 Vercel에 직접 연결
도메인 연결 뒤 재검증OG·robots/sitemap·OAuth 복귀가 새 도메인 기준으로 통과

이 사례의 프로젝트 ID, 환경 변수 이름, 관리자 SQL을 그대로 복사하지 않는다. 재사용할 것은 의존 순서, URL의 소유자, 비밀의 경계, 리전 배치, 배포 후 검증표다.