RDBMS/Prisma

Prisma ORM 설정 가이드 (TypeScript 환경)

James Wetzel 2025. 5. 21. 17:41

목표: TypeScript 프로젝트에서 Prisma ORM을 사용하여 데이터베이스와 상호작용할 수 있도록 설정합니다.

 

사전 준비:

  • Node.js (LTS 버전 권장) 및 npm/yarn 설치
  • TypeScript 프로젝트 (새로운 프로젝트 생성 또는 기존 프로젝트 사용)
  • 사용할 관계형 데이터베이스 (예: PostgreSQL, MySQL, SQLite)가 설치되어 있거나 접근 가능한 상태여야 합니다.

1단계: Prisma 패키지 설치

먼저 프로젝트에 Prisma CLI 도구와 Prisma Client 라이브러리를 설치합니다. @prisma/client는 데이터베이스와 상호작용하는 데 사용되는 타입 안전한 쿼리 빌더이며, prisma는 CLI 도구입니다.

npm install prisma @prisma/client
# 또는
yarn add prisma @prisma/client

2단계: Prisma 초기화

Prisma CLI를 사용하여 프로젝트에 Prisma를 초기화합니다. 이 명령은 prisma 디렉토리를 생성하고 그 안에 schema.prisma 파일을 만듭니다. 또한, 데이터베이스 연결 정보를 위한 .env 파일을 자동으로 생성하거나 업데이트합니다.

npx prisma init

이 명령을 실행하면 프로젝트 루트에 다음 파일 및 디렉토리가 생성됩니다:

  • prisma/: Prisma 관련 파일들을 저장하는 디렉토리
    • prisma/schema.prisma: Prisma 스키마를 정의하는 핵심 파일
  • .env: 환경 변수를 관리하는 파일 (특히 DATABASE_URL이 추가됩니다)

3단계: .env 파일 설정 (데이터베이스 연결)

.env 파일을 열고 DATABASE_URL 환경 변수를 실제 데이터베이스 연결 문자열로 설정합니다. 이 문자열은 Prisma가 데이터베이스에 연결하는 데 사용됩니다.

예시 (PostgreSQL):

# .env 파일

DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public"

예시 (SQLite):

# .env 파일 (development.db 파일이 프로젝트 루트에 생성됨)

DATABASE_URL="file:./dev.db"

예시 (MySQL):

# .env 파일

DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE"

중요: USER, PASSWORD, HOST, PORT, DATABASE를 사용하려는 데이터베이스의 실제 정보로 교체해야 합니다.


4단계: schema.prisma 파일 정의 (데이터 모델 및 설정)

prisma/schema.prisma 파일을 열고 다음과 같이 설정합니다.

  • datasource: 사용할 데이터베이스의 providerurl을 지정합니다. url.env 파일의 DATABASE_URL을 참조합니다.
  • generator: prisma-client-js를 사용하여 TypeScript를 지원하는 Prisma Client를 생성하도록 설정합니다.
  • model: 애플리케이션에서 사용할 데이터 모델(테이블)을 정의합니다. 각 모델은 데이터베이스 테이블에 매핑됩니다.
// prisma/schema.prisma

// 데이터베이스 설정
datasource db {
  provider = "postgresql" // 사용하는 데이터베이스 종류 (예: "mysql", "sqlite", "sqlserver")
  url      = env("DATABASE_URL") // .env 파일의 DATABASE_URL 환경 변수 참조
}

// Prisma Client 생성기 설정
// TypeScript 환경에서 강력한 타입 지원을 위해 필수
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["driverAdapters"] // 선택 사항: 최신 기능 미리보기 (필요 시 추가)
}

// 예시 데이터 모델: User
// 이 모델은 데이터베이스의 'User' 테이블에 해당합니다.
model User {
  id        String   @id @default(uuid()) // Primary Key, UUID로 자동 생성
  email     String   @unique // 이메일은 중복될 수 없음
  name      String?  // 이름 (선택 사항, nullable)
  age       Int      @default(0) // 기본값 설정
  createdAt DateTime @default(now()) // 생성 시간 (기본값: 현재 타임스탬프)
  updatedAt DateTime @updatedAt // 업데이트 시간 (변경 시 자동 업데이트)

  posts Post[] // User와 Post 간의 1:N 관계 (Post 모델의 author 필드와 연결됨)
}

