https://drive.google.com/file/d/1kI4g9ici-3R3o5lIJWV4LTnyi_Xug-6V/view?usp=drive_link
목적: Next.js(App Router) + Prisma ORM + PostgreSQL + Supabase(로컬 CLI / 호스티드 Cloud) + Vercel 배포 환경에서의 DB 연동 스펙을 도메인 비의존적으로 정의한다. 새 프로젝트를 시작할 때 이 문서를 그대로 적용하면 로컬 개발부터 운영 배포까지의 DB 파이프라인이 구성된다.
| 구성 요소 | 기술 | 버전 (최소) | 비고 |
|---|---|---|---|
| Runtime | Node.js | 24.x | .nvmrc로 고정 |
| Package Manager | pnpm | 9.x | package.json의 packageManager 필드 |
| Framework | Next.js (App Router) | 16.x | next.config.ts |
| ORM | Prisma | 6.x | @prisma/client (런타임), prisma (devDep, CLI) |
| DB Engine | PostgreSQL | 17 | Supabase CLI의 config.toml에서 major_version 지정 |
| 로컬 DB | Supabase CLI (Docker) | latest (pnpm dlx supabase) |
PostgreSQL + 최소 서비스 |
| 호스티드 DB | Supabase Cloud | — | Transaction Pooler + Direct Connection |
| 배포 플랫폼 | Vercel | — | Git 연동 자동배포 |
| 환경변수 검증 | Zod | 4.x | lib/env.ts에서 런타임 스키마 검증 |
| CI/CD | GitHub Actions | — | prod 마이그레이션 자동화 |
로컬·Preview·Production 세 계층으로 분리한다. 로컬은 호스티드와 완전히 분리된 별도 DB 인스턴스이다.
| 계층 | DB 인스턴스 | 앱 URL | 마이그레이션 적용 방법 |
|---|---|---|---|
| Local | Supabase CLI (Docker, 127.0.0.1:54322) |
http://localhost:3000 |
pnpm db:migrate (자유롭게) |
| Preview | 호스티드 Supabase (Production과 공유¹) | PR별 Vercel Preview URL | 별도 적용 안 함 (prod 스키마 공유) |
| Production | 호스티드 Supabase | Production URL | main 머지 시 GitHub Action 자동 db:deploy |
¹ 초기에는 Preview와 Production이 같은 DB를 공유한다. 실 사용자 데이터가 적재되기 시작하면 Preview용 별도 Supabase 프로젝트로 분리한다.
핵심 원칙: dev/prod 패리티는 "같은 엔진(PostgreSQL) + 같은 스키마"를 뜻하지, 같은 인스턴스를 공유한다는 뜻이 아니다.
<project-root>/
├── .env # 로컬 Supabase 기본값 (gitignore 대상)
├── .env.example # 커밋 대상 — 로컬 기본값 템플릿
├── .env.local # Vercel CLI가 생성 (gitignore 대상)
├── .gitignore # .env, .env.local, .env.*.local 포함
├── prisma/
│ ├── schema.prisma # Prisma 스키마 (SoT)
│ ├── migrations/ # Prisma 마이그레이션 SQL 디렉토리
│ │ ├── <timestamp>_<name>/ # 각 마이그레이션 (migration.sql)
│ │ └── migration_lock.toml # provider lock (postgresql)
│ └── seed.mjs # 시드 스크립트 진입점
├── prisma.config.ts # Prisma 설정 (schema 경로, seed 명령)
├── lib/
│ ├── db.ts # PrismaClient 싱글턴 팩토리
│ ├── env.ts # 환경변수 Zod 스키마 + getter 함수
│ └── health.ts # DB 헬스체크 로직
├── app/
│ └── api/health/route.ts # GET /api/health — DB 연결 검증 엔드포인트
├── scripts/
│ ├── db/
│ │ ├── guarded-migrate.mjs # 로컬 전용 마이그레이션 가드
│ │ └── test-integration-local.mjs # 통합테스트 원스텝 러너
│ ├── load-env.ts # CLI 스크립트용 .env 로더
│ ├── sync-env.mjs # 워크트리 간 .env 복사
│ └── validate-env.mjs # 빌드 시 환경변수 사전검증
├── supabase/
│ └── config.toml # Supabase CLI 로컬 설정
├── vercel.json # Vercel 빌드/설치 명령
├── .github/
│ └── workflows/
│ └── migrate-prod.yml # prod 마이그레이션 자동화 Action
└── package.json # npm scripts (db:*, 빌드 훅)
supabase/config.toml 설정project_id = "<project-name>"
[api]
enabled = true
port = 54321
schemas = ["public", "graphql_public"]
extra_search_path = ["public", "extensions"]
max_rows = 1000
[db]
port = 54322
shadow_port = 54320
major_version = 17
[db.pooler]
enabled = false
port = 54329
pool_mode = "transaction"
default_pool_size = 20
max_client_conn = 100
[studio]
enabled = false
port = 54323
[auth]
enabled = false
[storage]
enabled = false