Nest.js를 배워보자

🛠️ NestJS Prisma 6에서 자료가 없을 때 Seed 데이터 자동 추가하기

_Blue_Sky_ 2025. 12. 9. 17:05
728x90

NestJS 환경에서 Prisma 6를 사용하여 데이터베이스에 초기 자료(Seed data)가 없을 때 자동으로 데이터를 추가하도록 설정하는 가장 일반적이고 권장되는 방법은 Prisma의 Seeding 기능을 활용하고, 애플리케이션 시작 시 해당 시딩 로직을 실행하는 것입니다.

제공해주신 schema.prisma 설정은 모델 정의에 문제가 없습니다. 이제 다음 단계를 따라 시딩 기능을 구현해보세요.


1. Prisma Seed 파일 설정

package.json 파일에 prisma/seed.ts를 실행하도록 seeding 스크립트를 설정합니다.

// package.json 파일

"scripts": {
  "start:dev": "nest start --watch",
  "prisma:seed": "ts-node prisma/seed.ts", // 이 라인을 추가
  // ... 다른 스크립트들
},
"prisma": {
  "seed": "npm run prisma:seed"
},

2. Seeding 로직 작성 (prisma/seed.ts)

프로젝트 루트에 prisma 폴더를 만들고, 그 안에 seed.ts 파일을 생성하여 초기 데이터를 추가하는 로직을 작성합니다.

이 로직은 데이터베이스에 특정 자료가 없을 때만 데이터를 삽입하도록 작성해야 합니다.

// prisma/seed.ts

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

// PrismaClient 인스턴스를 생성합니다.
const prisma = new PrismaClient();

async function main() {
  console.log(`\n데이터베이스 초기화(Seeding) 시작...`);

  // 1. Todo 테이블에 자료가 있는지 확인합니다.
  const existingTodos = await prisma.todo.count();

  if (existingTodos === 0) {
    console.log(`Todo 테이블이 비어있습니다. 초기 Seed 데이터를 추가합니다.`);

    // 2. 초기 데이터 배열을 정의합니다.
    const todoData = [
      { title: 'Prisma Seeding 설정하기', content: 'NestJS 프로젝트에 Prisma Seed 로직을 구현합니다.', is_done: true },
      { title: '초기 데이터 확인하기', content: '데이터베이스에 접속하여 Todo 자료가 추가되었는지 확인합니다.', is_done: false },
      { title: 'Prisma Client 인스턴스 생성', content: 'PrismaClient를 초기화합니다.', is_done: false },
    ];

    // 3. 데이터를 삽입합니다.
    for (const todo of todoData) {
      await prisma.todo.create({
        data: todo,
      });
    }

    console.log(`✨ 총 ${todoData.length}개의 Todo Seed 데이터가 성공적으로 추가되었습니다.`);
  } else {
    console.log(`✅ Todo 테이블에 이미 ${existingTodos}개의 자료가 존재합니다. Seeding을 건너뜝니다.`);
  }
}

main()
  .catch((e) => {
    console.error(`❌ Seeding 중 오류 발생:`, e);
    process.exit(1);
  })
  .finally(async () => {
    // 4. 작업 완료 후 연결을 닫습니다.
    await prisma.$disconnect();
    console.log(`데이터베이스 초기화 완료.`);
  });

3. NestJS 애플리케이션 시작 시 Seed 실행 (onModuleInit)

NestJS 애플리케이션이 시작될 때마다 Seeding 로직을 실행하도록 PrismaService에 onModuleInit 라이프사이클 훅을 구현합니다.

💡 주의: 이 방법은 개발 환경에서 초기 데이터 설정을 편리하게 하지만, 프로덕션 환경에서는 마이그레이션이 완료된 후 수동으로 npx prisma db seed를 실행하는 것을 권장합니다.

 
// src/prisma/prisma.service.ts (PrismaClient를 관리하는 서비스 파일)

import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    // 1. 데이터베이스에 연결합니다.
    await this.$connect();

    // 2. 연결이 성공하면, 데이터가 없는지 확인하고 Seed 데이터를 추가하는 로직을 실행합니다.
    await this.seedDatabaseIfEmpty();
  }

  // 데이터가 없을 때 Seed를 추가하는 전용 메소드를 만듭니다.
  private async seedDatabaseIfEmpty() {
    try {
      const existingTodos = await this.todo.count();

      if (existingTodos === 0) {
        console.log(`[SEED] Todo 테이블이 비어있습니다. 초기 데이터를 삽입합니다...`);
        
        const todoData = [
          { title: 'Prisma Service를 통한 Seed', content: '애플리케이션 시작 시 자동 실행됩니다.', is_done: false },
          { title: '로깅 확인', content: '콘솔에 [SEED] 로그가 출력되는지 확인합니다.', is_done: false },
        ];
        
        // 트랜잭션으로 데이터를 삽입합니다.
        await this.$transaction(
          todoData.map((todo) => this.todo.create({ data: todo })),
        );

        console.log(`[SEED] 초기 Todo 데이터 ${todoData.length}개가 성공적으로 추가되었습니다.`);
      } else {
        console.log(`[SEED] Todo 테이블에 ${existingTodos}개의 자료가 있어 Seeding을 건너뜝니다.`);
      }
    } catch (error) {
      console.error(`[SEED] 데이터베이스 Seed 중 오류 발생:`, error);
    }
  }

  // (선택 사항) 애플리케이션 종료 시 연결을 끊는 로직도 추가할 수 있습니다.
  // async onModuleDestroy() {
  //   await this.$disconnect();
  // }
}

