카테고리 없음

Nest.js + Prisma(Postgres) + Docker

이런저런 IT 이야기 2023. 7. 16. 20:21
반응형

https://github.com/leeyonghe/NestPrismaProject

샘플코드입니다. 미리 살펴보세요.

1. Docker 구성

  • Node Server 1대
    • node:latest 서버 이미지를 사용
    • 포트번호 3000번으로 적용
    • 서버 실행시 반드시 prisma migrate 및 generate 명령어 사용
      • npx prisma db push && yarn install && nest start --watch
  • Postgres DB 1대
    • DB 사용자 및 패스워드 적용
      • POSTGRES_USER: postgres
      • POSTGRES_PASSWORD: postgres123!
  • docker-compose.yml 파일 내용
version: '3.7'
services:
  dp_project:
    tty: true
    container_name: dp_project
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
      - "5555:5555"
    volumes:
      - ./project/:/main
    command: sh -c "npx prisma db push && yarn install && nest start --watch"
    depends_on:
      - "postgres"
  postgres:
    container_name: postgres
    tty: true
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres123!
      PGDATA: /data/postgres
    ports:
      - "5432:5432"
    restart: unless-stopped
  • Dockerfile 내용 (Django 서버용)
FROM node:latest

RUN apt update

RUN npm i -g @nestjs/cli

WORKDIR /main

       - npx 명령어를 사용하기 위해서 nodejs & npm & npx 단계적으로 설치
       - npx 명령어는 docker-compose command에서 사용


2. Nest.js 프로젝트 생성

  • 사전에 Prisma 설치 필요
  • @nestjs/cli 설치
$ npm i -g @nestjs/cli
  • Nest.js 프로젝트 생성
$ nest new project
  • Prisma초기화
    • 아래 명령어 실행후 prisma 폴더 및 안에 schema.prisma파일이 생성되었는지 확인
$ prisma init
  • DB를 조회할 서비스 class 생성[user.service.ts]
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { Prisma, user } from '@prisma/client';

@Injectable()
export class UserService {

  constructor(private prisma: PrismaService) {}

  async user(
    userWhereUniqueInput: Prisma.userWhereUniqueInput,
  ): Promise<user | null> {
    return this.prisma.user.findUnique({
      where: userWhereUniqueInput,
    });
  }

  async users(): Promise<user[] | null> {    
    return this.prisma.user.findMany();
  }

  async createUser(data: Prisma.userCreateInput): Promise<user> {
    return this.prisma.user.create({
      data,
    });
  }

}
  • Prisma를 관리할 class 생성[prisma.service.ts]
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
  async onModuleDestroy() {
    await this.$disconnect();
  }
}
  • AppModule에 UserService, PrismaService 추가[app.module.ts]
import { Global, Module } from "@nestjs/common"
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaService } from './prisma.service';
import { UserService } from "./user.service";

@Module({
  imports: [],
  controllers: [AppController],
  providers: [PrismaService, UserService, AppService], // 3개가 모두 등록되어야 함
})
export class AppModule {}
  • ejs 파일 위치 설정 및 리소스 파일(css, js, 이미지) 위치 설정 [main.ts]
    • ejs 템플릿 파일은 Embedded JavaScript의 약자로, 자바스크립트가 내장되어 있는 html 파일이다
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.enableShutdownHooks()
  app.useStaticAssets(join(__dirname, '..', 'src', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'src', 'views'));
  app.setViewEngine("ejs");
  await app.listen(3000);
}
bootstrap();
  • ejs 파일을 저장할 폴더 생성
    • src > views > view.ejs
  • 리소스 파일들을 저장할 폴더 생성
    • src > public > jquery-3.7.0.min.js
  • Controller 설정
    • 호출하는 함수위에 rendering할 파일을 표시 => @Render('view.ejs') 
import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService } from './user.service';

@Controller()
export class AppController {
  
  constructor(private readonly appService: AppService, private readonly userService: UserService) {}

  @Get()
  @Render('view.ejs')
  async getHello(): Promise<object> {    
    try {
      
      // [STEP1]
      // this.userService.createUser({email : 'test@gmail.com', name: 'test' })

      // [STEP2]
      const user = await this.userService.user({email : 'test@gmail.com'})
      console.log(JSON.stringify(user))
      return this.appService.getHello(user);

    } catch (error) {      
      console.log(error)
      return { name: '', 'email': ''};
    }
    
  }

}


3. Prisma(Posgtgres)

  • schema.prisma파일 수정
  • postgres db 연결정보 및 user 추가
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = "postgresql://postgres:postgres123!@postgres:5432/postgres?sslmode=disable"
}

model user {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}


4. 폴더구조


5. 프로젝트 실행

import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService } from './user.service';

@Controller()
export class AppController {
  
  constructor(private readonly appService: AppService, private readonly userService: UserService) {}

  @Get()
  @Render('view.ejs')
  async getHello(): Promise<object> {    
    try {
      
      // [STEP1]
      this.userService.createUser({email : 'test@gmail.com', name: 'test' })

      // [STEP2]
      // const user = await this.userService.user({email : 'test@gmail.com'})
      // console.log(JSON.stringify(user))
      return this.appService.getHello(user);

    } catch (error) {      
      console.log(error)
      return { name: '', 'email': ''};
    }
    
  }

}
  • 최초 실행시 데이터가 없으므로 STEP1 부분을 주석을 풀고 STEP2를 주석처리후 실행
import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';
import { UserService } from './user.service';

@Controller()
export class AppController {
  
  constructor(private readonly appService: AppService, private readonly userService: UserService) {}

  @Get()
  @Render('view.ejs')
  async getHello(): Promise<object> {    
    try {
      
      // [STEP1]
      // this.userService.createUser({email : 'test@gmail.com', name: 'test' })

      // [STEP2]
      const user = await this.userService.user({email : 'test@gmail.com'})
      console.log(JSON.stringify(user))
      
      return this.appService.getHello(user);

    } catch (error) {      
      console.log(error)
      return { name: '', 'email': ''};
    }
    
  }

}
  • 두번째 실행시 데이터가 있으므로 STEP2 부분을 주석을 풀고 STEP1를 주석처리후 실행

  • 위와 같이 실행 결과를 볼수 있음

 

반응형