4.Node.js/Prisma(ORM)

[Prisma7] 1편. 개발환경 구축과 프로젝트 초기화 (Node.js + Prisma 7)

쿼드큐브 2026. 1. 12. 17:39
반응형
반응형

 

1편. Prisma 개발환경 구축과 프로젝트 초기화 (Node.js + Prisma 7)

 

📚 목차
1. 개발환경 구축 및 프로젝트 초기화
2. DB 연동 및 스키마 설계 (PostgreSQL)
3. Prisma 설정 및 싱글톤 클라이언트 구현
4. Prisma CLI 주요 명령어 요약

 

📂 [GitHub 예시 코드 보러가기] (https://github.com/cericube/nodejs-tutorials) /Prisma

 

1. 개발환경 구축 및 프로젝트 초기화

Prisma는 Node.js에서 데이터베이스를 다룰 때, SQL을 직접 작성하는 방식과 비교하여 다음을 제공하는 ORM 계열 도구입니다.

 

✔️ 개발환경 구축하기

Node.js 개발환경 구축하기 : JavaScript, TypeScript, Vitest

 

Node.js 개발환경 구축하기 : JavaScript, TypeScript, Vitest

1. Visual Studio Code 설치Visual Studio Code 설치 방법 (Installer / Zip 포터블 모드) Visual Studio Code 설치 방법 (Installer / Zip 포터블 모드)Windows에서 Visual Studio Code(이하 VSCode)를 설치하는 방법에는 설치형(Instal

quadcube.tistory.com

 

✔️ Prisma VS Code 확장설치

Prisma 확장 설치
Prisma 확장 설치

Prisma VS Code Extension은 데이터베이스 관리 UI, 문법 강조, 린팅, 코드 자동 완성, 포맷팅, 정의로 이동(Jump-to-Definition) 등 Prisma 개발에 필요한 다양한 기능을 VS Code 안에서 제공합니다.

 

.vscode/settings.json 설정 예시

{
  "editor.formatOnSave": true,
  "[prisma]": {
    "editor.defaultFormatter": "Prisma.prisma"
  },
  
  //이 확장은 사용 현황을 개선하기 위해 텔레메트리 데이터를 수집합니다.
  //텔레메트리 비활성화 방법
  "telemetry.telemetryLevel": "off"
}

 

🔷 Prisma 7.x 특징

https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7

구분 v6이하 v7.x
설치 편의성 매우 쉬움 (Prisma가 대부분 자동 처리) 다소 번거로움 (DB 드라이버 직접 설치 필요)
엔진 무게 무거움 (수십 MB 규모의 Rust 바이너리) 매우 가벼움 (Pure JS / WASM 기반)
통신 구조 JS ↔ Rust Engine ↔ DB JS ↔ JS(pg 등 드라이버) ↔ DB
성능 (응답 속도) 보통 (직렬화·IPC 오버헤드 존재) 매우 빠름 (중간 계층 제거로 오버헤드 최소화)
유연성 낮음 (Prisma Query Engine에 강하게 종속) 높음 (기존 pg 설정, 커넥션 옵션 그대로 활용 가능)

1. 과거(v6 이하)

Prisma는 모든 것을 내부의 거대한 Rust Query Engine이 처리하는 블랙박스 구조로, 설치는 간단했지만 엔진이 무겁고 JS–Rust 간 통신 오버헤드로 인해 특히 서버리스 환경에서 성능 한계가 있었습니다.

 

2. 현재(v7.x)
Prisma는 가벼운 JavaScript/WASM 엔진이 쿼리 해석만 담당하고, 실제 DB 통신은 pg 같은 외부 드라이버에 위임하는 구조로 전환되어, 번들 크기와 오버헤드를 줄이고 성능과 유연성을 크게 개선했습니다.

 

🔷 실습 폴더 구조

실습의 효율성을 위해 Prisma 전용 설정을 한곳에 모으고, 각 챕터에서 이를 참조하는 구조를 사용합니다.

nodejs-tutorials/
├── node_modules/
├── prisma/
│   ├── prisma/
│   │   ├── schema.prisma   <-- DB 설계도
│   │   └── migrations/     <-- DB 스키마 변경 이력(마이그레이션 파일/SQL)
│   ├── src/
│   │     ├── generated/       <-- Prisma Client 생성물(output 대상)
│   │     ├── shared/          
│   │     │   └── database.ts  <-- ../generated/client를 임포트
│   │     └── ch01 
│   ├── .env                <-- Prisma가 참조하는 환경변수
│   ├── prisma.config.ts    <-- Prisma CLI 설정(프로젝트 단위)
│   ├── package.json        <-- 서브프로젝트 의존성 관리
│   ├── tsconfig.json       <-- 서브프로젝트 컴파일 설정 관리
├── package.json            <-- 루트 프로젝트 의존성 관리
├── tsconfig.json           <-- 루트 프로젝트 컴파일 설정 관리
├── eslint.config.mjs
└── .prettierrc

 

🔷 의존성 설치 및 초기화

# 프로젝트 폴더 이동
cd nodejs-tutorials/prisma

# 1. prisma 서브 프로젝트 생성
npm init -y

# 2. 필수 패키지 설치(TypeScript 사용, tsx 사용)
npm i -D prisma@7.1.0
npm i @prisma/client@7.1.0

# 3. PostgreSQL용 드라이버와 Prisma 어댑터 설치
npm i pg @prisma/adapter-pg
npm i -D @types/pg

▸ prisma : Prisma의 명령줄 도구(CLI) 패키지입니다.

npx prisma init
npx prisma generate
npx prisma migrate dev
npx prisma db push
npx prisma studio

▸ @prisma/client : 실제 애플리케이션 코드에서 import해서 사용하는 ORM 라이브러리입니다.

▸ pg (node-postgres) : Node.js 생태계에서 가장 검증된 PostgreSQL 통신 라이브러리입니다

▸ @prisma/adapter-pg : Prisma 전용 어댑터(브릿지)

▸ @types/pg : TypeScript 타입 정의

PS D:\NodejsDevelope\workspace\nodejs-tutorials\prisma> npm list
nodejs-tutorials@1.0.0 D:\NodejsDevelope\workspace\nodejs-tutorials
└─┬ prisma-study@1.0.0 -> .\prisma
  ├── @prisma/adapter-pg@7.2.0
  ├── @prisma/client@7.2.0
  ├── @types/pg@8.16.0
  ├── pg@8.16.3
  └── prisma@7.2.0

 

🔷Prisma 초기화(npx prisma init)

cd nodejs-tutorials/prisma
npx prisma init

실행 후 생성되는 파일은 일반적으로 다음과 같습니다.

▸ schema.prisma : 모델/데이터소스/클라이언트 생성 설정

▸ .env : .env: 프로젝트의 데이터베이스 접속 정보를 보관합니다.
환경 변수 자동 로드를 위해 반드시 프로젝트 최상위 디렉터리(Root)에 위치해야 합니다.

▸ prisma.config.ts : Prisma CLI 설정(프로젝트 단위)

npx prisma init 결과 예시
npx prisma init 결과 예시

 

2. DB 연동 및 스키마 설계 (PostgreSQL)

🔷 .env에 PostgreSQL 연결 문자열 설정

Prisma/.env에 DATABASE_URL을 설정합니다.

DATABASE_URL="postgresql://USER:PASSWORD@localhost:5432/prisma_tutorial?schema=study"

USER/PASSWORD는 본인의 로컬 PostgreSQL 계정에 맞게 변경합니다.
실습 환경에서는 데이터베이스는 prisma_tutorial, PostgreSQL 스키마는 cube를 기준으로 구성합니다.

 

🔷 prisma.config.ts 설정 이해

prisma.config.ts는 Prisma CLI 설정을 코드로 고정해 두는 용도입니다.

// Node.js에서 .env 파일을 읽어 process.env에 주입해 주는 코드입니다.
import "dotenv/config";
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  // Prisma CLI가 사용할 스키마 파일의 경로입니다.
  schema: "prisma/schema.prisma",
  // 마이그레이션 파일들이 저장될 경로를 지정합니다.
  // migrate dev 실행 시 생성되는 SQL/메타 파일들이 이 폴더에 쌓입니다.
  migrations: {
    path: "prisma/migrations",
  },
  // Prisma CLI가 DB에 연결할 때 사용할 연결 문자열을 어디서 가져올지입니다.
  datasource: {
    url: env("DATABASE_URL"),
  },
});

 

