지식창고

Node.js 숙련주차 강의

ORM / Prisma

PrismaORM(Object Relational Mapping)으로써,
Javascript 객체(Object)와 데이터베이스의 관계(Relation)을 연결(Mapping) 해주는 도구라고 한다..

한줄 요약 : ORM은 Javascript하구 RDB를 이어주는 친구

만약 ORM 이란 친구들이 나오지 않았더라면?
아마도 SQL에 명령문을 직접적으로 입력해줬을 것이다..

그렇게 사용했던 것이 Raw Query라는데?
(흠.. 한 번쯤은 사용해 볼 수도..?)

그래서 준비한 Raw Query 실전 압축 사용법!

  1. npm을 이용해 MySQL 드라이버를 프로젝트에 설치!

     yarn add mysql2
    
  2. MySQL을 내 DataBase에 연결!

     import mysql from 'mysql2'
    
     const connect = mysql.createConnection({
       host: "",
       user: "",
       password: "",
       database: ""
     });
    

    코드 설명
    1 - mysql2 패키지 에서 사용할 client를 가져온다!
    3~7 - client에 외부 MySQL DB를 연결해준다!
    4 - host : MySQL DB 주소
    5 - user : DB의 계정 명
    6 - password : DB 계정의 비밀번호
    7 - database : 연결해서 사용할 Database의 이름

  3. MySQL 쿼리(명령어) 입력!

     await connect.promise().query(`
       CREATE TABLE 테이블명
       (
         id    INT         NOT NULL AUTO_INCREMENT PRIMARY KEY,
         name  VARCHAR(20) NOT NULL
       )
       SELECT  *
       FROM    테이블명
     `);
    

    코드 설명
    1번은 await과 promise()를 이용해 동기적으로 실행되게 설계!
    2~8번은 MySQL의 query를 사용하는 구간으로 원래 SQL 문법을 사용한다!
    추가로 ``(백틱)을 사용해 템플릿 리터럴(Template literals)을 이용해 플레이스 홀더기능(${변수명})을 사용할 수 있다!

이제 다시 본론으로 돌아오자면.. 이런 애들을 직접 갖다 붙이는 것이 위험할 수도 있고!
데이터가 많아질수록.. Javascript와 MySQL 관계를 확인해가며 유지보수 하는데 자원이 많이 들게 된다!
(추가로 확장, 재활용이 힘들어 보이기도..)

그래서 나온 ORM!!, 이번엔 그 중 Prisma를 사용하는 방법을 알아봅시다!

  1. Prisma 라이브러리 설치

     yarn add prisma @prisma/client
    
  2. prisma.schema 파일 기본 설정!

     datasource db {
         provider = "mysql"
         url      = env("DATABASE_URL")
     }
    

    코드 설명
    2번의 provider는 Prisma가 사용할 데이터베이스 엔진의 유형을 말한다!(=mysql)
    3번의 url은 데이터베이스와 연결하기 위한 주소!(.env 를 이용해 값을 숨겨두었다!)

    url

  3. prisma.schema 파일에 prisma model로 형식 지정하기!

     model UserHistories {
         // uuid 는 '범용 고유 식별자'로 시간 및 기타 정보들을 저장한 데이터 타입
         userHistoryId   String   @id @default(uuid()) @map("userHistoryId")
         // 사용자(Users) 테이블을 참조하는 외래키
         userId          Int      @map("userId") 
         name            String   @map("name") 
         value           String?  @map("value") 
         createdAt       DateTime @default(now()) @map("createdAt")
    
         // Users 테이블과 관계를 설정
         user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
         // fields(지금 테이블의 행) / references(선택한 테이블(Users)의 행) / 삭제 시, 종속성
    
         @@map("UserHistories")
     }
    

    코드 설명
    1번과 14번은 UserHistories 라는 이름을 prisma 내부와, 연결된 MySQL에 똑같은 테이블명을 적용시키기 위해 쓰였다.
    2~8 번은 기존의 SQL와 비슷한 기능을 이용해 테이블 형식을 지정하는 곳이다.
    (@map = 이름지정 / @id = PRIMARY KEY / @default() = DEFAULT /
    String? = 뒤의 ?문자는 값이 없어도 됨(NULL)을 표현 / autoincrement() 및 now() 기능도 존재한다)
    10~12번은 외부 테이블과의 연결을 설명해주는 곳으로 FOREIGN KEY와 비슷한 역할이다

  4. MySQL에 테이블 올리기!

    저장한 형식을 연결된 MySQL에 올리기 위해선 터미널에 명령어를 사용해야 한다!

     # schema.prisma 파일에 설정된 모델을 바탕으로 MySQL에 정보를 업로드
     npx prisma db push
    
  5. prisma 클라이언트 생성 및 메서드 사용

     import { PrismaClient } from '@prisma/client';
     const prisma = new PrismaClient()
    
     await prisma.userHistories.create({
         data: {
             name: "bbie",
             value: "wow"
         }
     })
    
     await prisma.userHistories.findFirst({
         where: { name: "bbie"},
         select: { 
             name: true,
             createdAt: true
             }
     })
    
     await prisma.userHistories.update({
         where: { name: "bbie"},
         data: { 
             name: "pierrot"       
             }
     })
        
     await prisma.userHistories.delete({
         where: { name: "pierrot"}
     })
    

    코드 설명
    1번은 prisma에서 사용할 클라이언트를 뽑아왔다
    2번은 클라이언트를 이용해 인스턴트를 생성하였다
    4~9번은 생성 메서드의 예시로 prisma 인스턴트를 이용해 테이블을 지정한 후 메서드를 사용한다
    11~17번은 조회 메서드
    19~24번은 수정 메서드
    26~28번은 삭제 메서드 이다

