๐Ÿ”ฅTIL #4. prisma ์„ค์น˜ ๋ฐ node.js์™€ ์—ฐ๋™

๋ฐฑ์Šน์ง„ยท2020๋…„ 12์›” 24์ผ
7

NodeJS

๋ชฉ๋ก ๋ณด๊ธฐ
4/7

๐ŸŒŸ Today I learned

  1. prisma ๋ž€?
  2. prisma ์„ค์น˜ ๋ฐ ์„ค์ •๋ฐฉ๋ฒ•
    1. project ์ดˆ๊ธฐ ์„ธํŒ…
    2. prisma folder ์ƒ์„ฑ
    3. ์—ฐ๊ฒฐํ•  database ์ •๋ณด ์„ค์ •
    4. ์—ฐ๊ฒฐํ•˜๊ธฐ
    5. prisma client ์„ค์น˜ ๋ฐ ์ƒ์„ฑ
  3. prisma๋กœ data read, create ํ•ด๋ณด๊ธฐ

1. prisma

prisma๋ž€ node.js ์™€ Typescript๋ฅผ ์œ„ํ•œ ํ˜„์‹œ์  ์ตœ์‹  ํŠธ๋ Œ๋“œ์˜ ORM(Object Relational Mapping) open source ๊ธฐ์ˆ ์ด๋‹ค. (๊ธฐ์กด ORM๊ธฐ์ˆ ๋กœ๋Š” Sequelize, typeORM ์ด ์žˆ๋‹ค.)
prisma๋Š” Heroku ์ฐฝ์—…์ž์™€ Graphql ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ฉ์ž‘ํ•˜์—ฌ ๋งŒ๋“  ๊ธฐ์ˆ ์ด๊ธฐ์— ํŠนํžˆ Graphql๊ณผ ์ž˜ ๋งž๋Š”๋‹ค. Graphql ๋„์ž…์„ ๊ณ ๋ คํ•œ๋‹ค๋ฉด prisma๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.