🔷 schema.prisma의 핵심 블록 이해

schema.prisma는 크게 3가지 블록으로 구성됩니다.

 

1. generator client : Prisma Client 생성 설정'

▸ 애플리케이션 코드에서 사용할 Prisma Client(= 타입 안전한 DB 접근 코드)를 “어떤 방식으로, 어디에 생성할지” 정합니다.

▸ Prisma는 schema.prisma를 보고 prisma.user.findMany() 같은 API를 자동으로 만들고, 그 결과물을 Prisma Client로 제공합니다.

generator client {
  provider        = "prisma-client" // Prisma 7부터는 -js를 뺍니다.
  output          = "../src/generated" // output 설정이 필수가 되었습니다.
}
prisma-client-js prisma-client
new PrismaClient() (매개변수 없음) new PrismaClient({ adapter }) (어댑터 필수)
Prisma 내장 엔진 (Rust 기반 Query Engine) 외부 JavaScript DB 드라이버 (pg, mysql2 등)
코드가 간결하고 설정·관리가 편리함 서버 실행 속도가 빠르고 메모리 사용량이 적음
바이너리 크기가 크고 서버리스 환경에서 초기 구동이 느림 초기 설정 코드가 다소 복잡함

 

2. datasource db : DB 종류/연결 정보