4. 실행 및 테스트

  1. 마이그레이션 실행 (테이블이 없다면):
    npx prisma migrate dev --name init
    
  2. NestJS 애플리케이션 실행
npm run start:dev

애플리케이션이 시작될 때, 콘솔에 [SEED] 관련 로그가 출력되며 Todo 테이블에 자료가 없다면 자동으로 2개의 초기 데이터가 추가됩니다. 데이터가 이미 있다면 Seeding은 건너뛰어집니다.

어떤 환경에서 이 Seeding 로직을 실행하고 싶으신가요? (개발 환경 vs. 프로덕션 환경)


"ts-node prisma/seed.ts"는 TypeScript로 작성된 Prisma 시딩(Seeding) 파일을 실행하는 명령어입니다.

 

1. ⚙️ ts-node

ts-nodeNode.js 환경에서 TypeScript 파일(.ts)을 직접 실행할 수 있게 해주는 도구입니다.

  • 일반적인 Node.js: Node.js는 JavaScript 파일(.js)만 실행할 수 있습니다. TypeScript 파일을 실행하려면 먼저 tsc (TypeScript Compiler)를 사용해 JavaScript로 컴파일(변환)하는 과정이 필요합니다.
  • ts-node의 역할: ts-node는 이 컴파일 과정을 메모리에서 즉석으로 처리해주기 때문에, 개발자가 별도의 빌드 단계 없이 TypeScript 파일을 바로 실행하고 테스트할 수 있게 해줍니다.

쉽게 말해, "TypeScript 파일을 바로 읽고 실행해주는 변환기" 역할입니다.


2. 🌳 prisma/seed.ts

prisma/seed.ts데이터베이스에 초기 데이터를 채우기 위해 개발자가 작성한 TypeScript 파일입니다.

  • Prisma Seeding: 데이터베이스를 초기 설정하거나 개발 환경을 구축할 때, 기본적으로 필요한 사용자, 설정값, 테스트 데이터 등을 자동으로 채워 넣는 과정을 시딩(Seeding)이라고 합니다.
  • 파일의 위치 및 역할: Prisma는 기본적으로 프로젝트 루트 폴더의 prisma/seed.ts (또는 .js) 파일을 시딩 파일로 인식하도록 설정할 수 있습니다. 이 파일 안에는 PrismaClient를 사용하여 데이터를 조회하고, 없을 경우 create나 createMany 명령어로 데이터를 삽입하는 로직이 들어있습니다.

3. 🧑‍💻 명령어의 전체 의미

ts-node prisma/seed.ts라는 명령어는 결국 다음과 같은 흐름으로 작동합니다.

  1. ts-node를 사용해서
  2. prisma/seed.ts 파일을
  3. 컴파일 과정 없이 즉시 실행해라.

이 명령은 주로 package.json 파일의 스크립트에 등록되어, 데이터베이스 마이그레이션이 끝난 후 (예: npx prisma db seed 명령 실행 시) 자동으로 초기 데이터를 주입하는 용도로 사용됩니다.

구성 요소 역할
ts-node 실행 환경: TypeScript 코드를 즉시 실행 가능한 JavaScript 코드로 변환/실행
prisma/seed.ts 대상 파일: 데이터베이스에 초기 자료(Seed)를 추가하는 로직이 담긴 파일
전체 명령 데이터베이스 초기 데이터를 쉽고 빠르게 주입
 

💡 왜 이 명령을 사용할까?

이 명령을 사용하는 가장 큰 이유는 개발의 편리성 때문입니다.

NestJS와 Prisma를 함께 사용할 때, PrismaClient를 이용해 작성된 시딩 로직을 별도의 컴파일 없이 개발 환경에서 즉시 실행하여 개발 환경 구축 시간을 절약할 수 있습니다.


🔑 package.json 안의 "prisma": { "seed": "..." } 설정 이해하기

package.json 파일에 있는 이 블록은 Prisma CLI에게 시딩(Seeding) 작업을 어떻게 수행해야 하는지 알려주는 설정입니다.

간단히 말해, "사용자가 npx prisma db seed 명령어를 실행하면, 실제로는 이 설정에 지정된 스크립트를 실행해라" 라고 정의하는 것입니다.


1. npx prisma db seed 명령어

이 명령어가 바로 Prisma의 공식 시딩 실행 명령어입니다.

  • 개발자가 데이터베이스 마이그레이션을 완료한 후, 초기 데이터를 주입하고 싶을 때 이 명령어를 터미널에 입력합니다.
  • Prisma CLI는 이 명령을 받으면 package.json 파일을 확인하여 prisma.seed 설정 값을 찾아 실행합니다.

