[Node.js] query를 편하게 해주는 sequelize에 대해서 알아보자

들어가며

slack bot을 만들면서 db query를 사용했어야 했는데, 정말 간단하게 사용가능하며 Java의 JPA처럼 쓸 수 있는 라이브러리인 sequelize에 대해서 간단하게 알아보자.

Getting Started

Node.js를 생성하고 sequelize 라이브러리를 install 해야 한다.

npm install --save sequelize

그 후에 사용할 데이터 베이스 관련해서 install해야 하며 in-memory 환경에서 간단하게 사용해 볼 예정이라 sqlite3를 install을 한다.

$ npm install --save sqlite3

데이터 베이스 설정

sequelize에서는 다양한 데이터베이스 환경을 지원해주고 있어 필요한 라이브러리를 install해서 해당 설정에 맞게 셋팅을 해주면 된다.

가이드에서는 3가지 방법을 제시해주고 있는데 그 중 편한 방식으로 셋팅하면 된다.

const sequelize = new Sequelize('sqlite::memory:'); // Example for sqlite

(async () => {
  try {
    await sequelize.authenticate();
    console.log('Connection has been established successfully.');
  } catch (error) {
    console.error('Unable to connect to the database:', error);
  }
})();

해당 방식으로 memory 방식으로 서버가 띄울때 데이터베이스에 연결이 되며 authenticate를 통해서 간단한 테스틑 query를 통해서 연결 유무를 확인할 수 있다.

SELECT 1+1 AS result

테이블 생성

Slack bot을 만들며 사용했던 Task table을 어떻게 하면 만들 수 있는지 알아보자.

테이블도 정의할 수 있는 방법이 2가지가 있는데 extending model과 define 방식이 있다.

그 중 extending 방식을 사용해서 해당 테이블용 메소드를 만들어서 사용하는게 더 편해서 해당 방식으로 선택하였다.

const {Model, DataTypes, Op} = require('sequelize');

class Task extends Model {
  // method 작성
}

Task.init({
  id: {
    type: DataTypes.STRING(255),
    allowNull: false,
    primaryKey: true
  },
  username: {
    type: DataTypes.STRING(255),
    allowNull: false
  },
  date: {
    type: DataTypes.STRING(255),
    allowNull: false
  },
  task: {
    type: DataTypes.STRING(3000)
  }
}, {/*option*/sequelize, tableName: 'task', timestamps: false});

Model을 상속 받아서 작성해야 하며, 해당 Class를 init을 통해서 설정해주어야 한다. nullable, pk 등 다양한 설정을 할 수 있다.

주의해야 할 점은 tableName이 복수형으로 자동 생성 되는 기능이 있는데 Task로 클래스를 해두었다면 Tasks로 테이블이 생성되니 사용할 때 주의해야 한다.

복수형으로 생성되지 않게 하려면 마지막 Option 부분에 tableName을 재정의해주면 해당 값으로 테이블이 생성 된다.

그리고 sequelize는 테이블을 만들게 되면 createdAt, updatedAt 필드가 자동으로 추가되는데 해당 필드가 생성되지 않게 하려면 option 부분에 timestamps: false을 추가해야 한다.

어느정도 테이블 구조를 잡았으니 실제 데이터베이스에도 동기화를 해주기 위해 sync method를 사용해서 실제로 테이블을 생성해주어야 한다.

sequelize.sync(); // 전체 model 동기화
Task.sync(); // 해당 model만 동기화

JPA 처럼 create, drop-create등 옵션이 존재하니 잘 사용하면 좋을 것 같다.

sequelize.sync({force: true}); // drop-create 방식
sequelize.sync({alter: true}); // 변경된 필드 반영
sequelize.sync(); // 해당 모델의 테이블이 존재하지 않으면 create
Executing (default): CREATE TABLE IF NOT EXISTS `task` (`id` VARCHAR(255) NOT NULL PRIMARY KEY, `username` VARCHAR(255) NOT NULL, `date` VARCHAR(255) NOT NULL, `task` VARCHAR(3000));

default는 table이 존재하지 않으면 create 해주는 옵션을 사용한다.

Insert

Insert는 create를 이용해서 할 수 있으며, json 방식으로 해당 값을 채워주기면 하면 알아서 query가 생성되서 전송된다.

(async () => {
  let task = await Task.create({id:'0', username: 'woojin', date:'2020-03-25', task:'sequelize study'});
  console.log('task', task);
})();

쿼리 실행

Executing (default): INSERT INTO `task` (`id`,`username`,`date`,`task`) VALUES ($1,$2,$3,$4);

Select

Select에 대해서도 findAll, findByPk 등 만들어져 있는 메소드들이 존재해 필요한 상황에 따라서 사용하면 된다.

가장 좋았던 거는 where절에 대해서 QueryDsl처럼 쉽게 셋팅할 수 있었고 쉽게 어떤 조건인지 확인을 할 수 있었다.

(async () => {
  let tasks = await Task.findAll({
    where:  {
      username: 'woojin'
    }
  });
  console.log('tasks', tasks);
})();
Executing (default): SELECT `id`, `username`, `date`, `task` FROM `task` AS `Task` WHERE `Task`.`username` = 'woojin';

해당 select문은 username이 ‘woojin’인 Task를 전체 찾는 쿼리인데 정말 쉽게 원하 조건을 사용할 수 있다.

좀 더 복잡한 쿼리를 만들기 위해서는 해당 문서를 참고하자.

마치며

간단하게 토이 프로젝트에 적용해본거라 1:N과 같은 양방향 관계에 대해서는 사용해보질 않았다.

그렇지만 사용하는데 있어서 간단하게 적용해볼 수 있고 다양한 데이터베이스를 지원하고 있어서 데이터베이스에 맞는 쿼리 작성을 하지 않아도 알아서 해당 데이터베이스에 맞게 적용되기에 편하다고 생각한다.

반복적인 쿼리를 만들기 보다는 ORM을 통해서 생산성을 높이는데 집중할 수 있었던 것 같다.


sequelize example은 해당 github에서 확인 가능합니다.

Leave a comment