▸ Prisma가 어떤 데이터베이스(postgresql, mysql, sqlite 등)에 연결할지를 정의합니다.

datasource db {
  provider = "postgresql"
}

 

3. model ... : “DB 설계도” (테이블/관계/인덱스 정의)

▸ DB의 테이블 구조를 Prisma DSL로 선언하는 부분입니다.

model User {
  id          Int        @id @default(autoincrement())
  email       String     @unique
  displayName String?
  createdAt   DateTime   @default(now())
  deletedAt   DateTime?  // Soft Delete용
  profile     Profile?
  posts       Post[]
  likes       PostLike[]
}

 

🔷 실습용 스키마 적용

실습용 ERD
실습용 ERD

model User {
  id          Int        @id @default(autoincrement())
  createdAt   DateTime   @default(now()) @map("created_at")
  updatedAt   DateTime   @updatedAt @map("updated_at")
  deletedAt   DateTime?  @map("deleted_at")
  email       String     @unique
  displayName String?    @map("display_name")
  // Relations
  profile     Profile?
  posts       Post[]
  comments    Comment[]
  likes       PostLike[]

  @@index([deletedAt])
  @@map("users")
}

model Profile {
  id        Int       @id @default(autoincrement())
  createdAt DateTime  @default(now()) @map("created_at")
  updatedAt DateTime  @updatedAt @map("updated_at")
  deletedAt DateTime? @map("deleted_at")
  bio       String?
  // Relations (1:1)
  userId    Int       @unique @map("user_id")
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([deletedAt])
  @@map("profiles")
}

model Post {
  id        Int       @id @default(autoincrement())
  createdAt DateTime  @default(now()) @map("created_at")
  updatedAt DateTime  @updatedAt @map("updated_at")
  deletedAt DateTime? @map("deleted_at")
  title     String
  content   String?
  published Boolean   @default(false)

  // Relations (N:1)
  authorId Int  @map("author_id")
  author   User @relation(fields: [authorId], references: [id], onDelete: Restrict)

  comments Comment[]
  likes    PostLike[]

  @@index([authorId, createdAt])
  @@index([published, createdAt])
  @@index([deletedAt])
  @@map("posts")
}

model Comment {
  id        Int       @id @default(autoincrement())
  createdAt DateTime  @default(now()) @map("created_at")
  updatedAt DateTime  @updatedAt @map("updated_at")
  deletedAt DateTime? @map("deleted_at")
  content   String

  // Relations
  postId Int  @map("post_id")
  post   Post @relation(fields: [postId], references: [id], onDelete: Cascade)

  authorId Int  @map("author_id")
  author   User @relation(fields: [authorId], references: [id], onDelete: Restrict)

  @@index([postId, createdAt])
  @@index([authorId, createdAt])
  @@index([deletedAt])
  @@map("comments")
}

model PostLike {
  // Composite key (explicit M:N)
  userId Int @map("user_id")
  postId Int @map("post_id")

  // Common-ish columns
  createdAt DateTime  @default(now()) @map("created_at")
  updatedAt DateTime  @updatedAt @map("updated_at")
  deletedAt DateTime? @map("deleted_at")

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  post Post @relation(fields: [postId], references: [id], onDelete: Cascade)

  @@id([userId, postId])
  @@index([postId, createdAt])
  @@index([deletedAt])
  @@map("post_likes")
}

반응형

 

3. Prisma 설정 및 싱글톤 클라이언트 구현

🔷 DB 스키마 반영 : npx prisma migrate dev

마이그레이션 파일을 생성하며 DB 변경 이력을 남깁니다.

cd nodejs-tutorials/prisma
npx prisma migrate dev --name init
# or npx prisma migrate dev --name init --schema ./prisma/schema.prisma

▸DB와 schema는 미리 생성하는 것이 안전합니다.

 

✔️ npx prisma db push

