실전 가이드 — Supabase 앱 첫 배포
첫 배포는 서버 하나를 올리는 일이 아니라 서로 다른 제어판의 계약을 맞추는 일이다
로컬에서 잘 되던 앱을 인터넷에 공개할 때는 코드만 배포되지 않는다. 데이터베이스 스키마, 브라우저에 공개할 키, OAuth 콜백, 실행 리전, DNS와 절대 URL 기준점까지 함께 옮겨야 한다. 이 장은 Next.js + Supabase 앱을 Vercel에 배포하고 Google 로그인과 Cloudflare DNS를 붙이는 한 번의 전체 흐름을 다룬다.
먼저 그릴 지도
섹션 제목: “먼저 그릴 지도”다섯 시스템이 있지만 책임은 겹치지 않는다.
| 시스템 | 맡는 것 | 이 시스템에 넣지 않는 것 |
|---|---|---|
| GitHub | 코드와 마이그레이션 파일 | 운영 시크릿 |
| Supabase | Postgres·RLS·Storage·사용자 세션 | Google 동의 화면, 앱 빌드 |
| 사용자 인증과 동의 | 앱의 최종 복귀 경로 허용 판단 | |
| Vercel | Next.js 빌드·함수·프리뷰·TLS | 영속 데이터 |
| Cloudflare | 도메인의 DNS 레코드 | 이 구성에서는 앱 실행과 CDN |
위 그림은 배포 뒤의 실행 트래픽을 보여준다. 설정할 때는 반대 방향으로 값을 복사하는 일이 많다. 다음 표를 먼저 읽어 두면 어느 대시보드에서 값을 만들고 어느 대시보드에 붙여 넣어야 하는지 헷갈리지 않는다.
| 값을 만든 곳 | 전달하는 설정값 | 값을 넣는 곳 | 만들어지는 관계 |
|---|---|---|---|
| Supabase | Project URL·publishable key | Vercel Settings > Environment Variables | Vercel에서 실행되는 앱 → Supabase Data API·Auth |
| Supabase | Auth Callback URL | Google Clients > Authorized redirect URIs | Google 인증 완료 → Supabase Auth |
| OAuth Client ID·Client Secret | Supabase Authentication > Sign In / Providers > Google | Supabase Auth → Google 사용자 인증 위임 | |
| Vercel | CNAME Target·소유권 확인 TXT | Cloudflare DNS > Records | 커스텀 도메인 → Vercel 배포 |
| Vercel에 추가한 커스텀 도메인 | 최종 앱 URL·origin | Vercel 환경 변수·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의 승인된 리디렉션 URI | https://<ref>.supabase.co/auth/v1/callback | Google → Supabase Auth |
| Supabase Redirect URLs | https://app.example.com/auth/callback | Supabase Auth → 우리 앱 |
| Supabase Site URL | https://app.example.com | redirectTo가 없을 때의 기본 복귀점 |
| Vercel의 사이트 URL 환경 변수 | https://app.example.com | canonical·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 - 한국어 뜻:
대시보드 홈 > 조직 > 새 프로젝트
-
Supabase Dashboard에서 프로젝트를 담을 Organization을 선택하고 New project를 누른다.
-
프로젝트 생성 폼을 채운다.
필드 timeline에서 선택한 값주의 Project name알아볼 수 있는 이름 URL이나 DB 이름과 같을 필요는 없다 Database password비밀번호 관리자가 만든 값 CLI 연결에 다시 필요하다 RegionNortheast Asia (Seoul)Vercel icn1과 가까운ap-northeast-2Pricing plan현재 용도에 맞는 플랜 한도·상업 이용 조건은 배포 전에 다시 확인 사용자의 위치보다 DB를 호출하는 서버 함수와 가까운 리전을 고른다. 리전은 나중에 바꾸려면 새 프로젝트를 만들고 데이터를 이전해야 하므로 이 화면에서 결정한다.
-
Create new project를 누르고 상태가 준비될 때까지 기다린다.
프로젝트 참조 ID와 앱 연결 값 찾기
| 필요한 값 | 영문 메뉴 | 한국어 뜻 |
|---|---|---|
| Project reference | Settings > General > Reference ID | 설정 > 일반 > 참조 ID |
| Project URL | 상단 Connect 또는 Integrations > Data API | 연결 또는 통합 > 데이터 API |
| publishable key | 상단 Connect 또는 Settings > API Keys | 연결 또는 설정 > API 키 |
| legacy anon key | Settings > 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 키를 넣어서는 안 된다.
로컬 마이그레이션 연결
-
터미널에서 로컬 프로젝트를 원격 프로젝트에 연결하고 적용 대상을 미리 본다.
터미널 창 supabase loginsupabase link --project-ref <project-ref>supabase migration list --linkedsupabase db push --linked --dry-run -
예상한 마이그레이션만 보이면 적용하고 다시 목록을 확인한다.
터미널 창 supabase db push --linkedsupabase migration list --linked -
Dashboard에서 결과를 확인한다.
확인 대상 영문 메뉴 한국어 뜻 앱 테이블과 RLS Table Editor > public > <table>테이블 편집기 > public > <테이블>함수·트리거·마이그레이션 이력 SQL Editor > New querySQL 편집기 > 새 쿼리Storage 버킷 Storage > Buckets스토리지 > 버킷Data API URL·schema Integrations > Data API통합 > 데이터 API응답 행 상한 Integrations > Data API > Settings > Max rows통합 > 데이터 API > 설정 > 최대 행 수마이그레이션 성공 메시지만으로 애플리케이션에 필요한 모든 객체가 있다는 사실까지 증명되지는 않는다.
timeline에서는 테이블 9개, RPC 4개와 비공개item-images버킷을 직접 확인했다.
db push는 기본적으로 마이그레이션만 적용한다. 프로덕션에는 --include-seed를 붙이지 않는다.
로컬 테스트 계정과 약한 비밀번호가 들어 있는 seed라면 그대로 보안 사고가 된다. 운영 초기 데이터는
별도의 멱등한 스크립트나 관리자 흐름으로 만든다.
2. Google과 Supabase Auth를 잇는다
섹션 제목: “2. Google과 Supabase Auth를 잇는다”Google 설정을 시작하기 전에 Supabase가 받을 callback 주소를 먼저 복사한다.
- English:
Authentication > Sign In / Providers > Google - 한국어 뜻:
인증 > 로그인 / 제공업체 > Google
Google 항목을 열면 Callback URL이 보인다. 아직 provider를 켜지 않아도 이 값을 복사할 수 있다. 보통 다음 모양이다.
https://<project-ref>.supabase.co/auth/v1/callbackGoogle 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
(프로젝트 선택 > 새 프로젝트)에서 먼저 만든다.
동의 화면과 테스트 사용자 설정
-
Overview > Get started(개요 > 시작하기)를 누른다. -
초기 설정 마법사에서
App name과User support email을 넣고, 일반 Google 계정을 받을 앱은Audience = External(대상 = 외부)을 선택한다. 개발자 연락처 이메일까지 넣고 완료한다. -
Audience > Test users > Add users(대상 > 테스트 사용자 > 사용자 추가)에서 배포 검증에 쓸 본인 계정을 추가한다. 테스트 상태에서는 이 단계를 빠뜨렸을 때403: access_denied로 막힐 수 있다. -
Data Access > Add or remove scopes(데이터 액세스 > 범위 추가 또는 삭제)를 연다. 현재 Supabase 공식 가이드 기준 최소 범위는openid,userinfo.email,userinfo.profile이다. 기본으로 들어 있는 email/profile을 확인하고,openid가 없으면 추가한다. Drive·Calendar 같은 다른 Google API scope는 필요할 때만 추가한다.
OAuth 클라이언트 만들기
-
Clients > Create Client(클라이언트 > 클라이언트 만들기)를 누른다. -
Application type = Web application(애플리케이션 유형 = 웹 애플리케이션)을 선택하고 관리용 이름을 붙인다. -
Authorized JavaScript origins(승인된 JavaScript 원본)에는 origin만 넣는다.https://app.example.comhttp://localhost:3000 # 클라우드 Auth로 로컬 시험을 할 때만경로나
/auth/callback을 붙이지 않는다.timeline처럼 Google JS SDK·One Tap 없이signInWithOAuth리디렉션만 쓰는 흐름은 비워도 동작했지만, 현재 Supabase 공식 가이드는 앱 origin 등록을 안내한다. -
Authorized redirect URIs(승인된 리디렉션 URI)에 앞에서 복사한 Supabase Callback URL을 넣는다.https://<project-ref>.supabase.co/auth/v1/callback -
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 IDs | Google에서 만든 Web client ID |
Client Secret | Google에서 만든 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 URLhttps://app.example.com
Redirect URLshttps://app.example.com/auth/callbackhttps://*-<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 화면이 요구하는 검토 절차를 따른다.
3. Vercel에 실행 환경을 만든다
섹션 제목: “3. Vercel에 실행 환경을 만든다”GitHub 저장소 import
- English:
Dashboard > Add New... > Project - 한국어 뜻:
대시보드 > 새로 추가 > 프로젝트
-
Git provider로 GitHub를 선택하고, 저장소 목록에서 대상 저장소의 Import를 누른다.
-
Configure Project 화면에서
Framework Preset = Next.js가 자동 감지됐는지 확인한다. 모노레포라면Root Directory만 실제 앱 위치로 맞춘다. Build Command·Output Directory는 실패 원인이 확실하지 않으면 기본값을 유지한다. -
첫 배포 전에 같은 화면의 Environment Variables를 펼쳐 Supabase URL과 공개 키를 넣는다.
timeline은 두 값이 없으면 모듈 로드 시점에 실패하므로 Production·Preview·Development에 모두 필요하다. -
Deploy를 누른다. 빌드가 끝나면 생성된 프로덕션
*.vercel.app주소를 기록한다.
환경 변수
- English:
Project > Settings > Environment Variables - 한국어 뜻:
프로젝트 > 설정 > 환경 변수
첫 배포 뒤 값을 추가하거나 바꿀 때는 이 화면의 추가 폼에 Name·Value를 넣고 적용할 Environment를
선택한 다음 Save한다. timeline의 실제 변수는 다음과 같다.
| 변수 | 값을 찾는 곳 | 적용 환경 | 공개 여부 |
|---|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase Connect 또는 Integrations > Data API | Production·Preview·Development | 공개 |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase Settings > API Keys의 publishable 또는 legacy anon key | Production·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를 변경한다.
-
Vercel 프로젝트에 도메인을 먼저 추가한다.
- English:
Project > Settings > Domains > Add Domain - 한국어 뜻:
프로젝트 > 설정 > 도메인 > 도메인 추가
timeline.upggu.com처럼 사용할 전체 도메인을 입력하고 Production에 연결한다. Vercel이 표시한 DNS 레코드의 Type·Name·Value를 복사한다. 일반 예제 값을 외워 쓰지 않는다. - English:
-
Cloudflare에서 해당 zone의 DNS 레코드 화면으로 간다.
- English:
Account home > <domain> > DNS > Records > Add record - 한국어 뜻:
계정 홈 > <도메인> > DNS > 레코드 > 레코드 추가
- English:
-
서브도메인 CNAME을 만든다.
Type: CNAMEName: timelineTarget: <Vercel Domains 화면이 제시한 고유 대상>Proxy status: DNS only (회색 구름)TTL: AutoCloudflare 화면에서는 Target이
Content로 보일 수 있다. Proxy status 토글은 주황색Proxied가 아니라 회색DNS only인지 확인하고 Save한다.위 예시는 서브도메인이다. 루트 도메인은 A 레코드나 CNAME flattening을 쓸 수 있으므로, 레코드 값을 외워 쓰지 말고 현재 Vercel Domains 화면이 제시한 유형과 대상을 그대로 따른다.
Vercel이 소유권 확인용 TXT 레코드도 요구하면, 같은 Cloudflare 화면에서 별도의 TXT 레코드를 추가한다. TXT는 proxy 토글이 없으며 Name과 Content를 Vercel 화면 그대로 복사한다.
-
Vercel의
Settings > Domains로 돌아가Valid Configuration과 인증서 발급 완료를 확인한다. DNS 조회 결과도 Vercel이 제시한 대상과 맞는지 본다. -
새 도메인을 절대 URL의 기준으로 승격한다. 다음 네 군데를 순서대로 다시 방문한다.
제품 영문 메뉴 바꿀 것 Vercel Settings > Environment VariablesProduction의 NEXT_PUBLIC_SITE_URL→ 새 도메인Vercel Deployments > latest production > ... > Redeploy환경 변수를 넣은 새 배포 생성 Supabase Authentication > URL ConfigurationSite URL과 정확한 프로덕션 /auth/callbackGoogle Google Auth Platform > BrandingHomepage·Privacy Policy·Authorized domains Google Google Auth Platform > Clients > <client>등록했다면 Authorized JavaScript origins에 새 origin 추가 -
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 |
| OAuth | Google → Supabase callback → 앱 callback → 보호 페이지 | Supabase Authentication > Users, Google Overview > Errors |
| 권한 | 사용자 A의 비공개 데이터를 사용자 B와 익명이 읽지 못함 | Supabase 각 table의 RLS policies |
| 파일 | 실제 브라우저 업로드와 다시 읽기 | Supabase Storage > Buckets > <bucket> |
| 프리뷰 | 프리뷰 로그인 후 같은 프리뷰로 복귀 | Supabase Authentication > URL Configuration |
| 절대 URL | OG·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로 생기는 앱은 순서를 지켜야 한다.
- 실제 Google 계정으로 한 번 로그인한다.
Authentication > Users(인증 > 사용자)에서 계정 생성을 확인한다.Table Editor > public > profiles에서 trigger가 만든 profile 행을 확인한다.SQL Editor > New query에서 그 계정 하나만 식별하는 조건으로 관리자 필드를 갱신한다.- 관리자 화면에 들어간 뒤 일반 계정에서는 같은 화면이 거부되는지 다시 확인한다.
프로덕션 auth.users에 로컬 seed 계정을 직접 넣거나, 조건 없는 update profiles set is_admin = true를
실행하지 않는다.
증상으로 찾는 잘못된 제어판
섹션 제목: “증상으로 찾는 잘못된 제어판”| 증상 | 먼저 볼 곳 |
|---|---|
Google의 redirect_uri_mismatch | Google 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 loop | Proxy 상태와 SSL mode. 우선 DNS only로 원인 격리 |
사례 — timeline의 첫 배포
섹션 제목: “사례 — timeline의 첫 배포”mechurak/timeline issue #8은 이 순서를 실제로
진행하며 체크리스트와 막힌 지점을 함께 남긴 기록이다.
| 선택 | 결과와 이유 |
|---|---|
Supabase ap-northeast-2 + Vercel icn1 | 순차 DB 왕복이 많은 앱이라 같은 도시에 배치 |
7개 migration만 db push | 로컬 seed 계정은 배포하지 않고 운영 DB를 빈 상태로 시작 |
| Google → Supabase → Next.js PKCE callback | callback 두 종류를 분리하고 세션을 쿠키에 저장 |
NEXT_PUBLIC_SITE_URL은 Production만 | 프리뷰 OG와 OAuth가 프리뷰 주소를 유지 |
| Cloudflare CNAME, DNS only | timeline.upggu.com을 Vercel에 직접 연결 |
| 도메인 연결 뒤 재검증 | OG·robots/sitemap·OAuth 복귀가 새 도메인 기준으로 통과 |
이 사례의 프로젝트 ID, 환경 변수 이름, 관리자 SQL을 그대로 복사하지 않는다. 재사용할 것은 의존 순서, URL의 소유자, 비밀의 경계, 리전 배치, 배포 후 검증표다.
참고 자료
섹션 제목: “참고 자료”- Supabase Database Migrations —
link,db push, 선택적 seed 배포 - Supabase API Keys — publishable·secret·legacy key의 용도와 노출 경계
- Supabase Login with Google — Google callback, 최소 scope, PKCE callback
- Supabase Redirect URLs — Site URL, 정확한 프로덕션 URL과 Vercel 프리뷰 와일드카드
- Google OAuth web server applications — 승인된 redirect URI의 정확한 일치 조건
- Google Auth Platform Audience — External/Internal, Testing/In production과 test users
- Vercel Regions — 기본
iad1,icn1, 데이터 소스와 함수 리전 배치 - Vercel Environment Variables — Production·Preview·Development 범위와 새 배포 적용
- Vercel Custom Domain — 외부 DNS에서 Vercel이 제시한 레코드 연결
- Vercel의 외부 프록시 안내 — TLS 검증·IP·캐시·지원 범위의 주의점
- Cloudflare DNS 레코드 관리 — 레코드 추가 화면과 Type·Name·Content·TTL 입력
- Cloudflare Proxy status — Proxied와 DNS only의 실제 트래픽 차이
timelineissue #8 — 첫 배포 결정·실행·검증 기록