알고리즘 코드 카타

명예의 전당

  • 문제
    “명예의 전당”이라는 TV 프로그램에서는 매일 1명의 가수가 노래를 부르고, 시청자들의 문자 투표수로 가수에게 점수를 부여합니다.
    매일 출연한 가수의 점수가 지금까지 출연 가수들의 점수 중 상위 k번째 이내이면 해당 가수의 점수를 명예의 전당이라는 목록에 올려 기념합니다.
    즉 프로그램 시작 이후 초기에 k일까지는 모든 출연 가수의 점수가 명예의 전당에 오르게 됩니다.
    k일 다음부터는 출연 가수의 점수가 기존의 명예의 전당 목록의 k번째 순위의 가수 점수보다 더 높으면, 출연 가수의 점수가 명예의 전당에 오르게 되고 기존의 k번째 순위의 점수는 명예의 전당에서 내려오게 됩니다.

명예의 전당 목록의 점수의 개수 k, 1일부터 마지막 날까지 출연한 가수들의 점수인 score가 주어졌을 때, 매일 발표된 명예의 전당의 최하위 점수를 return하는 solution 함수를 완성해주세요.

  • 조건

    • 3 ≤ k ≤ 100

    • 7 ≤ score의 길이 ≤ 1,000

    • 0 ≤ score[i] ≤ 2,000

function solution(k, score) {
    let answer = [];
    let award = [];
    
    for (let i=0;i<score.length;i++){
        award.push(score[i])
        if (award.length > k) {
            award.sort((a,b) => +b - +a )
            award.pop()
        }
        answer.push(Math.min(...award))
    }
    return answer;
}

SQL 코드 카타

경기도에 위치한 식품창고 목록 출력하기

  • 문제
    FOOD_WAREHOUSE 테이블에서 경기도에 위치한 창고의 ID, 이름, 주소, 냉동시설 여부를 조회하는 SQL문을 작성해주세요.
    이때 냉동시설 여부가 NULL인 경우, ‘N’으로 출력시켜 주시고 결과는 창고 ID를 기준으로 오름차순 정렬해주세요.
SELECT  WAREHOUSE_ID
        , WAREHOUSE_NAME
        , ADDRESS
        , COALESCE(FREEZER_YN, 'N') AS FREEZER_YN
FROM    FOOD_WAREHOUSE
WHERE   ADDRESS LIKE "%경기도%"

강원도에 위치한 생산공장 목록 출력하기

  • 문제
    FOOD_FACTORY 테이블에서 강원도에 위치한 식품공장의 공장 ID, 공장 이름, 주소를 조회하는 SQL문을 작성해주세요.
    이때 결과는 공장 ID를 기준으로 오름차순 정렬해주세요.
SELECT  FACTORY_ID
        , FACTORY_NAME
        , ADDRESS
FROM    FOOD_FACTORY
WHERE   ADDRESS LIKE "%강원도%"

DATETIME에서 DATE로 형 변환

  • 문제
    ANIMAL_INS 테이블에 등록된 모든 레코드에 대해, 각 동물의 아이디와 이름, 들어온 날짜를 조회하는 SQL문을 작성해주세요.
    이때 결과는 아이디 순으로 조회해야 합니다.