- 마이그레이션 히스토리를 남기지 않고, 현재 스키마 상태를 DB에 즉시 동기화합니다.

- 빠르게 실습 진입 가능
- 운영/팀 협업에는 부적합(히스토리 추적이 약함)

cd nodejs-tutorials/prisma
npx prisma db push
# or npx prisma db push --schema ./prisma/schema.prisma

 

🔷 Prisma Client 생성: npx prisma generate

Prisma Client는 schema.prisma를 기반으로 타입이 포함된 클라이언트 코드를 생성합니다.

cd nodejs-tutorials/prisma
npx prisma generate
# or npx prisma generate --schema ./prisma/schema.prisma

 

 

npx prisma generate 결과 화면
npx prisma generate 결과 화면

 

🔷 싱글톤 Prisma Client 예시 : shared/database.ts

챕터별로 실습 파일이 나뉘어도 DB 연결은 하나의 공통 유틸로 관리하는 편이 안정적입니다.
아래는 Prisma/shared/database.ts 예시입니다.

▸ Prisma CLI의 특징: npx prisma 명령어는 기본적으로 실행 위치의 ./prisma/ 폴더나 ./ 폴더에서 .env를 자동으로 탐색합니다.
▸ Node.js의 특징: tsx나 ts-node로 코드를 실행할 때, 환경 변수 엔진은 기본적으로 프로세스가 시작된 최상위 폴더(Root)에서만 .env를 찾습니다.

import { Pool } from 'pg';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '../generated/client';
import * as dotenv from 'dotenv';
import * as path from 'path';

// 1. 환경 변수 로드 (Prisma 폴더 내 .env 우선 참조)
dotenv.config({ path: path.resolve(__dirname, '../.env') });

const isDev = process.env.NODE_ENV === 'development';

// 2. Global 타입 선언 (HMR 시 인스턴스 중복 생성 방지)
declare global {
  var prisma: PrismaClient | undefined;
}

// 3. 인스턴스 생성 함수
function getPrismaClient() {
  const connectionString = process.env.DATABASE_URL;
  
  // PostgreSQL Pool 설정
  const pool = new Pool({ connectionString });
  const adapter = new PrismaPg(pool, { schema: 'study' });

  // 클라이언트 생성 (개발 환경에서만 쿼리 로그 출력)
  return new PrismaClient({
    adapter,
    log: isDev ? ['query', 'info', 'warn', 'error'] : ['error'],
  });
}

// 4. 싱글톤 클라이언트 수출 (이미 있으면 재사용, 없으면 생성)
export const prisma = global.prisma || getPrismaClient();

// 개발 환경일 경우 global 객체에 할당하여 캐싱
if (isDev) global.prisma = prisma;

/**
 * 5. 프로세스 종료 시 연결 해제 (Graceful Shutdown)
 */
const shutdown = async () => {
  await prisma.$disconnect();
  process.exit(0);
};

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

 

✔️ dotenv의 기본 탐색 메커니즘

▸ 일반적으로 dotenv.config()를 인자 없이 호출하면, Node.js는 현재 명령어를 실행 중인 위치(.) 에서 .env 파일을 찾습니다.

▸ 만약 루트(nodejs-tutorials/)에서 npx ts-node Prisma/ch01/index.ts를 실행하면, dotenv는 nodejs-tutorials/.env를 찾으려 합니다.
▸ 하지만 실제 파일은 nodejs-tutorials/Prisma/.env에 있으므로, 아무 설정을 하지 않으면 환경 변수를 불러오지 못해 DB 연결 에러가 발생합니다.

▸ 결과적으로 path.resolve(__dirname, '../.env')를 사용하면, 서버를 어디서 실행하든 항상 Prisma/.env를 절대 경로로 추적하게 되어 설정 오류를 방지할 수 있습니다.

 

 

🔷 첫 실행 스크립트 (prisma/src/ch01/index.ts)

prisma/src/ch01/index.ts를 만들고 아래 코드를 넣습니다.

// Prisma/ch01/index.ts
import { prisma } from '../shared/database';

async function main() {
  console.log('데이터 생성을 시작합니다...');

  // 1. 데이터 생성 (Create)
  const created = await prisma.user.create({
    data: {
      // @unique 제약조건 회피를 위해 타임스탬프 활용
      email: `dev${Date.now()}@example.com`,
      displayName: 'Prisma Dev',

      // 2. 관계 생성 (Nested Write)
      // User와 Profile을 하나의 트랜잭션으로 묶어 동시에 생성
      profile: {
        create: { bio: '안녕하세요. Prisma 학습용 프로필입니다.' },
      },
    },
    // 3. 관계 조회 (Include)
    // 반환 객체에 연관된 profile 데이터까지 포함시켜 출력
    include: { profile: true },
  });

  // 4. 결과 확인
  console.log('1) Created user:', created);
}

