TypeScript와 함께 Node.js에서 MySQL 사용

이 게시물은 원래 내blog에 게시되었습니다.


오랫동안 저는 다이아몬드<> 연산자를 사용하여 SQL 쿼리에서 입력된 결과를 얻을 수 있다는 사실을 알지 못한 채 TypeScript와 함께 MySQL을 사용해 왔습니다. 그것은 오늘부터 변경되었으며 귀하와 공유하고 싶습니다. 이를 위해 Node.js에서 SQL 쿼리를 실행하기 위해 mysql2 npm 패키지를 사용하고 있습니다.

먼저 간단한 MySQL 테이블을 생성해 보겠습니다.

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(50) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  admin BOOLEAN NOT NULL DEFAULT 0,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);


그렇게 쉽습니다.

다음으로 이전 단계에서 방금 생성한 테이블을 기반으로 쿼리 결과에 대한 유형을 정의해야 합니다. 따라서 interface을 정의하고 테이블 열에 따라 속성을 설정합니다.

import { RowDataPacket } from "mysql2"

export interface IUser extends RowDataPacket {
  id?: number
  email: string
  password: string
  admin: boolean
  created_at: Date
}


mysql npm 패키지에서 RowDataPacket 유형을 확장하는 것을 잊지 마십시오. 그렇지 않으면 다음 단계에서 오류가 발생합니다.

이제 SQL 쿼리에 이 인터페이스를 사용할 수 있습니다. 기본 CRUD 작업을 포함하는 래퍼 클래스를 만들어 보겠습니다.

import { OkPacket } from "mysql2"

import { connection } from "./db"

export class UserRepository {
  readAll(): Promise<IUser[]> {
    return new Promise((resolve, reject) => {
      connection.query<IUser[]>("SELECT * FROM users", (err, res) => {
        if (err) reject(err)
        else resolve(res)
      })
    })
  }

  readById(user_id: number): Promise<IUser | undefined> {
    return new Promise((resolve, reject) => {
      connection.query<IUser[]>(
        "SELECT * FROM users WHERE id = ?",
        [user_id],
        (err, res) => {
          if (err) reject(err)
          else resolve(res?.[0])
        }
      )
    })
  }

  create(user: IUser): Promise<IUser> {
    return new Promise((resolve, reject) => {
      connection.query<OkPacket>(
        "INSERT INTO users (email, password, admin) VALUES(?,?,?)",
        [user.email, user.password, user.admin],
        (err, res) => {
          if (err) reject(err)
          else
            this.readById(res.insertId)
              .then(user => resolve(user!))
              .catch(reject)
        }
      )
    })
  }

  update(user: IUser): Promise<IUser | undefined> {
    return new Promise((resolve, reject) => {
      connection.query<OkPacket>(
        "UPDATE users SET email = ?, password = ?, admin = ? WHERE id = ?",
        [user.email, user.password, user.admin, user.id],
        (err, res) => {
          if (err) reject(err)
          else
            this.readById(user.id!)
              .then(resolve)
              .catch(reject)
        }
      )
    })
  }

  remove(user_id: number): Promise<number> {
    return new Promise((resolve, reject) => {
      connection.query<OkPacket>(
        "DELETE FROM users WHERE id = ?",
        [user_id],
        (err, res) => {
          if (err) reject(err)
          else resolve(res.affectedRows)
        }
      )
    })
  }
}


실행할 때connection.query() 다이아몬드 연산자<>를 사용하여 쿼리 결과의 유형인 인터페이스를 지정합니다. new Promise((resolve, reject) => {/* ... */}) 구문을 사용하여 결과를 Promise이 아닌 callback으로 반환합니다.
readById() 메서드는 optional chaining (?.) 연산자를 사용하여 IUser의 단일 인스턴스를 반환합니다. 메서드 create()update()res.insertId/user.id 값을 읽어 방금 생성/업데이트된 사용자를 반환합니다. 메서드remove()는 영향을 받는 행의 수를 반환합니다. 모든 것이 제대로 작동하면 이 수는 1여야 합니다.

이 기술을 사용하는 한 가지 이점은 VSC와 같은 IDE 및 편집기에서 작업할 때 이제 IntelliSense를 통해 쿼리 결과의 테이블 열을 제안한다는 것입니다. 반면에 데이터베이스 테이블에 따라 TypeScript 인터페이스를 정의해야 하기 때문에 약간의 오버헤드가 있습니다. 더 복잡한 쿼리를 사용하면 훨씬 더 복잡해질 수 있습니다.

대안은 예를 들어 ORM과 같은 TypeORM을 사용하는 것입니다.

좋은 웹페이지 즐겨찾기