// 예시 데이터 모델: Post
model Post {
  id        String    @id @default(uuid())
  title     String
  content   String?
  published Boolean   @default(false)
  // User 모델과의 관계 정의
  author    User      @relation(fields: [authorId], references: [id])
  authorId  String // 외래 키 (User 모델의 id 필드와 연결)
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
}

TypeScript 추가 사항:
generator client 블록의 provider = "prisma-client-js" 설정이 TypeScript를 위한 핵심입니다. 이 설정 덕분에 Prisma Client가 생성될 때 모델에 대한 완벽한 TypeScript 타입 정의가 포함되어, 코드 작성 시 강력한 타입 검사 및 자동 완성 기능을 활용할 수 있습니다.


5단계: 데이터베이스 마이그레이션

schema.prisma 파일에 정의된 데이터 모델을 기반으로 실제 데이터베이스에 스키마를 생성하거나 변경 사항을 적용합니다.

npx prisma migrate dev --name initial_migration
  • migrate dev: 개발 환경에서 스키마 변경 사항을 관리하고 데이터베이스에 적용하는 명령입니다.
  • --name initial_migration: 생성될 마이그레이션 파일의 이름을 지정합니다. (처음이라면 initial_migration이나 init 등으로 명명합니다.)
  • 이 명령을 실행하면 prisma/migrations 디렉토리가 생성되고, 그 안에 현재 스키마 상태를 반영하는 SQL 마이그레이션 파일이 생성됩니다. 또한, 자동으로 Prisma Client를 다시 생성합니다.

스키마 변경 시:
schema.prisma 파일에 모델을 추가하거나, 기존 모델을 수정(필드 추가/삭제/타입 변경 등)할 때마다 이 명령을 다시 실행해야 합니다. Prisma는 변경 사항을 감지하여 새로운 마이그레이션 파일을 생성하고 데이터베이스에 적용합니다.


6단계: Prisma Client 생성 (선택 사항이지만 권장)

npx prisma migrate dev 명령은 마이그레이션 후에 자동으로 npx prisma generate를 실행합니다. 하지만, 스키마를 변경하지 않고 단순히 Prisma Client만 다시 생성하고 싶을 때 이 명령을 수동으로 실행할 수 있습니다.

npx prisma generate

이 명령은 node_modules/.prisma/client 경로에 @prisma/client 패키지를 재생성합니다. 이 패키지에는 정의된 모델에 대한 TypeScript 타입 정의와 쿼리 빌더가 포함됩니다.


7단계: 애플리케이션 코드에서 Prisma Client 사용

이제 TypeScript 코드에서 Prisma Client를 사용하여 데이터베이스와 상호작용할 수 있습니다.

Prisma Client 인스턴스 관리 (Singleton 패턴 권장):

개발 환경에서 핫 리로딩 등으로 인해 Prisma Client 인스턴스가 여러 개 생성되는 것을 방지하기 위해, 싱글톤 패턴으로 Prisma Client 인스턴스를 관리하는 것이 좋습니다.

예를 들어, src/lib/prisma.ts (또는 src/utils/prisma.ts) 파일을 생성합니다:

// src/lib/prisma.ts

import { PrismaClient } from '@prisma/client';

// TypeScript에서 전역 변수 선언을 위한 모듈 증대 (Declaration Merging)
// 이 부분이 TypeScript의 전역 타입 스코프에 `prisma` 변수를 추가하여,
// 다른 파일에서 `global.prisma`를 사용할 때 타입 오류가 발생하지 않도록 합니다.
declare global {
  // `var` 키워드를 사용하여 전역 스코프에 변수를 선언합니다.
  var prisma: PrismaClient | undefined;
}

let prisma: PrismaClient;