main()
  .catch((e) => {
    // 에러 발생 시 로그 출력
    console.error(e);
  })
  .finally(async () => {
    // DB 연결 종료 (프로세스 종료 전 필수)
    await prisma.$disconnect();
  });
# cd ./prisma
# 실행은 서브프로젝트 root에서 한다
npx tsx --env-file=./.env ./src/ch01/index.ts

▸ .env는 project root에 존재해야 하나 ./Prisma/.env에 위치하고 있어 .env위치를 지정해서 실행해야 합니다.

실행 예시 화면
실행 예시 화면
실행 로그화면 예시
실행 로그화면 예시

 

4. Prisma CLI 주요 명령어 요약

명령어 역할 사용 시점
prisma init 프로젝트 구조 생성 Prisma 최초 도입 시
format / validate 스키마 정렬·검증 저장 전, CI 단계
migrate dev 로컬 DB 반영 모델 수정 후
migrate deploy 운영 DB 반영 CD 파이프라인
db push 즉시 반영 프로토타입, MongoDB
generate Client 생성 스키마 변경 후
studio GUI 관리 데이터 확인 시
db pull DB → 스키마 기존 DB 연동 시

 

🔷 프로젝트 초기 설정 (Setup)

- Prisma를 프로젝트에 처음 도입할 때 사용하는 단계

 

npx prisma init

▸ Prisma 사용을 시작하기 위한 최초 명령어

▸ prisma/schema.prisma : 모델과 DB 설정을 정의하는 핵심 파일
▸ .env : DB 연결 문자열 등 환경 변수 관리

npx prisma init
npx prisma init --datasource-provider postgresql

 

🔷 스키마 관리 및 검증 (Schema & Validation)

- 모델을 설계하면서 스키마의 품질과 안정성을 유지하는 단계

 

npx prisma format

▸ schema.prisma 파일을 자동 정렬
▸ 관계(Relation) 정의가 불완전한 경우 일부 필드를 자동 보완

 

npx prisma validate : CI/CD 파이프라인에서 반드시 권장

▸ 스키마 문법 오류, 관계 설정의 논리적 문제 (예: 참조 불일치)를 사전에 검사합니다.

 

🔷 데이터베이스 동기화 (Migration & Sync)

- Prisma 스키마와 실제 DB 상태를 맞추는 핵심 단계

 

npx prisma migrate dev (개발용)

▸ 스키마 변경 사항을 기반으로 SQL 마이그레이션 파일 생성, 로컬 DB에 즉시 반영
▸ Prisma Client 자동 재생성

npx prisma migrate dev
npx prisma migrate dev --name add_user_table

 

npx prisma migrate deploy (운영/배포용) : CI/CD 환경에서 사용

▸ 이미 생성된 마이그레이션 파일을 운영 DB에 순차적으로 적용

 

npx prisma db push
▸ 마이그레이션 파일을 남기지 않고 스키마를 DB에 즉시 반영

▸ 빠른 프로토타이핑
▸ MongoDB 사용 시 (마이그레이션 개념이 없음)

 

npx prisma db pull
▸ 이미 존재하는 DB 구조를 읽어서 schema.prisma 파일로 변환 (역공학)

▸ 기존 레거시 DB를 Prisma로 가져올 때 매우 유용합니다.

 

🔷 클라이언트 및 데이터 조작 (Client & Data)

- 코드에서 Prisma를 사용하기 위한 준비 및 관리 단계

 

npx prisma generate

Prisma Client 생성
TypeScript 타입 정의 포함

보통 migrate dev 시 자동 실행되지만, 수동 실행이 필요할 수 있습니다.

 

npx prisma studio

▸ 브라우저 기반 DB GUI 도구
▸ 데이터 조회, 수정, 삭제 가능

▸ 기본 접속 주소: http://localhost:5555

 

npx prisma db seed

▸ 초기 데이터(Seed) 스크립트 실행
▸ package.json에 정의된 seed 명령어를 기준으로 동작

#1. 구조 예시
project-root/
├─ prisma/
│  ├─ schema.prisma
│  └─ seed.ts

#2. package.json 설정 예시
{
  "prisma": {
    "seed": "tsx prisma/seed.ts"
  }
}

 


※ 게시된 글 및 이미지 중 일부는 AI 도구의 도움을 받아 생성되거나 다듬어졌습니다.

반응형

 

반응형