들어가며

이전 포스팅에서 프로젝트 환경 셋업을 진행한데 이어, 데이터베이스 연동을 해보려고한다.
스프링에서는 jpa와 hibernate를 사용해서 엔티티 관리가 비교적 편했는데, node 생태계에서는 어떤 ORM이 좋을지 고민이 많았다. TypeORM도 고려했지만 이전에 잠깐이나마 회사에서 prisma를 사용해보기도 했고, 타입 안정성과 직관적인 스키마 작성 방식이 마음에 들어서 prisma로 결정했다.

이번 글에서는 prisma를 처음 설정하면서 겪었던 시행착오와 해결 과정을 공유하려고 한다.


Prisma 초기 설정

패키지 설치

Prisma를 사용하려면 두 가지 패키지가 필요하다.

npm install prisma @prisma/client
  • prisma: CLI 도구 및 스키마 관리
  • @prisma/client: 실제 쿼리를 실행하는 클라이언트

Prisma 초기화

설치가 끝나면 초기화 명령어를 실행한다.


npx prisma init

이 명령어를 실행하면 두 가지가 생성된다:

  1. prisma/schema.prisma - 데이터베이스 스키마 정의 파일

  2. .env - 환경변수 파일 (DATABASE_URL 포함)

추가로 prisma.config.ts 파일도 생성되었다.

스키마 작성

기존 스프링의 Plan 엔티티를 prisma 스키마로 변환했다. jpa 어노테이션과 prisma 문법이 꽤 유사해서 생각보다 어렵지 않았다. 전체적으로 데이터소스 연동과 모델 정의하는 부분은 prisma 공식 문서에서 도움을 많이 받았다.

model Plan {
	id String @id @default(uuid()) @db.Char(36)
	title String @db.VarChar(50)
	startDate DateTime @db.Date
	endDate DateTime @db.Date
	region String @db.VarChar(255)
	visible Boolean @default(false)
	copyAllowed Boolean @default(false)
	bookmarkCount Int @default(0) @db.Int
	likeCount Int @default(0) @db.Int
	testerCount Int @default(0) @db.Int
	createdAt DateTime @default(now())

@@map("plans")
}

유의할 점은 아래와 같다.

  • UUID를 MySQL에서 사용하려면 @db.Char(36) 타입 지정이 필요로 한다.

  • @@map("plans")로 실제 테이블 이름을 지정할 수 있다

  • 날짜 필드는 DateTime 타입을 사용하되, @db.Date로 MySQL DATE 타입으로 매핑해야한다.

환경변수 설정에서 만난 첫 번째 문제

.env 파일에 데이터베이스 연결 정보를 입력했다.

DATABASE_URL="mysql://prisma:1234@localhost:3307/ack_existing"

여기서 처음에 실수한 부분이 있었다. prisma 초기화 시 자동 생성된 주석을 보고 prisma+mysql:// 프로토콜을 사용했다가 오류가 발생했다. 알고 보니 이건 Prisma Accelerate라는 별도 서비스를 사용할 때의 URL 형식이었다.

일반 MySQL 연결은 그냥 mysql:// 프로토콜을 사용하면 된다.

마이그레이션 실행에서 만난 두 번째 문제

이제 마이그레이션을 실행하려고 했다.

npx prisma migrate dev --name init

그런데 다음과 같은 에러가 발생했다.


PrismaConfigEnvError: Missing required environment variable: DATABASE_URL

분명 .env 파일에 DATABASE_URL을 설정했는데 왜 못 찾는 걸까? 헤매다가 .env 파일 상단의 주석을 자세히 읽어보니 답이 있었다.


# Environment variables declared in this file are NOT automatically loaded by Prisma.

# Please add `import "dotenv/config";` to your `prisma.config.ts` file

prisma가 자동으로 .env 파일을 로드하지 않는다는 것이었다. prisma.config.ts 파일에서 직접 환경변수를 로드해야 했다.

해결 방법

prisma.config.ts 파일을 열어서 맨 위에 한 줄을 추가했다.

import "dotenv/config"; // 임포트 추가
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  engine: "classic",
  datasource: {
    url: env("DATABASE_URL"),
  },
});

dotenv 패키지는 이미 설치되어 있었다.

이제 다시 마이그레이션 명령어를 실행하니 다음과 같은 출력이 나오며 성공하였다.


Environment variables loaded from .env

Prisma schema loaded from prisma/schema.prisma

Datasource "db": MySQL database "ack_existing" at "localhost:3307"

  

Applying migration `20251111075715_init`

  

The following migration(s) have been created and applied from new schema changes:

  

migrations/

└─ 20251111075715_init/

└─ migration.sql

  

Your database is now in sync with your schema.

  

 Generated Prisma Client


디비버 확인결과, 테이블이 정상적으로 생성되어 있었다.

Spring JPA vs Prisma 비교

스프링 JPA에서 prisma로 마이그레이션하면서 느낀 차이점을 간단히 정리해보면

JPA/Hibernate


@Entity
public class Plan {
	@Id
	@GeneratedValue(strategy = GenerationType.UUID)
	private UUID id;
	
	@Column(length = 50, nullable = false)
	private String title;
}

Prisma


model Plan {
	id String @id @default(uuid()) @db.Char(36)
	title String @db.VarChar(50)
}

개인적으로 prisma 문법이 조금 더 직관적이고 간결하다고 느꼈다. 스키마 파일만 봐도 테이블 구조가 한눈에 들어오는 게 좋은 것 같다. 하지만, 스키마 구조가 변경될 때마다 generate 명령어를 입력해주어야 한다는게 좀 불편한 것 같다.

마치며

다음 글에서는 fastify와 prisma를 활용하여 본격적인 코드 마이그레이션을 진행하려고한다.
엔티티, DTO 스키마, 데이터베이스 영속화(Repository), 서비스 로직, Route 등의 구현과정을 작성하겠다.