개인 프로젝트에 DB가 필요해졌다. 선택지를 몇 개 놓고 고민했다.
Supabase는 많이 쓰는데 무료 플랜이 2주 이상 비활성이면 인스턴스를 멈춰버린다. 개인 사이드 프로젝트 특성상 트래픽이 일정하지 않으니까 좀 불안했다. PlanetScale은 MySQL 계열이고, 얼마 전에 무료 플랜을 없앴다. Neon은 PostgreSQL 서버리스인데 콜드 스타트가 있다.
Railway는 사용한 만큼만 과금하는 구조인데, PostgreSQL 하나 돌리는 비용이 한 달에 몇 달러 수준이라서 개인 프로젝트용으로 부담이 없었다. 거기다 UI가 깔끔하고, 설정이 별로 없다는 게 마음에 들었다.
DB 생성
Railway 대시보드에서 New Project → Database → PostgreSQL 선택하면 1분도 안 걸려서 인스턴스가 올라온다. 별도의 설정 없이 바로 쓸 수 있는 상태가 된다.
생성하고 나면 Variables 탭에서 접속 정보가 다 나온다.
접속 URL 두 개를 구분해야 한다
Railway PostgreSQL에서 처음에 헷갈렸던 부분이 이거다. 접속 URL이 두 종류다.
- DATABASE_URL —
postgres.railway.internal도메인. Railway 내부 네트워크에서만 접근 가능. - DATABASE_PUBLIC_URL —
turntable.proxy.rlwy.net같은 도메인. 외부에서 접근 가능.
Vercel에 배포된 앱은 Railway 내부망이 아니니까, 프로덕션에서는 DATABASE_PUBLIC_URL을 써야 한다. 로컬 개발할 때도 마찬가지.
근데 Railway 내부에서 돌아가는 앱이 있다면 내부 URL을 쓰는 게 빠르고 보안상 낫다. 내가 쓴 경우엔 Vercel + Railway 조합이라 Public URL만 씀.
Variables 탭에서 DATABASE_PUBLIC_URL 옆에 느낌표 아이콘이 있는데, 클릭하면 전체 URL을 복사할 수 있다. 처음엔 이걸 못 찾고 헤맸다.
Prisma 연결
ORM은 Prisma를 썼다. schema.prisma의 datasource 설정과 .env 파일에 URL 넣는 게 기본 흐름이다.
// .env
DATABASE_URL="postgresql://postgres:비밀번호@turntable.proxy.rlwy.net:포트/railway"
Prisma 7 버전부터는 드라이버 어댑터 구조로 바뀌어서 설정 방식이 좀 달라졌다. 기존처럼 datasourceUrl을 생성자에 바로 넣는 게 안 된다.
// lib/prisma.ts
import { PrismaClient } from './generated/prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
function createPrismaClient() {
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
return new PrismaClient({ adapter });
}
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
@prisma/adapter-pg를 별도로 설치해야 한다.
npm install @prisma/adapter-pg
Vercel 배포 시 주의사항
Prisma는 prisma generate를 실행해야 클라이언트 코드가 생성된다. 로컬에서는 한 번 실행해두면 되는데, Vercel 배포 환경에서는 매번 새로 빌드하니까 빌드 전에 generate를 실행해줘야 한다.
package.json의 build 스크립트에 추가하거나, 별도 빌드 스크립트를 만들어서 처리한다.
# scripts/build.sh
npx prisma generate
next build
이걸 안 해두면 Cannot find module './generated/prisma/client' 에러로 배포가 터진다. lib/generated/ 디렉토리가 .gitignore에 들어있으니 당연한 결과인데, 처음엔 왜 로컬에선 되는데 배포에서 안 되는지 바로 안 보여서 좀 걸렸다.
로컬 개발 환경 설정
prisma.config.ts를 쓰는 경우 Prisma CLI가 .env.local을 읽지 않는다는 것도 알아둬야 한다. dotenv/config는 기본적으로 .env를 읽으니까, .env에도 DATABASE_URL을 넣어줘야 prisma migrate dev 같은 명령어가 제대로 동작한다.
.env는 로컬 개발 전용 접속 정보, .env.local은 Next.js 앱에서 쓰는 환경변수 — 이렇게 역할을 분리해서 관리하면 혼선이 없다.
데이터 확인
Railway 대시보드에서 Data 탭 들어가면 테이블과 레코드를 GUI로 볼 수 있다. psql 접속이나 별도 클라이언트 없이도 간단한 확인은 여기서 된다.
Prisma Studio를 쓰는 것도 편하다.
npx prisma studio
로컬에서 localhost:5555로 열리는 GUI인데, 개발 중에 데이터 확인하거나 직접 레코드 추가할 때 유용하다.
설정이 생각보다 단순해서 처음 세팅에 30분도 안 걸렸다. 주의할 점은 internal URL vs public URL 구분, 그리고 Vercel 배포 시 prisma generate 빠뜨리지 않는 것 정도다.