// 프로덕션 환경에서는 새로운 PrismaClient 인스턴스를 생성
if (process.env.NODE_ENV === 'production') {
  prisma = new PrismaClient();
} else {
  // 개발 환경에서는 전역 변수를 사용하여 기존 인스턴스가 있으면 재사용
  // 핫 리로딩 시 PrismaClient 인스턴스가 계속 생성되는 것을 방지
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}

export default prisma;

애플리케이션 코드에서 Prisma Client 사용 예시:

// 예를 들어, API 핸들러, 서비스 레이어 등에서 사용
import prisma from './lib/prisma'; // 위에서 정의한 prisma 인스턴스 임포트

async function createUser(email: string, name?: string) {
  try {
    const newUser = await prisma.user.create({
      data: {
        email,
        name,
      },
    });
    console.log('Created user:', newUser);
    return newUser;
  } catch (error) {
    console.error('Error creating user:', error);
    throw error;
  } finally {
    // 연결 풀을 사용하는 경우 일반적으로 close()를 명시적으로 호출할 필요는 없지만,
    // 필요에 따라 연결을 종료할 수 있습니다.
    // await prisma.$disconnect();
  }
}

async function findUsers() {
  try {
    // Prisma Client는 강력한 타입 추론을 제공합니다.
    // users는 자동으로 `User[]` 타입으로 추론됩니다.
    const users = await prisma.user.findMany({
      where: {
        // 필터링 조건
        age: {
          gt: 18 // age가 18보다 큰 사용자
        }
      },
      orderBy: {
        createdAt: 'desc' // 최신 생성 순으로 정렬
      },
      select: {
        id: true,
        email: true,
        name: true,
        posts: { // 관계된 Post 데이터도 함께 조회
          select: {
            title: true,
            published: true
          }
        }
      }
    });
    console.log('Found users:', users);
    return users;
  } catch (error) {
    console.error('Error finding users:', error);
    throw error;
  }
}

// 예시 함수 호출
async function main() {
  await createUser('test@example.com', 'Test User');
  await findUsers();
}

main()
  .catch(e => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    // 애플리케이션 종료 시 데이터베이스 연결 해제
    await prisma.$disconnect();
  });

TypeScript의 이점:

  • 자동 완성 및 타입 검사: prisma.user.create()prisma.user.findMany()와 같은 메서드를 호출할 때, Prisma Client는 schema.prisma에 정의된 모델을 기반으로 정확한 필드 이름, 타입, 관계 등에 대한 자동 완성 및 타입 검사를 제공합니다. 예를 들어, data 객체에 존재하지 않는 필드를 입력하면 컴파일 타임에 오류가 발생합니다.
  • 런타임 오류 감소: 잘못된 쿼리나 존재하지 않는 필드 참조로 인한 런타임 오류를 TypeScript의 타입 시스템으로 미리 방지할 수 있습니다.
  • 코드 가독성 향상: 데이터베이스 스키마와 애플리케이션 코드 간의 명확한 연결을 제공하여 코드의 가독성과 유지보수성을 높입니다.

추가 팁:

  • Prisma Studio: npx prisma studio 명령을 실행하여 데이터베이스의 데이터를 시각적으로 확인하고 편집할 수 있는 GUI 도구를 실행하세요. 개발 및 디버깅에 매우 유용합니다.
  • 환경 변수 보안: .env 파일은 .gitignore에 추가하여 버전 관리에서 제외하는 것이 필수적입니다. 프로덕션 환경에서는 호스팅 서비스의 환경 변수 관리 시스템을 사용해야 합니다.
  • 배포 시 마이그레이션: 프로덕션 환경에 배포할 때는 마이그레이션이 올바르게 실행되도록 설정해야 합니다. 보통 CI/CD 파이프라인에서 npx prisma migrate deploy 명령을 사용하여 적용합니다.
  • 관계형 데이터베이스 이해: Prisma ORM을 효과적으로 사용하려면 관계형 데이터베이스의 기본 개념(테이블, 필드, 관계 등)을 이해하고 있는 것이 좋습니다.

이 단계들을 따르면 TypeScript 프로젝트에서 Prisma ORM을 성공적으로 설정하고 사용할 수 있습니다.

728x90
반응형