2. prisma ์„ค์น˜ ๋ฐ ์„ค์ •๋ฐฉ๋ฒ•

  1. project ์ดˆ๊ธฐ ์„ธํŒ…

    ๋จผ์ € ํ•ด์•ผ ํ•˜๋Š”๊ฒƒ์€ Node.js project ์ดˆ๊ธฐ์„ค์ •์ด๋‹ค.

    npm init -y # package.json ์ƒ์„ฑ
    npm i @prisma/cli --save-dev # prisma command line interface ์„ค์น˜

  2. prisma folder ์ƒ์„ฑ

    prisma ๋ฅผ node.js์™€ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด ์šฐ์„  prisma schema ํŒŒ์ผ๊ณผ env ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.
    ์•„๋ž˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•ด๋ณด์ž.

    npx prisma init

    (npx๋Š” npm v5.2.2 ์ด์ƒ๋ถ€ํ„ฐ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ์กฐ๊ธˆ๋” ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ฃผ ๋ชฉ์ ์ด๋‹ค. npx๋Š” ์ฐจํ›„ ์ƒ์„ธํžˆ ์•Œ์•„๋ณด์ž.)

    ์œ„ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ์‹คํ–‰ํ•œ ๊ฒฝ๋กœ์ƒ์— 'prisma' ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๊ทธ ์•ˆ์—๋Š” 'schema.prisma'์ด ์ƒ์„ฑ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  'prisma' ํด๋”์™€ ๋™์ผ ๊ฒฝ๋กœ์— .env ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ๋‹ค.

    schema.prisma : migration๋œ Database table schema๊ฐ€ ์ •์˜๋˜๋Š” ํŒŒ์ผ(prisma ํ•ต์‹ฌ)
    .env : Database ์ ‘์†์„ ์œ„ํ•œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ์ˆจ๊น€ ํŒŒ์ผ.

  3. ์—ฐ๊ฒฐํ•  database ์ •๋ณด ์„ค์ •

    prisma/schema.prisma ํŒŒ์ผ์„ ์—ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜จ๋‹ค.

    //This is your Prisma schema file,
    //learn more about it in the docs: https://pris.ly/d/prisma-schema
    
    datasource db {
      provider = "postgresql"  // ์—ฌ๊ธฐ๋ฅผ mysql๋กœ ๋ณ€๊ฒฝ
      url      = env("DATABASE_URL")
    }
    
    generator client {
      provider = "prisma-client-js"
    }```

    db์˜ provider๊ฐ€ default๋กœ 'postgresql'๋กœ ๋˜์–ด์žˆ๋‹ค. ์ด๋ฅผ 'mysql'๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.
    ๊ทธ ๋‹ค์Œ .env ํŒŒ์ผ์„ ์—ด์–ด๋ณด์ž

    // prisma/schema.prisma
    
    // Environment variables declared...
    // See the documentation for more detail: ...
    
    // Prisma supports the native connection...
    // See the documentation for all the connection string options:...
    
    DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"```

    .env์˜ 'DATABASE_URL'์— [์—ฐ๊ฒฐํ•  sql ๋ชจ๋ธ๊ณผ ๊ณ„์ •, ํŒจ์Šค์›Œ๋“œ, ์‚ฌ์šฉํ•  database] ์ •๋ณด๋ฅผ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค. node project๋ฅผ ์œ„ํ•ด ์ƒ์„ฑํ•œ database ๊ธฐ์ค€์œผ๋กœ ์ˆ˜์ •ํ•œ๋‹ค.

    // .env
    
    DATABASE_URL="mysql://node_blogs_user:{ํŒจ์Šค์›Œ๋“œ}@localhost:3306/node_blogs?schema=public"```
  4. ์—ฐ๊ฒฐํ•˜๊ธฐ

    prisma๋Š” ์ตœ์‹  ๊ธฐ์ˆ ์ด๋‹ค๋ณด๋‹ˆ ์•„์ง schema๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‹ค์ œ Table์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ทจ์•ฝํ•œ ๋“ฏ ํ•˜๋‹ค. ํ•ด์„œ prisma ์—ฐ๋™์„ ์œ„ํ•ด์„  dbdiagram.io ๊ฐ™์€ tool์„ ์ด์šฉํ•ด ๋ชจ๋ธ๋งํ•œ ๋ฐ์ดํ„ฐ๋ฅผ sqlํŒŒ์ผ๋กœ exportํ•˜์—ฌ database์— import ํ•˜๋Š” ๊ณผ์ •์ด ๋จผ์ € ํ•„์š”ํ•˜๋‹ค.
    ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋‹ค๋ฉด ์•„๋ž˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ž.

    npx prisma introspect
    
    // ์ด ๋ช…๋ น์–ด๋Š” .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์ž‘์„ฑํ•œ DATABASE_URL ์— ์ ‘์†ํ•ด์„œ 
    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ” ์Šคํ‚ค๋งˆ๋ฅผ ๊ฒ€์‚ฌํ•œ ํ›„, schema.prisma ํŒŒ์ผ์— ๋ชจ๋ธ์„ ์ž‘์„ฑํ•œ๋‹ค.```

    ์‹คํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด prisma/schema.prisma ์—๋Š” ์•„๋ž˜์ฝ”๋“œ์ฒ˜๋Ÿผ ํƒ€์ผ“ database์˜ ์ •๋ณด๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค.

    // prisma/schema.prisma
    
    generator client {
      provider = "prisma-client-js"
    }
    
    datasource db {
      provider = "mysql"
      url      = env("DATABASE_URL")
    }
    
    model articles {
      id         Int             @id @default(autoincrement())
      user_id    Int
      title      String
      body       String
      status     articles_status @default(DRAFT)
      created_at DateTime?       @default(now())
      updated_at DateTime?
      deleted_at DateTime?
      users      users           @relation(fields: [user_id], references: [id])
      comments   comments[]
    
      @@index([user_id], name: "user_id")
    }
    
    model comments {
      id         Int       @id @default(autoincrement())
      article_id Int
      user_id    Int
      body       String
      created_at DateTime? @default(now())
      updated_at DateTime?
      deleted_at DateTime?
      articles   articles  @relation(fields: [article_id], references: [id])
      users      users     @relation(fields: [user_id], references: [id])
    
      @@index([article_id], name: "article_id")
      @@index([user_id], name: "user_id")
    }
    
    model users {
      id         Int          @id @default(autoincrement())
      email      String       @unique
      password   String
      status     users_status @default(ACTIVE)
      created_at DateTime?    @default(now())
      updated_at DateTime?
      deleted_at DateTime?
      articles   articles[]
      comments   comments[]
    }
    
    enum articles_status {
      DRAFT
      PUBLISHED
      DELETED
    }
    
    enum users_status {
      ACTIVE
      INACTIVE
    }```
  5. prisma client ์„ค์น˜ ๋ฐ ์ƒ์„ฑ

    // ๋จผ์ € client ๋…ธ๋“œ ๋ชจ๋“ˆ ํŒจํ‚ค์ง€ ์„ค์น˜
    npm install @prisma/client
    
    npx prisma generate
    
    // ์ด ๋ช…๋ น์–ด๋Š” npx prisma introspect ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด์„œ ์ƒ์„ฑ๋œ schema.prisma ํŒŒ์ผ์„ ์ฝ์–ด์„œ
    // node_modules/@prisma/client ํด๋” ์•ˆ์— ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•  prisma client ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑ ํ•ด ์ค€๋‹ค.```