2. "prisma": { "seed": "npm run prisma:seed" }의 역할

이 설정은 다음과 같은 매핑(Mapping) 역할을 합니다.

Prisma 명령어 (사용자 입력) package.json 내부 설정 실제 실행되는 스크립트
npx prisma db seed "prisma": { "seed": "..." } npm run prisma:seed

세부 설명:

  1. "prisma" 블록: Prisma 관련 설정을 모아두는 곳입니다.
  2. "seed" 속성: Prisma CLI의 db seed 명령과 연결되는 속성입니다.
  3. "npm run prisma:seed" 값: db seed 명령이 실행될 때 대신 실행될 스크립트입니다. 이것은 다시 package.json의 scripts 섹션에 정의된 별도의 명령을 참조합니다.

3. 전체 실행 흐름 요약

사용자가 처음 질문에서 설정했던 스크립트들과 합쳐보면 전체 실행 흐름은 이렇게 됩니다.

  1. 사용자가 터미널에 **npx prisma db seed**를 입력합니다.
  2. Prisma CLI가 package.json을 읽고 "prisma": { "seed": ... } 설정을 확인합니다.
  3. Prisma는 이 설정에 따라 npm run prisma:seed 명령을 실행합니다.
  4. package.json의 scripts 섹션에서 "prisma:seed": "ts-node prisma/seed.ts" 정의를 찾습니다.
  5. 최종적으로 **ts-node prisma/seed.ts**가 실행되어, 개발자가 작성한 TypeScript 시딩 로직이 데이터베이스에 초기 데이터를 주입합니다.

💡 핵심: 이 설정은 TypeScript 파일인 prisma/seed.ts를 실행하기 위해 중간 단계로 **ts-node**와 npm run 명령을 연결해주는 "다리" 역할을 합니다.


❓ "처음부터 ts-node prisma/seed.ts면 안 될까요?"

네, 가능합니다, 하지만 상황과 목적에 따라 다릅니다.

ts-node prisma/seed.ts 명령은 시딩 파일을 직접 실행하는 가장 빠른 방법입니다. 하지만 Prisma의 공식 워크플로우를 사용하려면 npx prisma db seed 명령을 통해 간접적으로 실행하는 것이 권장됩니다.


1. 🟢 ts-node prisma/seed.ts를 직접 실행할 경우 (가능)

개발자가 터미널에 이 명령을 입력하면, package.json 설정에 관계없이 즉시 시딩 로직이 실행됩니다.

  • 장점: 가장 빠르고 직관적입니다. 테스트를 위해 시딩 로직만 빠르게 실행해 보고 싶을 때 유용합니다.
  • 단점:
    • Prisma 생태계 이탈: 이 방법은 Prisma CLI의 **공식적인 시딩 명령(npx prisma db seed)**을 사용하지 않습니다. 따라서 Prisma가 제공하는 다른 자동화나 환경 변수 관리 등의 이점을 누리기 어렵습니다.
    • 통일성 부족: 보통 마이그레이션(migrate dev) 후에는 바로 db seed를 실행하여 환경을 구축하는 것이 일반적인 워크플로우인데, 이 명령만 따로 사용하면 워크플로우의 통일성이 떨어집니다.

2. 🟡 npx prisma db seed를 통해 실행할 경우 (권장)

이것이 Prisma가 공식적으로 권장하는 방법이며, 앞서 설명했던 것처럼 package.json의 "prisma": { "seed": "npm run prisma:seed" } 설정이 필요합니다.

  • 장점:
    • 공식 워크플로우: Prisma 생태계에 맞춰 표준화된 명령을 사용합니다.
    • 환경 일관성: CI/CD 파이프라인이나 팀원 간의 환경 설정 시, 모두가 npx prisma db seed만 알면 되기 때문에 명령어의 일관성이 유지됩니다.
    • 내부 동작 보장: Prisma CLI는 db seed를 실행하기 전에 필요한 환경을 설정하거나 유효성 검사를 수행할 수 있습니다.
  • 단점: 직접 실행하는 것보다 한 단계를 더 거치기 때문에 (npx prisma db seed -> npm run prisma:seed -> ts-node prisma/seed.ts) 약간의 설정이 필요합니다.

📝 결론

명령 사용 목적 필요성
ts-node prisma/seed.ts 빠른 테스트 및 디버깅 (시딩 로직만 즉시 실행) package.json 설정 필요 없음
npx prisma db seed 표준화된 환경 구축 (공식 워크플로우, 팀 작업) package.json 설정 필요

팀 프로젝트나 프로덕션 환경에서는 npx prisma db seed를 사용하는 것이 표준화와 유지보수 측면에서 훨씬 좋습니다. 하지만 개인적으로 간단히 시딩 파일을 테스트할 때는 ts-node prisma/seed.ts를 직접 사용해도 전혀 문제가 없습니다.

728x90