SELECT  ANIMAL_ID
        , NAME
        , DATE_FORMAT(DATE(DATETIME), '%Y-%m-%d') 날짜
FROM    ANIMAL_INS
ORDER BY 1

흉부외과 또는 일반외과 의사 목록 출력하기

  • 문제
    DOCTOR 테이블에서 진료과가 흉부외과(CS)이거나 일반외과(GS)인 의사의 이름, 의사ID, 진료과, 고용일자를 조회하는 SQL문을 작성해주세요.
    이때 결과는 고용일자를 기준으로 내림차순 정렬하고, 고용일자가 같다면 이름을 기준으로 오름차순 정렬해주세요.
SELECT  DR_NAME
        , DR_ID
        , MCDP_CD
        , DATE_FORMAT(DATE(HIRE_YMD), "%Y-%m-%d") HIRE_YMD
FROM    DOCTOR
WHERE   MCDP_CD IN ("CS","GS")
ORDER BY 4 DESC, 1

가격이 제일 비싼 식품의 정보 출력하기

  • 문제
    FOOD_PRODUCT 테이블에서 가격이 제일 비싼 식품의 식품 ID, 식품 이름, 식품 코드, 식품분류, 식품 가격을 조회하는 SQL문을 작성해주세요.
SELECT  *
FROM    FOOD_PRODUCT
WHERE   PRICE = 
(
SELECT  MAX(PRICE)
FROM    FOOD_PRODUCT
)

이름이 없는 동물의 아이디

  • 문제
    동물 보호소에 들어온 동물 중, 이름이 없는 채로 들어온 동물의 ID를 조회하는 SQL 문을 작성해주세요.
    단, ID는 오름차순 정렬되어야 합니다.
SELECT  ANIMAL_ID
FROM    ANIMAL_INS
WHERE   NAME IS NULL
ORDER BY 1

조건에 맞는 회원수 구하기

  • 문제
    USER_INFO 테이블에서 2021년에 가입한 회원 중 나이가 20세 이상 29세 이하인 회원이 몇 명인지 출력하는 SQL문을 작성해주세요.
SELECT  COUNT(*) USERS
FROM    USER_INFO
WHERE   DATE_FORMAT(DATE(JOINED),"%Y") = "2021" AND AGE BETWEEN "20" AND "29"

중성화 여부 파악하기

  • 문제
    보호소의 동물이 중성화되었는지 아닌지 파악하려 합니다. 중성화된 동물은 SEX_UPON_INTAKE 컬럼에 ‘Neutered’ 또는 ‘Spayed’라는 단어가 들어있습니다.
    동물의 아이디와 이름, 중성화 여부를 아이디 순으로 조회하는 SQL문을 작성해주세요.
    이때 중성화가 되어있다면 ‘O’, 아니라면 ‘X’라고 표시해주세요.
SELECT  ANIMAL_ID
        , NAME
        , IF(SEX_UPON_INTAKE LIKE "Intact%", "X","O") 중성화
FROM    ANIMAL_INS

카테고리 별 상품 개수 구하기

  • 문제
    PRODUCT 테이블에서 상품 카테고리 코드(PRODUCT_CODE 앞 2자리) 별 상품 개수를 출력하는 SQL문을 작성해주세요.
    결과는 상품 카테고리 코드를 기준으로 오름차순 정렬해주세요.
SELECT  SUBSTRING(PRODUCT_CODE, 1, 2) CATEGORY
        , COUNT(*) PRODUCTS
FROM    PRODUCT
GROUP BY 1

고양이와 개는 몇 마리 있을까

  • 문제
    동물 보호소에 들어온 동물 중 고양이와 개가 각각 몇 마리인지 조회하는 SQL문을 작성해주세요.
    이때 고양이를 개보다 먼저 조회해주세요.
SELECT  ANIMAL_TYPE
        , COUNT(*) COUNT
FROM    ANIMAL_INS
GROUP BY 1
ORDER BY 1

입양 시각 구하기(1)

  • 문제
    보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다. 09:00부터 19:59까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요.
    이때 결과는 시간대 순으로 정렬해야 합니다.
SELECT  DATE_FORMAT(DATETIME,"%H") HOUR
        , COUNT(*) COUNT
FROM    ANIMAL_OUTS
WHERE   DATE_FORMAT(DATETIME,"%H") BETWEEN 9 AND 20
GROUP BY 1
ORDER BY 1

Reference

programmers
스파르타코딩클럽