3. prisma๋กœ data read, create ํ•ด๋ณด๊ธฐ

prisma๋กœ mysql์˜ node_blogs database์™€ mapping์ด ๋˜์—ˆ๋‹ค. ์ด์ œ node ์ƒ์—์„œ ์ด๋ฅผ ์ด์šฉํ•ด table์„ ์ œ์–ดํ•ด๋ณด์ž.

const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const {PrismaClient} = require('@prisma/client')
const prisma = new PrismaClient()

// SignUp (ํšŒ์›๊ฐ€์ž…)
// 1. ์š”์ฒญ์ž๊ฐ€ email, password ๋“ฑ์˜ ํšŒ์› ์ •๋ณด๋ฅผ ๋ณด๋‚ด๋ฉด ์ด๋ฅผ ๋ฐ›์•„ User table์— ์ถ”๊ฐ€
app.post('/users/signup', (req, res) => {
	const {email, password} = req.body
    	const hashedPassword = bcrypt.hash(password, 10)
    	const createdUser = await prisma.users.create({
      	data : {
	        email,
	        password: hashedPassword,
	      }
	    })

        res.status(201).json({createdUser})
})

// LogIn 
// 1. ์š”์ฒญ์ž์˜ email, password ์ฒดํฌ
app.post('/users/login', async (req, res) => {
  try {
    const { email, password: inputPassword } = req.body
    const foundUser = await prisma.users.findOne({ where: { email } })

    if (!foundUser) {
      const error = new Error('invalid input')
      error.statusCode = 400
      throw error
    }

    const { id, password: hashedPassword } = foundUser
    const isValidPassword = await bcrypt.compare(inputPassword, hashedPassword)

    if (!isValidPassword) {
      const error = new Error('invalid input')
      error.statusCode = 400
      throw error
    }

    const token = jwt.sign({ id }, 'node_blogs_secret_key', { expiresIn: '1h' })
    res.status(200).json({ message: 'login success', token })
  } catch (err) {
    res.status(err.statusCode).json({ message: err.message })
  }
})```

์œ„ ์ฒ˜๋Ÿผ prisma ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด model์„ ์ ‘๊ทผ ๋ฐ ์ œ์–ด ํ•  ์ˆ˜ ์žˆ๋‹ค.

profile
12๋…„ .NET ๊ฐœ๋ฐœ ๊ฒฝ๋ ฅ์„ ๊ฐ€์ง„ ์›น ์ดˆ์งœ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค :)

1๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2022๋…„ 5์›” 8์ผ

์ž์„ธํ•œ ํ”„๋ฆฌ์ฆˆ๋งˆ ์„ค๋ช… ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค
์ž˜๋ดค์Šต๋‹ˆ๋‹ค!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