VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
데이터베이스 만들기
Prisma에는 두 개의 데이터베이스가 필요합니다: 앱이 사용하는 데이터베이스와, 마이그레이션을 계산하기 위해 내부적으로 사용하는 두 번째 “섀도” 데이터베이스입니다. 둘 다 만들고, 각각에 권한을 가진 전용 사용자를 만드세요:*.*에 더 넓은 CREATE/DROP 권한을 부여하는 대신, 미리 만들어 두고 앱 사용자에게 그 데이터베이스 하나에 대한 권한만 부여하면, 비밀번호가 유출되었을 때의 피해 범위가 서버 전체가 아니라 데이터베이스 두 개로 유지됩니다.
앱 스캐폴딩
prisma latest 태그는 8.0 릴리스 후보를 가리킵니다: 시험해 보기에는 괜찮지만, 계속 동작해야 하는 가이드에는 잘못된 기본값입니다.
Prisma 초기화
prisma init은 기본적으로 Claude Code, Cursor, Windsurf 에이전트 스킬을 저장소에 스캐폴딩하는 설치 후 단계를 실행합니다(.claude/, .windsurf/, .agents/와 skills-lock.json). --no-skills는 이를 건너뜁니다. Prisma와 함께 AI 코딩 에이전트를 사용한다면 정말 유용한 기능이지만, 이 가이드의 범위에는 들어가지 않습니다.
이 명령은 prisma/schema.prisma, .env 파일, 그리고 prisma7.config.ts(Prisma 7에서는 설정 파일 이름에 버전 접미사가 붙습니다)를 만듭니다. 연결 문자열을 설정하세요:
.env
schema.prisma에 직접 넣었습니다. Prisma 7부터 섀도 데이터베이스 URL을 포함한 연결 URL은 스키마 파일이 아니라 prisma7.config.ts에 있습니다:
prisma7.config.ts
schema.prisma는 prisma init이 만든 generator 블록을 유지하고, 이제 데이터 소스에는 연결 URL 없이 provider만 선언합니다:
prisma/schema.prisma
마이그레이션 실행
prisma/migrations/<timestamp>_init/migration.sql을 생성하고, prisma generate를 대신 호출합니다. 테이블이 존재하는지 확인하세요:
드라이버 어댑터로 연결
Prisma 7의 생성된 클라이언트는 더 이상 스스로 연결하지 않습니다: 인수 없는new PrismaClient()는 이제 타입 검사에 실패합니다. 데이터베이스마다 하나씩 있는 드라이버 어댑터 패키지를 전달합니다:
@prisma/adapter-mariadb는 MariaDB와 MySQL 모두를 위한 어댑터입니다: 와이어 프로토콜이 공유되므로, 일반 MySQL에서 동작하는 것과 같은 방식으로 VillageSQL에서도 동작합니다. Next.js의 개발 모드 핫 리로드가 파일을 저장할 때마다 새 연결 풀을 열지 않도록 싱글턴을 설정하세요:
src/lib/prisma.ts
새 계정의 첫 연결
MySQL 8과 VillageSQL은caching_sha2_password 플러그인으로 계정을 만듭니다. 특정 계정이 처음 인증할 때 클라이언트는 RSA 키 교환을 완료해야 합니다. 그 뒤로는 서버가 결과를 캐시하고, 이후 연결은 그 단계를 건너뛰는 더 빠른 경로를 탑니다. @prisma/adapter-mariadb는 암호화되지 않은 연결에서 그 교환을 거부하므로, 이 계정을 한 번도 인증한 적이 없는 서버에서는 빌드가 실패합니다:
active=0 idle=0은 서버에 연결할 수 없는 것처럼 읽히게 만듭니다. 기저의 드라이버 오류가 더 구체적입니다:
.env
asyncmy에 대해 문서화한 것과 같은 함정이며, 같은 이유로 간헐적입니다: mysql CLI를 포함해 어떤 클라이언트든 그 계정의 전체 인증을 한 번 완료하면, FLUSH PRIVILEGES나 서버 재시작으로 캐시가 비워질 때까지 캐시된 결과가 문제를 숨깁니다.
생성된 클라이언트의 임포트 경로에는 두 가지 특이점이 있습니다. index.ts가 없으므로 @/generated/prisma(디렉터리)가 아니라 @/generated/prisma/client(파일)를 임포트합니다. 그리고 그 파일의 모든 내보내기는 @ts-nocheck로 지정되어 있습니다. 읽으라고 있는 것이 아니라 임포트하라고 있는 생성 코드이기 때문입니다.
Server Component에서 읽기와 쓰기
src/app/page.tsx
prisma.post.findMany()는 Server Component로 실행되므로, 목록은 클라이언트 측 페치 없이 데이터가 이미 HTML에 담긴 채로 렌더링됩니다. 폼은 Server Action("use server")으로 전송되고, 이 액션은 같은 Prisma 클라이언트를 통해 쓰기를 수행한 뒤 revalidatePath("/")를 호출해 클라이언트 측 새로 고침 없이 새 게시물이 나타나게 합니다.
이 시점에서 npm run build를 실행하면 실제로 데이터베이스에 쿼리합니다: 홈 페이지가 동적 API를 사용하지 않으므로 Next.js는 빌드 시점에 정적 콘텐츠로 사전 렌더링하고, 이는 findMany()가 요청 시점뿐 아니라 빌드 중에도 실행된다는 뜻입니다. npm run build 중에 데이터베이스에 연결할 수 없으면 빌드는 거기서 실패하므로, 이 앱의 CI 파이프라인은 런타임뿐 아니라 빌드 시점에도 연결 가능한 데이터베이스가 필요합니다.
자주 묻는 질문
프로덕션에도 섀도 데이터베이스가 필요한가요?
아니요.prisma migrate dev는 개발 중에 스키마 드리프트를 감지하기 위해 섀도 데이터베이스를 사용합니다. 프로덕션에서는 prisma migrate deploy를 사용하세요. 이 명령은 섀도 데이터베이스를 전혀 건드리지 않고 기존 마이그레이션 파일을 적용합니다.
독립 스크립트가 왜 “Cannot read properties of undefined (reading ‘prepareCacheLength’)“로 실패하나요?
그 스크립트의 환경에서process.env.DATABASE_URL이 undefined입니다. Next.js는 next dev와 next build를 위해 .env를 자동으로 로드하지만, 일반 node나 tsx 스크립트는 그렇지 않습니다. 스크립트 맨 위에 import "dotenv/config"를 추가하세요.
@prisma/adapter-pg의 MySQL 대응물을 대신 쓸 수 있나요?
MySQL 전용 어댑터 이름은 없습니다. @prisma/adapter-mariadb가 Prisma가 MariaDB와 MySQL 모두를 위해 제공하는 어댑터이며, VillageSQL도 포함합니다.
문제 해결
함께 보기
- Next.js와 Drizzle로 MySQL 사용하기 — Prisma 대신 더 가볍고 SQL 우선인 ORM 위에 만든 같은 앱
- Next.js로 MySQL 사용하기 — ORM 없이 연결 설정, Server Action, Route Handler 패턴
- ORM으로 MySQL 사용하기 — Prisma뿐 아니라 모든 ORM에서 중요한 설정
- 연결 풀링 — 드라이버 어댑터의 풀 크기가 프로세스 수와 어떻게 상호작용하는지